代码
网页地址 @Configuration的查找方法注入
其中的代码如下:
public abstract class CommandManager {
public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
// okay... but where is the implementation of this method?
protected abstract Command createCommand();
}
@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
AsyncCommand command = new AsyncCommand();
// inject dependencies here as required
return command;
}
@Bean
public CommandManager commandManager() {
// return new anonymous implementation of CommandManager with createCommand()
// overridden to return a new prototype Command object
return new CommandManager() {
protected Command createCommand() {
return asyncCommand();
}
}
}
这段代码的目的
定义一个抽象类 CommandManager,每次调用 process(Object commandState) 方法时,需要动态创建一个新的 Command 实例,并调用其操作方法。
使用 Spring 的 @Bean 和 @Scope 注解,将 Command Bean 定义为 原型作用域(prototype),从而保证每次调用时都会创建一个新的 Command 实例。
在 CommandManager 的实现中,通过重写 createCommand() 方法,以编程方式动态获取原型作用域的 Bean。
实际场景
可以根据此举一个实际例子:
1、基础配置
// Command 接口
public interface Command {
void setState(Object state);
Object execute();
}
// AsyncCommand 实现类
public class AsyncCommand implements Command {
private Object state;
@Override
public void setState(Object state) {
this.state = state;
}
@Override
public Object execute() {
// 模拟执行逻辑,比如处理异步任务
System.out.println("Processing state: " + state);
return "Result for state: " + state;
}
}
// CommandManager 抽象类
public abstract class CommandManager {
public Object process(Object commandState) {
// 每次调用时创建新的 Command 实例
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
protected abstract Command createCommand(); // 工厂方法,子类实现
}
2、场景实现
// 任务处理服务
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class TaskProcessingService {
@Autowired
private CommandManager commandManager; // 注入 CommandManager Bean
// 处理任务
public void processTasks() {
// 模拟处理多个任务
Object result1 = commandManager.process("Task1");
System.out.println(result1);
Object result2 = commandManager.process("Task2");
System.out.println(result2);
}
}
// Spring 启动类
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
// 初始化 Spring 容器
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 获取 TaskProcessingService Bean
TaskProcessingService service = context.getBean(TaskProcessingService.class);
// 调用任务处理方法
service.processTasks();
}
}
// Spring 配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
return new AsyncCommand();
}
@Bean
public CommandManager commandManager() {
return new CommandManager() {
@Override
protected Command createCommand() {
return asyncCommand();
}
};
}
}
当运行 MainApp 时,输出如下:
Processing state: Task1
Result for state: Task1
Processing state: Task2
Result for state: Task2
解析
在 Spring 中,CommandManager
的实例是通过 @Bean
方法(例如 public CommandManager commandManager()
)定义的,因此 @Bean
方法的返回值是 Spring 容器中管理的一个 Bean 实例。当我们在使用 @Autowired
注解注入 CommandManager
时,Spring 会自动将容器中对应的 Bean(由 commandManager()
方法创建)注入到目标对象中。
1. 为什么 commandManager()
方法用于创建 CommandManager
实例?
在 Java 代码中,commandManager()
方法返回了一个匿名实现的 CommandManager
对象,这个方法使用了 Spring 的 @Bean
注解。Spring 的 @Bean
注解告诉 Spring 容器,commandManager()
方法的返回值应该被注册为一个 Bean,并在容器中进行管理。
关键代码
@Bean
public CommandManager commandManager() {
return new CommandManager() {
@Override
protected Command createCommand() {
return asyncCommand(); // 调用原型作用域的 Command 实例
}
};
}
-
commandManager()
方法:- 返回一个匿名实现的
CommandManager
对象。 - 使用
@Bean
注解告诉 Spring,这个返回的对象应该作为容器中的一个 Bean。
- 返回一个匿名实现的
-
Spring 如何处理:
- Spring 容器会在启动时调用
commandManager()
方法,获取返回的对象,并将其注册为 Bean。 - 这个 Bean 的默认作用域是
singleton
(单例),所以在容器中只有一个CommandManager
实例。 - 在使用
@Autowired
注入时,Spring 会将这个单例的CommandManager
注入到其他需要的对象中。
- Spring 容器会在启动时调用
2. 为什么 private CommandManager commandManager
指向了 commandManager()
方法创建的 Bean?
在 Spring 中,@Autowired
注解用于自动注入依赖。Spring 会根据类型找到容器中对应的 Bean 并将其注入到目标对象中。
关键代码
@Autowired
private CommandManager commandManager;
-
依赖注入的逻辑:
- Spring 容器中存在一个名为
commandManager
的 Bean(由commandManager()
方法创建)。 - 当 Spring 看到类中声明了
@Autowired
的private CommandManager commandManager;
时,Spring 会根据类型CommandManager
去查找容器中的 Bean。 - 找到
commandManager()
方法对应的 Bean 后,Spring 将该 Bean 注入到TaskProcessingService
的commandManager
属性中。
- Spring 容器中存在一个名为
-
结果:
private CommandManager commandManager
直接引用了commandManager()
方法返回的CommandManager
实例(即匿名类的实例)。
3. Spring 是如何利用 commandManager()
方法创建实例的?
Spring 在启动时,会扫描配置类(比如 @Configuration
标注的类)中的 @Bean
方法,这些方法的返回值会被注册为 Spring 容器中的 Bean。Spring 会根据需要调用这些方法来创建 Bean 实例。
流程拆解
-
配置类扫描:
- Spring 容器在启动时会扫描
AppConfig
等配置类,并找到标注了@Bean
的方法。 - 对于
commandManager()
方法,Spring 发现其返回类型是CommandManager
,并将其注册为一个名为commandManager
的 Bean。
- Spring 容器在启动时会扫描
-
实例化 Bean:
- Spring 容器会调用
commandManager()
方法,将其返回的对象(匿名类实例)保存为commandManager
Bean。 - 如果没有特殊指定作用域(如
@Scope("prototype")
),默认情况下commandManager
Bean 是单例的。
- Spring 容器会调用
-
依赖注入:
- 当 Spring 容器处理中发现某个类的字段(如
TaskProcessingService
的commandManager
)标注了@Autowired
,并且类型是CommandManager
,Spring 将自动注入容器中注册的类型匹配的 Bean。
- 当 Spring 容器处理中发现某个类的字段(如
4. 为什么 CommandManager
是接口或抽象类,但 commandManager()
方法可以注入?
在 Java 中,不能直接实例化接口或抽象类,但可以通过 匿名类 或 子类 来实现它们。在 commandManager()
方法中,我们通过匿名类实现了 CommandManager
的抽象方法,因此返回的是一个合法的 CommandManager
实例。
关键点
return new CommandManager() {
@Override
protected Command createCommand() {
return asyncCommand();
}
};
- 在
commandManager()
方法中,new CommandManager() { ... }
创建了一个匿名实现的CommandManager
对象。 - 匿名类实现了
CommandManager
的createCommand()
方法,所以它是一个合法的CommandManager
实例。
5. 示例完整工作流程
现在我们结合完整的代码,梳理运行时的具体流程:
(1) Bean 创建阶段
- Spring 容器启动时,扫描
AppConfig
。 - Spring 调用
commandManager()
方法,获得一个匿名实现的CommandManager
实例。 - Spring 将这个匿名类实例注册为
commandManager
Bean。
(2) 依赖注入阶段
- 容器扫描到
TaskProcessingService
中的@Autowired CommandManager commandManager;
。 - 容器查找
CommandManager
类型的 Bean,找到commandManager
。 - 将
commandManager
Bean 注入到TaskProcessingService
中。
(3) 运行阶段
- 调用
TaskProcessingService.processTasks()
。- 该方法调用了
commandManager.process()
。
- 该方法调用了
commandManager.process()
方法内部调用createCommand()
方法。createCommand()
调用了asyncCommand()
方法,返回一个新的AsyncCommand
实例。
commandManager.process()
设置任务状态并调用Command.execute()
执行任务。
6. 总结
-
commandManager()
方法创建了一个匿名实现类的实例:- 该实例是
CommandManager
的实现类。 - Spring 将这个实例注册为容器中的
commandManager
Bean。
- 该实例是
-
@Autowired
注解将容器中的 Bean 注入到目标对象:TaskProcessingService
中的commandManager
字段指向commandManager()
方法返回的实例。
-
匿名类的好处:
- 通过在
commandManager()
方法中使用匿名类,可以快速实现CommandManager
的抽象方法,无需额外定义子类。 - Spring 的 Bean 管理机制使得这种匿名类的实例可以被全局复用。
- 通过在