大多数的 bean 在应用场景中都是单例的。当这个单例的 bean 需要和非单例的 bean 联合使用时,有可能会因为不同 bean 的生命周期而产生问题。假设单例的 bean A 在每个方法调用中使用了非单例的 bean B,由于容器只会创建 bean A 一次,而只有一次机会来配置属性。那么容器就无法给 bean A 每次都提供新的 bean B 的示例。下面将通过 lookup-method 注入来解决这个问题。
引入 Spring5 依赖,如下:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.29</version> </dependency>
打印器服务拥有一个简单的 print() 方法,向控制台输出一个字符串,同时,使用 @Scope 设置 Bean 的范围是 prototype 的,每次都会创建一个新的示例。
代码如下:
package com.hxstrive.spring5.lookup_method; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; /** * 打印器 * @author hxstrive.com */ @Component @Scope(value = "prototype") public class Printer { public void print() { System.out.println(this + " >> " + getClass().getName()); } }
打印器服务类用于管理所有的打印器,调用打印器的 print() 方法打印信息,注意,每次调用 PrinterManager 类的 show() 方法都会调用 createPrinter() 抽象方法,创建一个新的 Printer 打印器实例。
代码如下:
package com.hxstrive.spring5.lookup_method; import org.springframework.beans.factory.annotation.Lookup; import org.springframework.stereotype.Component; /** * Printer 管理器 * @author hxstrive.com */ @Component public abstract class PrinterManager { public void show() { Printer printer = createPrinter(); printer.print(); } //使用 CGLIB 库,重新生成子类,重写配置的方法和返回对象 @Lookup("printer") protected abstract Printer createPrinter(); }
注意:由于采用了 CGLIB 生成子类方式,因此需要用来动态注入的类不能是 final 修饰的;需要动态注入的方法也不能是 final 修饰的。同时,还需要注意,Printer 类的 scope 配置需要时 prototype。
Application 类是一个典型的 Java Application 类,其中 main 方法就是应用执行的入口。下面示例中,AnnotationConfigApplicationContext 类是 Spring 上下文的一种实现,实现了基于 Java 配置类的加载,主要用于管理 Spring bean。Application 类上的 @ComponentScan 注解会自动扫描指定包下全部标有 @Component 的类,并自动注册为 bean,当然也包含 @Component 下的子注解 @Service、@Repository、@Controller 等。
代码如下:
package com.hxstrive.spring5.lookup_method; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; /** * lookup-method 注入示例 * @author hxstrive.com */ @ComponentScan public class LookupMethodApp { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(LookupMethodApp.class); PrinterManager printerManager = context.getBean(PrinterManager.class); printerManager.show(); printerManager.show(); printerManager.show(); } }
运行服务,输出如下:
com.hxstrive.spring5.lookup_method.Printer@5db45159 >> com.hxstrive.spring5.lookup_method.Printer com.hxstrive.spring5.lookup_method.Printer@6107227e >> com.hxstrive.spring5.lookup_method.Printer com.hxstrive.spring5.lookup_method.Printer@7c417213 >> com.hxstrive.spring5.lookup_method.Printer
从上面的输出可知,调用三次 PrinterManager 的 show() 方法创建了三个新的 Printer 对象,每次都创建了一个新的 Printer 实例。