序
人人都爱Spring加Hibernate。
但Spring MVC+hibernate的Sample如Appfuse的代码却远算不得最简洁优美好读,如果在自己的项目中继续发挥我们最擅长的依样画葫芦大法,美好愿望未必会实现。
所以,Pramatic精神不灭。这个系列就是探寻最适合自己的Spring+Hibernate模式。
BTW. Rod在他的新书《Professional Java Development with the Spring Framework》里,终于亲笔写了一个比较大的Sample,可以作为大家的参考盲从,减少一些争执。
I-配置文件简化
我厌倦一切配置文件繁重的框架。
最好的情况是,框架提供极端灵活复杂的配置方式,但只在你需要的时候。
Spring提供了三种可能来简化XML。随着国内用户水平的提高,这些基本的简化技巧大家都已掌握,从Spring 1.2开始又有了小小的改进,
1.1.autowire="byName" /"byType"
假设Controller有一个属性名为customerDAO,Spring就会在配置文件里查找有没有名字为CustomerDAO的bean, 自动为Controller注入。
如果bean有两个属性,一个想默认注入,一个想自定义,只要设定了autowire,然后显式的声明那个想自定义的,就可以达到要求。这就应了需求,在需要特别配置的时候就提供配置,否则给我一个默认注入可以了。
还有一个更懒的地方,在最最根部的
不过Rod认为开发期可以这样,但Production Server上不应该使用Autowire,大家自己来决定了。
1.2.
这太方便懒人了,想不到两个独立的XML节点都可以玩继承和派生,子节点拥有父节点的全部属性。
最好用的地方就是那个Transtion Proxy的定义。先定义一个又长又冗的父类,然后用子类去继承它。
另外,还有一个Inner Bean的机制,可以把DAO写成Proxy的内部类。为什么要写成内部类?为了让Proxy冒名顶替它去让Controller Autowire。(详见后面的示例)
1.3. 宽松的配置, To XML or Not to XML
据说Spring比Struts的配置宽松了很多,这就给人把东西从配置文件中撤回原码中的机会。
不赞成什么都往配置文件里晒,造成了Rich Information的配置文件,修改或者查看的时候,要同时打开配置文件和原码才能清楚一切。
而我希望配置文件就集中做一些整体的配置,还有框架必须的、无需管理的冗余代码。而一些细节的变化不大的配置和逻辑,就尽量别往里塞了。因此,Success/Fail View 的配置,不建议放在里面。
2.简化后的配置文件
1.Controller只剩下一句
2.DAO只剩下5行
< property name ="target" >
< bean class ="com.itorgan.lherp.dao.impl.SaleOrderDAOImpl" />
property >
bean >
最主要的简化是把属性值和引用bean从 子节点变回了 属性值,对不喜欢autowire的兄弟比较有用。
当然,如果value要CDATA的时候还是要用子节点。
< value > fooValue value >
property >
简化为
< property name ="foo" value ="fooValue" />
2.引用 bean
< ref bean ="fooBean" >
property >
简化为
< property name ="foo" ref ="fooBean" />
3. list如果只有一个值时,可以直接用value表示
< list >
< value > wuyu value >
list >
property >
简化为
< property name ="myFriendList" value ="wuyu" />
因为只有一个值时,List就退化为Object类型嘛,多体贴。
如果没有用Eclipse的Spring插件,那至少也要使用spring自带的dtd使XML编辑器smart一些,能够自动为你生成属性,判断节点/属性名称有没有拼错等。
5.还有更变态的简化配置方法
比如autoproxy,不过我觉得更简化就不可控了,所以没有采用。
(2)--Model层
但Appfuse的model层总共有一个DAO接口、一个DAOImpl类、一个Service接口、一个ServiceImpl类、一个DataObject.....大概只有受惯了虐待的人才会欣然接受吧。
另外,Domain-Driven逢初一、十五也会被拿出来讨论一遍。
其实无论什么模式,都不过是一种人为的划分、抽象和封装。只要在团队里理解一致,自我感觉优雅就行了。在我的Model层里,DO和Manager一生一旦包演全场,VO作为纯数据载体,而Manager类放置商业方法,用getHibernateTemplate()直接访问数据库,不强制基于接口编程......
1.DataObject类
好听点也可以叫Domain Object。Domain Driven Development虽然诱人,但因为Java下的ORM框架都是基于Data Mapper模式的,没有Ruby On Rails中那种Active Recorder的模式。所以,还是压下了这个欲望,Data Object纯粹作一个数据载体,而把数据库访问与商业逻辑操作统一放到Manager类中。
2.Manager类
我的Manager类是Appfuse中DAO类与Service类的结合体,因为:
2.1 不想使用纯DAO
以往的DAO是为了透明不同数据库间的差异,而现在Hibernate已经做的很好。所以目前纯DAO的更大作用是为了将来可以切换到别的ORM方案比如iBatis,但一个Pragmaic的程序员显然不会无聊到为了这个机会不大的理由,现在就去做一个纯DAO层,项目又不是Appfuse那样为了demo各种ORM方案而存在。
2.2 也不使用纯的薄Service层
在JPetStore里有一个很薄的Service层,Fascade了一堆DAO类,把这些DAO类的所有方法都僵硬的重复了一遍。而我认为Fascade的意义在二:
一是Controller调用Manager甲的时候,总会伴随着调用Manager乙的某些方法。使用Fascade可以避免Controller零散的调用一堆Manager类。
二是一个商业过程里可能需要同时调用Manager甲乙丙丁的方法。
这些时候,Fascade都是合理的。但我讨厌类膨胀,所以我宁愿在甲乙丙丁中挑一个来充当Fascade的角色。有耦合的问题吗?对一个不是死搬书的Designer来说,组件边界之内的类之间的耦合并不是耦合。
3.去除不必要的基于接口编程
众所周知,Spring是提倡基于接口编程的。
但有些Manager类,比如SaleOrderManager ,只有5%的机会再有另一个Impl实现。95%时间里这两兄弟站一起,就像C++里的.h和.cpp,徒增维护的繁琐(经常要同步两个文件的函数声明),和代码浏览跳转时的不便(比如从Controler类跟踪到Service类时,只能跳转到接口类的相应函数,还要再按一次复杂的热键才跳转到实现类)
连Martin Flower都说,强制每个类都分离接口和实现是过犹不及。只在有多个独立实现,或者需要消除对实现类的依赖时,才需要分离接口。
3.1 DAO被强制用接口的原因
Spring IOC本身是不会强制基于接口的,但DAO类一般要使用Spring的声明式事务机制,而声明式的事务机制是使用Spring AOP来实现的。Spring AOP的实现机制包括动态代理和Cgilib2,其中Spring AOP默认使用的Java动态代理是必须基于接口,所以就要求基于接口了。
3.2 解决方法
那就让Spring AOP改用CGLib2,生成目标类的子类吧,我们只要指定使用声明式事务的FactoryBean使用CGLib的方式来实现AOP,就可以不基于接口编程了。
指定的方式为 设置proxyTargetClass为true。如下:
id ="baseService" abstract ="true" >
< property name ="transactionManager" ref ="transactionManager" />
< property name ="proxyTargetClass" value ="true" />
bean >
又因为这些Service Bean都是单例,效率应该不受影响。
4.总结
对比Appfuse里面的5个类,我的Model层里只有VO作为纯数据载体,Manager类放商业方法。有人说这样太简单了,但一个应用,要划成几个JSP,一个Controller,一个Manager,一个VO,对我来说已经足够复杂,再要往上架墙叠屋,恕不奉陪,起码在我的项目范围里不需要。(但有很多项目是需要的,神佑世人)
(3)--Controller层
MVC不就是把M、V、C分开么?至唯物朴素的做法是两个JSP一个负责View,一个负责Controller,再加一个负责Model的Java Bean,已经可以工作得很好,那时候一切都很简单。
而现在为了一些不是本质的功能,冒出这么多非标准的Web框架,实在让人一阵郁闷。像Ruby On Rails那样简捷开发,可用可不用,而且没有太多的限制需要学习的,比如 Webwork这型还可以考虑。但像Struts那样越用框架越麻烦,或者像Tapestry那样有严重自闭倾向,额上凿着"高手专用玩具"的,用在团队里就是不负责任的行为了。
1. 原理
我们的Controller层,就是实现handleRequest(request,response)函数的普通JavaBean。
2. 优势
Spring MVC与struts相比的优势:
如果需要application对象,比如想用application.getRealPath()时,就要extends webApplicationObjectSupport。
3.2.每个Controler负责一组相关的action
以上三者都是把URL参数直接反射为Controller的函数,而 Stripes的设计可用annotation标注url action到响应函数的映射。
3. 5.提取基类
经过化简再化简,已经是很简单一个Java Bean ,任谁都可以轻松上手,即使某年某月技术的大潮把现在所有MVC框架都淹没了,也不至于没人识得维护。
(4)--View层
同样的,Freemarker和Velocity爱好者请跳过本篇。与弃用webwork而单用Spring MVC Controller接口的理由一样, Freemarker本来是一样好东西,还跨界支持jsp 的taglib,而且得到了WebWork的全力支持,但为了它的非标准化,用户数量与IDE的缺乏,在View层我们还是使用了 保守但人人会用,IDE友好的JSP2.0 配合JSTL。
对于B/S结构的企业应用软件来说,基本的页面不外两种,一种是填Form的,一种是DataGrid 数据列表管理的,再配合一些css, js, ajax的效果,就是View层要关注的东西了。
1. JSP 2.0的EL代替
JSP2.0可以直接把EL写在html部分,而不必动用
代替
< p >< c:out value ="{goods.name}" /> p >
(除了EL里面不能调用goods的函数,sun那帮老顽固始终坚持JSTL只能用于数据显示,不能进行数据操作,所以不能调用bean的get/set外的方法)
2. 最懒的form 数据绑定
Spring少得可怜的几个tag基本上是鸡肋,完全可以不要。 而Spring开发中的那些Simple Form tag又还没有发布。Spring的Tag主要用来把VO的值绑到input框上。但是,和Struts一样,需要逐个Input框绑定,而且语法极度冗长,遇到select框还要自己进行处理.....典型的Spring Sample页面让人一阵头晕.
而jodd的form tag给了我们懒人一个懒得多的方法,只要在