情景分析
在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); @Autowired private 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的目的。
解決方案
- 在bean A中引入
ApplicationContext
每次調用方法時用上下文的getBean(name,class)
方法去重新獲取bean B的實例。 - 使用
@Lookup
注解。
這兩種解決方案都能解決我們遇到的問題,但是第二種相對而言更簡單。以下給出兩種解決方案的代碼示例。
通過應用上下文ApplicationContext
獲取獲取
@Component public class SingletonBean { private static final Logger logger = LoggerFactory.getLogger(SingletonBean.class); @Autowired private ApplicationContext context; public void print() { //每次都從ApplicatonContext中獲取新的bean引用 PrototypeBean bean = this.context.getBean("prototypeBean",PrototypeBean.class); logger.info("Bean SingletonBean's HashCode : {}",bean.hashCode()); bean.say(); } }
結果
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...
通過@Lookup
注解實現方法注入
使用方法注入的方法需要滿足以下語法要求
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
public|protected要求方法必須是可以被子類重寫和調用的
abstract可選,如果是抽象方法,CGLIB的動態代理類就會實現這個方法,如果不是抽象方法,就會覆蓋這個方法
return-type是非單例的類型
no-arguments不允許有參數
@Component public class SingletonBean { private static final Logger logger = LoggerFactory.getLogger(SingletonBean.class); public void print() { PrototypeBean bean = getPrototypeBean().printClass(); logger.info("Bean SingletonBean's HashCode : {}",bean.hashCode()); bean.say(); } // 也可以寫成 @Lookup("prototypeBean") 來指定需要注入的bean @Lookup public PrototypeBean getPrototypeBean(){
reutrn null;
}
}
結果:
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...