javascript
单元测试 applicationinfomanager bean无法注入_你真的会用Spring吗?如何在单例Bean中注入原型Bean...
遇到什么問題
假設單例 BeanA 需要使用原型 BeanB(BeanB 可能是 BeanA 的一個屬性值)。可是容器僅創建一次單例 BeanA,因此只有一次機會來設置屬性 BeanB。
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)@Servicepublic class OrderService {}@Servicepublic class UserService {@Autowiredprivate OrderService orderService;public OrderService getOrderService() {return orderService;}}@Configuration@ComponentScanpublic class Main {public static void main(String[] args) {AnnotationConfigApplicationContext context =new AnnotationConfigApplicationContext(Main.class);UserService userService = context.getBean(UserService.class);OrderService orderService = userService.getOrderService();OrderService orderService1 = userService.getOrderService();//tureSystem.out.println(orderService == orderService1);}}如果直接使用@Autowired注入,容器僅創建一次單例UserService,因此只有一次機會來設置OrderService。
那么,如何在單例 Bean 中注入原型 Bean 呢?
解決方案 1:實現 ApplicationContextAware
第一種解決方案,可以讓UserService實現ApplicationContextAware接口,然后在每次需要使用原型 BeanOrderService時通過調用容器的getBean方法。
@Servicepublic class UserService implements ApplicationContextAware {private ApplicationContext context;public OrderService getOrderService() {return context.getBean(OrderService.class);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {context = applicationContext;}}Spring 官方并不建議使用這種方式:
The preceding is not desirable, because the business code is aware of and coupled to the Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC container, lets you handle this use case cleanly.
前面的內容是不理想的,因為業務代碼知道并耦合到 Spring 框架。 方法注入是 Spring IoC 容器的一項高級功能,使您可以干凈地處理此用例。
解決方案 2:使用@Lookup,實現方法注入
@Lookup
先來看一下@Lookup源碼
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Lookup {/** * This annotation attribute may suggest a target bean name to look up. * If not specified, the target bean will be resolved based on the * annotated method's return type declaration. */String value() default "";}@Lookup默認是通過方法的返回類型聲明來解析目標 Bean,也可以通過 value 來指定需要查找的目標 BeanName。
介紹
https://docs.spring.io/spring/docs/5.1.9.RELEASE/spring-framework-reference/core.html#beans-factory-lookup-method-injection
Lookup method injection is the ability of the container to override methods on container-managed beans and return the lookup result for another named bean in the container. The lookup typically involves a prototype bean, as in the scenario described in the preceding section. The Spring Framework implements this method injection by using bytecode generation from the CGLIB library to dynamically generate a subclass that overrides the method.
機器翻譯:查找方法注入是容器覆蓋容器管理的 Bean 上的方法并返回容器中另一個命名 Bean 的查找結果的能力。 查找通常涉及原型 bean,如上一節中所述。 Spring 框架通過使用從 CGLIB 庫生成字節碼來動態生成覆蓋該方法的子類來實現此方法注入。
使用限制
For this dynamic subclassing to work, the class that the Spring bean container subclasses cannot be final, and the method to be overridden cannot be final, either.
為了使此動態子類起作用,Spring Bean 容器子類的類也不能是 final,而要覆蓋的方法也不能是 final。
Unit-testing a class that has an abstract method requires you to subclass the class yourself and to supply a stub implementation of the abstract method.
對具有抽象方法的類進行單元測試需要您自己對該類進行子類化,并提供該抽象方法的存根實現。
A further key limitation is that lookup methods do not work with factory methods and in particular not with @Bean methods in configuration classes, since, in that case, the container is not in charge of creating the instance and therefore cannot create a runtime-generated subclass on the fly.
另一個關鍵限制是,查找方法不適用于工廠方法,尤其不適用于配置類中的@Bean 方法,因為在這種情況下,容器不負責創建實例,因此無法創建運行時生成的子類。
根據 Spring 官方文檔,我們可以知道:
使用@Lookup 實現單例 Bean 中注入原型 Bean
@Servicepublic abstract class UserService {@Lookuppublic abstract OrderService getOrderServiceUsingLookup();}雖然這個類是抽象的,但是還可以被實例化到 Spring 容器中,因為 Spring 會對當前類生成子類來實現方法注入。至于具體是怎么生成的增強對象,讀者可以自行 debug 源碼學習。
UserService代理對象
總結
以上是生活随笔為你收集整理的单元测试 applicationinfomanager bean无法注入_你真的会用Spring吗?如何在单例Bean中注入原型Bean...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python字符串中某个字符修改_Pyt
- 下一篇: python decorator ssh