本文共 4201 字,大约阅读时间需要 14 分钟。
使用spring框架时,当在单例对象中注入一个原生对象是,注入的原生会变成单例对象,本文就这一问题介绍几种 解决方案。
在Spring Framework官方文档的第一章第四节方法注入中,最后一个点方法注入,详细描绘了这个问题,也为此提出了解决方案。截图如下:
User类代码:
@Component@Scope("prototype")public class User { public User() { System.out.println ("Constrctor of user"); } public void query(){ System.out.println ("useer `s hashCode"+this.hashCode ()); }}
把User类交给spring IOC容器管理,将其设置为原生对象,创建无参构造方法,打印一行文字,再添加一个普通的方法,打印当前对象的哈希值。
测试类代码:
public class MainTest { public static void main(String[] args) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext (AppConfig.class); annotationConfigApplicationContext.getBean (User.class).query (); annotationConfigApplicationContext.getBean (User.class).query (); annotationConfigApplicationContext.getBean (User.class).query (); annotationConfigApplicationContext.getBean (User.class).query (); }}
测试结果:
创建一个ServiceImpl类,注入User类,添加一个方法,调用user类的query方法。
ServiceImpl类代码:
@Componentpublic class ServiceImpl { @Autowired User user; public void query() { user.query (); }}
测试类代码:
public class MainTest { public static void main(String[] args) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext (AppConfig.class); annotationConfigApplicationContext.getBean (ServiceImpl.class).query (); annotationConfigApplicationContext.getBean (ServiceImpl.class).query (); annotationConfigApplicationContext.getBean (ServiceImpl.class).query (); annotationConfigApplicationContext.getBean (ServiceImpl.class).query (); }}
测试结果:
结果分析:以为ServiceImpl类是单例的,所以在Spring IoC容器创建该对象时只创建了一个ServiceImpl实例对象,同时创建了一个user实例对象,只后将user实例对象注册到ServiceImpl实例对象中。因为ServiceImpl类是单例的,所以在测试方法中使用getBean方法获取ServiceImpl对象时,annotationConfigApplicationContext对象直接从Spring IoC容器中获取,得到的ServiceImpl对象由于已经在初始化过程中已注入了user对象,所以可直接使用,整个过程中没有通过annotationConfigApplicationContext对象获取user对象,因此ServiceImpl对象中user的原生属性失效。
ApplicationContextAware
接口直接在Spring 容器中获取原生对象在spring framework官方文档中,1.4.6的方法注入中给了我们关于这个问题的解决方案。如图:
可以是单例对对象的类实现 ApplicationContextAware接口,该接口提供了setApplicationContext方法,传入了applicationContext对象,及Spring上下文对象,程序员可以通过该对象直接获取原型类对象,来解决上述问题。
具体解决方法为,在ServiceImpl中引入一个ApplicationContext对象,继承ApplicationContextAware接口,重写setApplicationContext方法,将该方法传入的ApplicationContext对象赋值给ServiceImpl类中的ApplicationContext对象。最后在query方法中使用ApplicationContext对象获取user对象。修改后代码如下:
@Componentpublic class ServiceImpl implements ApplicationContextAware { ApplicationContext applicationContext;// @Autowired User user; public void query() { applicationContext.getBean (User.class).query (); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; }}
运行测试类结果为:
可以看到已经解决了上述问题。这种解决方案的弊端在于必须实现ApplicationContextAware接口,spring framework的侵入性较强。
这种方法的核心在于使用applicationContext对象重新获取原生对象,因为applicationContext对象本身就在Spring IoC容器中,所以可以直接将applicationContext对象注入到单例对象中。修改后的ServiceImpl来对象为:
@Componentpublic class ServiceImpl { @Autowired ApplicationContext applicationContext;// @Autowired User user; public void query() { applicationContext.getBean (User.class).query (); }}
将单例对象类修改成抽象了类,加入一个返回值类型为原型类的抽象方法,将该方法加上一个Lookup注解,这样spring framework就会利用CGLIB代理方法实现这个类,返回一个原生对象。spring官网中的截图如下:
修改后的ServiceImpl抽象类代码如下:
@Componentpublic abstract class ServiceImpl { // @Autowired User user; @Lookup public abstract User getPrototypUser(); public void query() { getPrototypUser ().query (); }}
因为spring framework是用代理的方法实现这个类,程序员也可以将上述的抽象方法改成返回值为空的方法,也是可以同样的效果。这种方法在官网上没有提到,但博主经过试验也可是可以,希望大家谨慎采用。更改后的ServiceImpl的代码如下:
@Componentpublic class ServiceImpl { // @Autowired User user; @Lookup public User getPrototypUser(){ return null; } public void query() { getPrototypUser ().query (); }}
转载地址:http://wntvb.baihongyu.com/