论Grails----转载
最近用Grails0.3.1/0.4写了一个小应用耍耍,主要想感受一下grails的快速开发能力,顺便尝尝Grooy的语法糖。
刚开始的几天,写了一个简单的模块,只有几个domainclass和几个controller,也没有写testcase,Grails还在0.3.1,使用下来觉得Grails非常爽,尤其是Groovy的语法糖,那些closure,each操作,简直是爽坏了。
过了几天,业务开始复杂起来,需要整合acegi了,于是碰到了第一个问题,pojoservice和POGOservice/domain互操作的问题。
再过了几天,grails升级到0.4了,业务也更加复杂了,添加了几个自己的POGOservice.用户,角色,权限,自定义的acegevoter...多对多关系终于出现了,单元测试不得不写了。。。于是感觉到用Grails开发的问题越来越严重了。。。
下面是我碰到的问题列表,由于我是初次使用GRails,如果有谁发现其中的错误请不吝指出,谢谢!
(1)POJOservice/Grailsservice互相操作不方便.假如我在某个javaservice中需要注入一个Groovy写的service,那么,这个ServiceType究竟该是什么?按照[http://www.grails.org/Services官方的doc],需要我们定义一个javainterface,然后让POGO的Service来implements该接口,然后就可以通过request.getSession().getServletContext().getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT).getBean(...)来获得POGOService.
看起来挺不错的,仔细一想,该操作根本必须让POJOService自行resolve需要的POGOServicedependency,这不是忽悠人么.还算啥IoC啊.还有,在POJO里面通常不大可能有request对象哈?实现ContextAware?不管用!嘿嘿一般人我可不告诉他啊,您必须得让POJOService继承WebApplicationObjectSupport,才能获得servletContext...然后获得该bean.估计是POGOService的Scope有问题,或者可能和POJOBean不在同一个level.于是一番折腾下来,我们的POJO变得面目全非,人不人鬼不鬼咯.
有一个现成的例子是Grails与Acegi的整合,我们必须提供一个GrailsUserDetailService实现,这是一个PojoService.Acegi需要调用该Service来获得ACEGI中UserDetails的信息,同时该PojoService需要通过操作grailsdomainobject,比如User.findByUsername()来lookupdomainclasses,于是里面就有很多有趣的代码了,比如invokeMethod('foo','args)....
(2)Grailsrun-app/test-app启动非常慢,在P1.9M的laptop上面从输入命令到可以运行,至少30s.这是很严重的问题,我们严重依赖TestCase来保证代码的行为,为了调试代码中的问题,必须不停的跑测试,调试,再跑测试.JUnit跑一次通常<5s,而这里每次runtestcase都需要30s,这是无法忍受的.这篇doc就是在grailstest-app的等待间歇中写出来的...
(3)DomainClassValidation的不足.据一个简单的场景创建用户.输入数据验证除了简单的字串长度检测外,还有一个额外需求,界面需要用户2次输入密码并且一致后,才能创建用户.这个场景需要同时提供Form端的验证和DomainClass端的验证,而passwordconfirm验证必须在Form端完成.由于目前Grials仅支持在domainclass上的验证,所以我们只能在controller里面用类似的代码来验证:
{{{
#!python
//FIXMEweneeddovalidationagainsttheformdatainsteadofthedomainclass
if(!params.password.equals(params.password_confirm)){
flash['error']="Thepasswordmustmatch"
returnparams
}
}}}
(4)Grails0.4不支持domain/controller里面的subfolder,这也是很必要的一个功能, 试想写Java代码但是不能用package,所有类都在一个目录里...受不了
(5)Plugin机制还不够完善,灵活性不够,反而大大降低了系统性能.Domainclass无法在APP中使用,gsp文件无法在app中被正确load...
(6)GORMmapping有限制,复杂应用还是得靠手动些JavaClass/Annotation/Mappingfile来完成.如果domainclass变成pojo,由于这些entity是由Hibernate创建的,dependencyinjection就成了问题这些东西就变成了Entity,至少不再是richdomainobject咯.另外,问题(1)也会随之而来.
(7)映射关系复杂到一定程度,在saveentityobjectgraph的时候,就会碰到Hibernate中著名的jdbcbatchexecutionexception问题.而我们无法在Service中处理tx,session.新增加的withTransaction{}closure也只是给你一个springtransactionManager,你只能savePoint()orsetRollbackOnly()而无法操控整个TX
以下代码是一POGOServiceTestCase,其中创建一系列POGO,save,然后再调用userServiceloadentity.我们需要用一个Session来创建并save这些POGO,然后另开一个Session来load这些POGO进行验证.但似乎testcase缺省的用了OpenSessionInViewpattern,从头到尾只有一个Session....哪怕让我session.evict()也好啊....
(8)Domainclass的delete()方法永远返回null,所以无法判断删除操作是否成功
----
2007-02-06继续批判
(9)Reload机制太弱.只有在controller/view上的代码改动,才可以reload.如果Domainclass被改动了,会报Unabletore-createconfigurationforreloadedclass500错误.同样修改过的transactionalsevice也不能reload.[[br]]按照grailsdoc的说法,
{{{
Domainclassesarecoretoanybusinessapplication.Theyholdstateaboutbusinessprocessesandhopefullyalsoimplementbehavior.
}}}
{{{
Aserviceisaclassthatholdsoneormoremethodsthatimplementbusinesslogic.Logicalpartsofthebusinesslogicarecontainedinseparateserviceclasses.Servicesaredemarcatedbytransactionstomakeunitsofworkatomiconthepersistencelevel.Whencertaintypesofexceptionsarethrowntransactionsarerolledback.
}}}
毫无疑问,domain和service中将含有绝大部分的业务逻辑代码,而你现在稍微一改动代码就要重新运行grailsrun-app,运行一次就是几十秒.对有过TDD经历人,这种等待绝对是折磨!