大多数的 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 实例。