情景分析
在Spring的諸多應用場景中bean都是單例形式,當一個單利bean需要和一個非單利bean組合使用或者一個非單利bean和另一個非單利bean組合使用時,我們通常都是將依賴以屬性的方式放到bean中來引用,然后以@Autowired來標記需要注入的屬性。但是這種方式在bean的生命周期不同時將會出現很明顯的問題,假設單利bean A需要一個非單利bean B(原型),我們在A中注入bean B,每次調用bean A中的方法時都會用到bean B,我們知道Spring Ioc容器只在容器初始化時執行一次,也就是bean A中的依賴bean B只有一次注入的機會,但是實際上bean B我們需要的是每次調用方法時都獲取一個新的對象(原型)所以問題明顯就是:我們需要bean B是一個原型bean,而事實上bean B的依賴只注入了一次變成了事實上的單利bean。
代碼說明
@Component
@Scope("prototype")
public class PrototypeBean {private static final Logger logger= LoggerFactory.getLogger(PrototypeBean.class);public void say() {logger.info("say something...");}
}
@Component
public class SingletonBean {private static final Logger logger = LoggerFactory.getLogger(SingletonBean.class);@Autowiredprivate PrototypeBean bean;public void print() {logger.info("Bean SingletonBean's HashCode : {}",bean.hashCode());bean.say();}
}
@SpringBootApplication
public class SampleApplication {private static final Logger logger = LoggerFactory.getLogger(SampleApplication.class);public static void main(String[] args) {SpringApplication.run(SampleApplication.class, args);}@Bean public CommandLineRunner test(final SingletonBean bean) {return (args)-> {logger.info("測試單例bean和原型bean的調用");int i =0;while(i<3) {i++;bean.print();}};}
}
結果
2018-12-06 15:04:29,721 INFO :-- [main .. ] o.s.SampleApplication 測試單例bean和原型bean的調用
2018-12-06 15:04:29,723 INFO :-- [main .. ] o.s.a.SingletonBean Bean SingletonBean's HashCode : 1713129148
2018-12-06 15:04:29,723 INFO :-- [main .. ] o.s.a.PrototypeBean say something...
2018-12-06 15:04:29,723 INFO :-- [main .. ] o.s.a.SingletonBean Bean SingletonBean's HashCode : 1713129148
2018-12-06 15:04:29,724 INFO :-- [main .. ] o.s.a.PrototypeBean say something...
2018-12-06 15:04:29,724 INFO :-- [main .. ] o.s.a.SingletonBean Bean SingletonBean's HashCode : 1713129148
2018-12-06 15:04:29,724 INFO :-- [main .. ] o.s.a.PrototypeBean say something...
我們看到每次輸出PrototypeBean的HashCode都是一樣的,證明我們實際上并沒有達到使用原型bean的目的。
解決方案
- 在beanA中引入ApplicationContext每次調用方法時用上下文的getBean(name,class)方法去重新獲取beanB的實例。 使用@Lookup注解。
- 這兩種解決方案都能解決我們遇到的問題,但是第二種相對而言更簡單。以下給出兩種解決方案的代碼示例。
通過應用上下文ApplicationContext獲取獲取
@Component
public class SingletonBean {private static final Logger logger = LoggerFactory.getLogger(SingletonBean.class);@Autowiredprivate ApplicationContext context;public void print() {PrototypeBean bean = getFromApplicationContext();logger.info("Bean SingletonBean's HashCode : {}",bean.hashCode());bean.say();}/*** 每次都從ApplicatonContext中獲取新的bean引用* @return PrototypeBean instance*/PrototypeBean getFromApplicationContext() {return this.context.getBean("prototypeBean",PrototypeBean.class);}
}
結果
2018-12-06 15:10:01,485 INFO :-- [main .. ] o.s.SampleApplication 測試單例bean和原型bean的調用
2018-12-06 15:10:01,487 INFO :-- [main .. ] o.s.a.SingletonBean Bean SingletonBean's HashCode : 376601041
2018-12-06 15:10:01,487 INFO :-- [main .. ] o.s.a.PrototypeBean say something...
2018-12-06 15:10:01,487 INFO :-- [main .. ] o.s.a.SingletonBean Bean SingletonBean's HashCode : 2056499811
2018-12-06 15:10:01,487 INFO :-- [main .. ] o.s.a.PrototypeBean say something...
2018-12-06 15:10:01,488 INFO :-- [main .. ] o.s.a.SingletonBean Bean SingletonBean's HashCode : 890733699
2018-12-06 15:10:01,488 INFO :-- [main .. ] o.s.a.PrototypeBean say something...
我們看到每次我們調用print()方法時都會重新從應用上下文獲取新的引用,達到了使用原型的目的。
通過@Lookup注解實現方法注入
使用方法注入的方法需要滿足以下語法要求
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
@Component
public abstract class SingletonBean {private static final Logger logger = LoggerFactory.getLogger(SingletonBean.class);public void print() {PrototypeBean bean = methodInject();logger.info("Bean SingletonBean's HashCode : {}",bean.hashCode());bean.say();}// 也可以寫成 @Lookup("prototypeBean") 來指定需要注入的bean@Lookupprotected abstract PrototypeBean methodInject();
}
結果
2018-12-06 15:18:50,105 INFO :-- [main .. ] o.s.SampleApplication 測試單例bean和原型bean的調用
2018-12-06 15:18:50,108 INFO :-- [main .. ] o.s.a.SingletonBean Bean SingletonBean's HashCode : 1349373781
2018-12-06 15:18:50,108 INFO :-- [main .. ] o.s.a.PrototypeBean say something...
2018-12-06 15:18:50,108 INFO :-- [main .. ] o.s.a.SingletonBean Bean SingletonBean's HashCode : 1046820071
2018-12-06 15:18:50,109 INFO :-- [main .. ] o.s.a.PrototypeBean say something...
2018-12-06 15:18:50,109 INFO :-- [main .. ] o.s.a.SingletonBean Bean SingletonBean's HashCode : 1722645488
2018-12-06 15:18:50,110 INFO :-- [main .. ] o.s.a.PrototypeBean say something...