`
raymond2006k
  • 浏览: 290046 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

我的开发经验分享(一)-Spring业务bean零配置

阅读更多

 注:在JavaEye注册不久,前些天这篇文章发布在了blog首页,似乎大家都在论坛活动多,因此转移到论坛这边分享

关键字: spring 动态bean管理 零配置

1. Spring业务bean配置的困扰

    利用Spring IoC 容器进行项目开发的一个重要工作就是在 applicationContext.xml 中进行bean管理。然而,业务bean(BO或Service),DAO bean的配置量随项目的功能规模增长而增长,对于大型项目而言,庞大的 bean配置文件维护起来极为痛苦。例如:

  1. <!-- 业务对象 -->        
  2. <bean id="authService" parent="baseTransactionProxy">        
  3.     <property name="target">        
  4.         <bean class="com.sr.jsfuser.service.AuthorityService">        
  5.             <property name="authDao" ref="authDao" />        
  6.         </bean>        
  7.     </property>        
  8. </bean>        
  9.         
  10. <bean id="userService" parent="baseTransactionProxy">        
  11.     <property name="target">             
  12.         <bean class="com.sr.jsfuser.service.pojo.UserService">        
  13.             <property name="userDao" ref="userDao" />        
  14.         </bean>        
  15.     </property>        
  16. </bean>        
  17. <!-- 数据访问对象 -->        
  18. <bean id="authDao" class="com.sr.jsfuser.dao.jdbc.AuthorityDao">        
  19.     <property name="dataSource" ref="dataSource" />        
  20. </bean>        
  21.         
  22. <bean id="userDao" class="com.sr.jsfuser.dao.jdbc.UserDao">        
  23.     <property name="dataSource" ref="dataSource" />        
  24. </bean>      

上例为两个功能的配置,鉴权(auth)和用户(user)。这种Service bean和DAO bean 的管理方式是按功能的管理方式,即one by one,配置量是
         F=f(n)
   

   其中 n 是项目中功能的数量。上例虽充分体现了Spring IoC容器依赖注入的优良特性,但配置量过大,维护和管理难度很大,不是一种最佳实践。


2. 零配置的动态Service Bean管理(ServiceFactory)  


     有没有一种既能发挥Spring优势,又能节省配置的模式呢?我们可以观察到,每个功能的Service bean的配置,只有 dao 注入是不同的,其他如Service类的命名,Service的事务管理器注入,Service类和DAO类的命名关系等都具有相似性或者完全相同;dao bean配置类似。

    因此,可以采用动态bean管理(Dynamic Bean Management)思想简化业务bean配置。动态bean管理使用新定义的 ServiceFactory 的createService方法来创建业务类Service实例, ServiceFactory  在创建时,自动进行dao创建,datasource注入等,不再需要进行bean的配置。原理如下图所示。



ServiceFactory原理有以下要点:
1) 利用了Spring ApplicationContext 的 registerBeanDefinition方法和动态bean注册技术;
2) 作为bean管理的一个规范:要求Service类实现 setDAO(DAO dao)方法,以便统一注入dao实例;
3) 作为bean管理的一个规范:调用 dao.setDatasource(datasource)方法进行数据源注入;
4) 作为bean管理的一个规范:内部使用className作为 service bean 和dao bean的内部id;
5) 作为bean管理的一个规范:ServiceFactory内获取bean实例时,都采用prototype scope


ServiceFactory内部实现序列图如下:



使用ServiceFactory 进行业务开发,代码如下:



UserService userService = (UserService)ServiceFactory.createService(UserService.class); User user = userService.findUser("admin");
 
这样,第一节中列出的spring配置片段中大量的业务bean配置将不再需要,使得配置工作量大大减少。

至于事务管理,即可使用向service注入transactionManager; 也可以使用 Spring 的
    org.springframework.transaction.interceptor.TransactionInterceptor ,
    org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator,
    org.springframework.aop.support.NameMatchMethodPointcutAdvisor
     三者配合,将 *Service类的 特定方法纳入事务范围。我们项目中使用的是后者,更为简便和松耦.


3. 通用的动态Bean管理(自定义BeanFactory类)  

    在上例中,注意新增了一个BeanFactory,区别于Spring 的 BeanFactory 接口。自定义 BeanFactory 通过 createBean(className) 进行动态bean注册和创建,
        

Object bean = BeanFactory.createBean(UserService.class);

    相当于替代了Object bean = new UserService();

  这相当于替代了 Java语言级的 new Object() ,并且使得类实例和Spring容器关联了起来,这样使用Spring的AOP配置就可以极为方便的对bean进行控制。(而上例中的ServiceFactory是该项目中利用BeanFactory的特定封装,其他项目的ServiceFactory实现可能不同,可参考实现)

4. JDK问题

     以上方案主要针对1.4。 但同样适用1.5及以上。有朋友说,1.5中用 annotation + <context:component-scan/>标签也能完成类似的工作,但那样需要在 Java 代码中写很多 annotation , 个人不偏好使用 annotation ,因为它相当于xml配置移到了 Java代码中,并且维护不方便。

分享到:
评论
29 楼 108439162 2014-01-02  
不得不说,可能博主自己觉得这样做很牛逼了。但是你忘了依赖注入的初衷是什么?就是将代码的各种new Instance()操作摘出来。项目过大。造成配置文件过多,完全可以通过按模块划分来解决。或者注解也是不错的方式,如果按照博主的想法,如果项目过大。做完一期之后,做二期的时候,需要修改功能。看到代码中的各种创建servce这简直是个噩梦。
28 楼 pikachu 2008-09-28  
raymond2006k 写道


还是你看明白了我的文章。




所以你的文章和老外的文章还是有差距的.再好的想法,不能清晰的表述,总是会带来这样那样的问题.


可以参考一下那些老外的行文,多看看他们的paper.毕竟技术类文章写作思路都是一致的,标准八股文写法是

1.概述或介绍.主要是背景,问题,所依赖技术以及主要功能
2.详述.
3.总结.使用方案后的效果,优势,同其他类似产品的比较
27 楼 raymond2006k 2008-09-28  
mazzystar 写道
这样配置文件就不完整了,配置文件中不能明确展现类之间的关系,看了之后肯定一头雾水,不如全部都零配置吧


对于业务bean, XXXService,XXXDAO,一般是byName来 autowire。
对于类之间的关系,按这种约定型设计,本来就是一目了然的。 userService当然要注入 userDAO,我想你不会把  orderDAO 注入给 userService吧。
业务bean的名称是稳定的, userService一旦确定,我想以后没有特殊理由,应该不会再改了。

数据库配置,事务配置,这些可变的信息才应该在applicationContext.xml 中配置,体现IoC思想。
26 楼 raymond2006k 2008-09-28  
flyingpigaa 写道
同意楼上观点,我们项目里spring 里bean 也超多,但并没有阅读困难。楼主是不是有点因噎废食


没有阅读困难就好,呵呵;每个项目,自己的方案用惯了就好; 没有说谁一定要替代谁。
如果有的话,或觉得配置麻烦的话,才需要参考我的方案。

我是在看Spring书籍的第三天,看到 service ,dao要一个个配置,我当时就有一个念头,我不想让程序员配置,一个都不想,我要减少他们的工作量 —— 零配置。
25 楼 raymond2006k 2008-09-28  
Readonly 写道
为什么不反思一下别人为啥不赞同你的帖子呢?反而跑去说别人的帖子如何如何,这不是典型的酸葡萄心理么...

另外关于你的方案,难道你不知道spring有autowire功能么?


唉,你还没看明白我的文章到底在讨论什么问题。

如果你是一个个配置,当然可以用 autowire 了;在你目前的用法下,OK的啊。
而我讨论的是如何不配置,零配置的问题。
24 楼 raymond2006k 2008-09-28  
bloodwolf_china 写道
我觉得这个这个注意不错啊,有点像grails中的思想,用约定替代配置。而且可以继承Spring BeanFactory写一个工厂类来生成service。


"用约定替代配置", 就是这个了。
XXXService,XXXDAO 的包名,类名制定了统一规则,正是按这个规则,可以自动为Service 找到对应的DAO类,并进行动态注入。
23 楼 raymond2006k 2008-09-28  
BeanFactory.createBean(UserService.class),
pikachu 写道

事实上构想不错,要是没有理解错的话,这个组件完成的是service,dao,transaction之间的依赖.并且将spring作为一个硕大的factory. 现在大家的问题是,为什么在更前端的代码要调用BeanFactory.createBean(UserService.class),难道不能进一步将service也注入么?


还是你看明白了我的文章。
Spring我还是看作IoC容器,
BeanFactory 代码我没贴出来吧,可能真的是漏了些材料:
/**
	 * 通过Spring容器创建bean
	 * 其中会检查bean的scope ,避免冲突。bean的scope取决与第一次创建时定义的scope。
	 * @param beanID
	 * @param beanClass
	 * @param isSingleton
	 * @return
	 */
	public static Object createBean(String beanID,Class beanClass,boolean isSingleton ) {
		
		if(!SpringContextManager.containsBean(beanID)) {
			
			if(log.isDebugEnabled())
				log.debug("bean dynamic register " + beanID +",class:" + beanClass +",scope(singleton?)" + isSingleton );
			SpringContextManager.registerBean(beanID, beanClass, isSingleton);
		}else {  //检查bean的scope ,避免冲突。bean的scope取决与第一次创建时定义的scope。
			BeanDefinition def = SpringContextManager.getBeanDefinition(beanID);
			if(isSingleton != def.isSingleton()) {
				throw new IllegalArgumentException("Can't register bean as " +  ( !def.isSingleton() ? "singleton" : "prototype")  + ", it's already registered as "
							+ (def.isSingleton() ? "singleton" : "prototype") +". Bean beanID '" + beanID  +"'");
			}
		}
		return getBean(beanID);	
	}




实际就是Spring context 的一个简单封装而已,加入动态注册功能,createBean(UserService.class),还是将对象注入IoC容器; 注入了容器,Spring 的AOP,事务等就能自动关联。
22 楼 fireflyc 2008-09-28  
你把配置文件分开不久可以了吗。
而且如果你真的想实现0配置的话也行,spring的注解支持;你现在的设计…………颇有黑代码的味道。
21 楼 MarkDong 2008-09-28  
如果说约定优于配置,那就用autowire,至少那个方式对代码是无侵入的,楼主的这种方式以后再想改为配置的,那就难了
20 楼 bloodwolf_china 2008-09-28  
我觉得这个这个注意不错啊,有点像grails中的思想,用约定替代配置。而且可以继承Spring BeanFactory写一个工厂类来生成service。
19 楼 MarkDong 2008-09-28  
并不觉得楼主的方法是个好方法,基于以下几点:
1、用配置文件的方式只在Spring初始化的时候进行一次注入就OK了,而楼主的方法每次均需要重新注入(至少从时序图上是这样显示的),这就带来了大量不必要的系统开销。
2、结合Spring插件,可以通过图形的方式很直观的看到各层之间的依赖关系,这也是为什么很多人都不推荐autowire的原因。而楼主这种方式连基本的bean配置都省略了,很可能引入一些很难追查源头的bug
18 楼 mazzystar 2008-09-28  
这样配置文件就不完整了,配置文件中不能明确展现类之间的关系,看了之后肯定一头雾水,不如全部都零配置吧
17 楼 flyingpigaa 2008-09-28  
同意楼上观点,我们项目里spring 里bean 也超多,但并没有阅读困难。楼主是不是有点因噎废食
16 楼 pikachu 2008-09-28  


事实上构想不错,要是没有理解错的话,这个组件完成的是service,dao,transaction之间的依赖.并且将spring作为一个硕大的factory.

现在大家的问题是,为什么在更前端的代码要调用BeanFactory.createBean(UserService.class),难道不能进一步将service也注入么?
15 楼 Readonly 2008-09-27  
为什么不反思一下别人为啥不赞同你的帖子呢?反而跑去说别人的帖子如何如何,这不是典型的酸葡萄心理么...

另外关于你的方案,难道你不知道spring有autowire功能么?
14 楼 raymond2006k 2008-09-27  
jones 写道
完全丧失IOC的核心理念,越改越不伦不类



今天刚看到另一网友发的贴:LightURL——打造零配置的Struts2开发, 介绍老外的一个插件的用法。

感慨啊感慨, 在JavaEye晃了一个来月真的感慨。
同样是 9-16号发的,他的贴很多网友赞同,精华贴;
我的贴很少人赞同,不赞同的回复一般都是不符合 ”Spring的标准适用模式“,没有体现IoC思想。

我分析了一下, 我们都提出了零配置的思想的实现,一个针对Struts的Action配置,一个针对Spring下的业务bean配置:

介绍老外的方案,赞成的人多; javaeyer 自己钻研的方案反对的人多.
介绍别人的方案,以易用性为判断标准; 介绍自己项目中成功的方案,以是否符合”标准模式“为判断标准。(其实未透彻理解IoC的核心价值和用法,坚持教条).
介绍apache上的方案,基本都是"不错,有机会要尝试一下 "; 介绍自己的方案,基本都是质疑之声,而又提不出信服的理由。

唉,这就是中国的软件界,感慨啊感慨。
哈哈,一笑了之。
13 楼 weizh 2008-09-27  
看来你是要OSGi,而非spring。
12 楼 raymond2006k 2008-09-27  
finalbone 写道
这种实现看起来挺别扭 到处都要显式调用Factory 另外遇到service循环引用怎么处理



我的方式是显示的 
BeanFactory.getBean(beanClass.getName());
BeanFactory.createBean(beanClass);

而"标准用法"是 context.getBean("userService");

你说说二者有什么本质区别没有? 即便连Action 的service引用都是通过配置注入的, 但你总有个地方 需要 指定
“userService”, 或者 ${userService}。
11 楼 raymond2006k 2008-09-27  
miaomiao0307 写道
我们不是应该将java代码拿到配置中,而不是将配置拿到java代码中吗?
这样还叫做注入吗


有时间看看Spring 的源代码吧,你会有所发现的。
10 楼 raymond2006k 2008-09-27  
zxbyhcsdn 写道
Object bean = BeanFactory.createBean(UserService.class);
这个???
拿还怎么体现IoC啦,
IoC的思想就是对象不去关心他依赖的其他对象,也不自己去做对象的获取.
他只是 面向 依赖的接口, 那个接口具体的指向什么对象,是IoC容器的事情.

业务逻辑模块并不需知道更不必调用组件框架的服务,例如不用关心和调用其factory或lookup其directory或context等。软件的组装部署和配置完全是由Ioc容器反过来主动控制业务逻辑模块来安排。Michael Mattson用所谓的好莱坞原则(Hollywood Principle)"别来找我,到时候,我会去找你"(don't call me, I will call you)形象地比喻了这一设计思想。这个比喻中的"我"指的是Ioc容器,"你"则是被其调遣配置的一个组件。


还有一点,开发中,最好针对一个用例建一个spring的配置文件,
而且子系统或者模块的话,可以建一个文件甲,然后把这模块的用例对应的配置文件放在一个文件甲里面.
这样你有时候需要去弄清楚某一个用例的类关系,看spring配置文件反倒是一件方便的事情了.

其实可以把srping配置文件看成是类图中类关系的一种表示



你可以把我的用法看作一个Spring BT用法,IoC不单指配置型注入,还有动态注入,Spring提供了 registerBean(beanDefinition) 的机制。
或者我的用法可以算 use Spring without Spring,呵呵;我的目标不是为Ioc而Ioc,作为架构师我的第一要务是减少程序员的工作量,工作的复杂度, 而不是死板和教条的墨守成规。

相关推荐

Global site tag (gtag.js) - Google Analytics