Spring这个响亮的名字,在java web应用开发的程序员中无人不晓,也许我形容得力度还不够,在此我表示歉意。大家都在快乐的使用spring将自己的java class管理起来,而且还能快速的帮你将class设置成singleton或者non-singleton。一切显得那么简单和完美。
不知道你是否尝试过这样的配置:
<bean id="singletonOne" class="com.ai92.enjoyspring.SingletonBean" singleton="true"> <property name="ns"><ref bean="nonsingleton"/></property> </bean> <bean name="nonsingleton" class="com.ai92.enjoyspring.NonSingleton" singleton="false"/> |
在一个被设置为单例的bean里面依赖一个non singleton bean。SingletonBean定义如下,里面依赖的bean采用setter injection:
public class SingletonBean{ private NonSingleton ns ; public void setNs(NonSingleton ns) { this.ns = ns; } public NonSingleton getNs() { return ns; } } |
NonSingleton的实现则可以随便。那么下面的测试结果应该是如何?
ApplicationContext ctx = new FileSystemXmlApplicationContext("/config/SpringBeans-EnjoySpring.xml");
SingletonBean single = (SingletonBean)ctx.getBean("singletonOne"); SingletonBean single2 = (SingletonBean)ctx.getBean("singletonOne"); NonSingleton nSingle3 = single.getNs(); NonSingleton nSingle4 = single2.getNs(); compare2Object(nSingle3 , nSingle4); |
也许你一开始就已经知道了答案(就像我一样),但是估计还是一些人被Spring的假象所迷惑了。是的,上面代码中的nSingle3和nSingle4是同一个对象,没有像配置文件声明的那样成为不同的对象。
首先要怪写这样代码的人,他对Spring的依赖使他忘记了Spring也是Java代码的堆砌,也要遵循JVM的规章。不过也要埋怨下Spring,对这种情况在文档中仅仅用了很不起眼的一小段来告知处理方法,对不知情的人很可能造成潜伏的隐患,因为他们太信赖Spring了。
按照Spring的解释,很少遇到类似singleton bean依赖non-singleton bean的情况。对于这种情况,很遗憾,没有太好的解决方法。Spring给出了两种方法来解决这种问题。下面我介绍如下:
第一种方法,很容易想到,就是将SingletonBean 中的setter injection改为getBean直接产生。代码如下:
public NonSingleton getNs() { return (NonSingleton)ctx.getBean("nonsingleton"); } |
当然,这是使用了ApplicationContext,如果直接使用BeanFactory,那么需要SingletonBean implements BeanFactoryAware,这样代码就和Spring耦合在一起了。
public NonSingleton getNs() { return (NonSingleton)beanFactory.getBean("nonsingleton"); }
private BeanFactory beanFactory;
public void setBeanFactory(BeanFactory beanFactory) throws BeansException{ this.beanFactory = beanFactory; } |
第二个方法,是在singleton bean 中通过Spring提供的Method Injection来设置对non-singleton bean的依赖。
首先,SingletonBean 中要提供一个Method Injection的入口:
protected NonSingleton createNonSingleton(){ return null; } |
这个方法可以是抽象的,这就意味着SingletonBean也是抽象的;也可以提供自己的实现,就像上面那样。Spring都将动态的生成一个SingletonBean的子类,并override这个入口方法,这得益于CGLIB library。下面你需要做的就是修改配置文件:
<bean id="singletonOne" class="com.ai92.enjoyspring.SingletonBean" singleton="true"> <lookup-method name="createNonSingleton" bean="nonsingleton"/> </bean> |
然后在SingletonBean中做一点点修改:
public NonSingleton getNs() { return createNonSingleton(); } |
恩,这时候再运行测试程序,得到的就是不同的对象了。
也许这两种解决方案已经可以让你感到很满意。但是,你有没有认真地考虑过自己的代码?为什么会出现singleton bean依赖non-singleton bean的情况?为了线程安全?还是……
有很多时候,奇怪的要求来自于奇怪的设计,也许你修改下自己的代码,就可以避开上面提到的问题。