查找方法注入 官方代码解析(单例作用域bean依赖于原型作用域bean)

代码

网页地址 @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 注入到其他需要的对象中。

2. 为什么 private CommandManager commandManager 指向了 commandManager() 方法创建的 Bean?

在 Spring 中,@Autowired 注解用于自动注入依赖。Spring 会根据类型找到容器中对应的 Bean 并将其注入到目标对象中。

关键代码
@Autowired
private CommandManager commandManager;
  • 依赖注入的逻辑

    • Spring 容器中存在一个名为 commandManager 的 Bean(由 commandManager() 方法创建)。
    • 当 Spring 看到类中声明了 @Autowiredprivate CommandManager commandManager; 时,Spring 会根据类型 CommandManager 去查找容器中的 Bean。
    • 找到 commandManager() 方法对应的 Bean 后,Spring 将该 Bean 注入到 TaskProcessingServicecommandManager 属性中。
  • 结果

    • private CommandManager commandManager 直接引用了 commandManager() 方法返回的 CommandManager 实例(即匿名类的实例)。

3. Spring 是如何利用 commandManager() 方法创建实例的?

Spring 在启动时,会扫描配置类(比如 @Configuration 标注的类)中的 @Bean 方法,这些方法的返回值会被注册为 Spring 容器中的 Bean。Spring 会根据需要调用这些方法来创建 Bean 实例。

流程拆解
  1. 配置类扫描

    • Spring 容器在启动时会扫描 AppConfig 等配置类,并找到标注了 @Bean 的方法。
    • 对于 commandManager() 方法,Spring 发现其返回类型是 CommandManager,并将其注册为一个名为 commandManager 的 Bean。
  2. 实例化 Bean

    • Spring 容器会调用 commandManager() 方法,将其返回的对象(匿名类实例)保存为 commandManager Bean。
    • 如果没有特殊指定作用域(如 @Scope("prototype")),默认情况下 commandManager Bean 是单例的。
  3. 依赖注入

    • 当 Spring 容器处理中发现某个类的字段(如 TaskProcessingServicecommandManager)标注了 @Autowired,并且类型是 CommandManager,Spring 将自动注入容器中注册的类型匹配的 Bean。

4. 为什么 CommandManager 是接口或抽象类,但 commandManager() 方法可以注入?

在 Java 中,不能直接实例化接口或抽象类,但可以通过 匿名类子类 来实现它们。在 commandManager() 方法中,我们通过匿名类实现了 CommandManager 的抽象方法,因此返回的是一个合法的 CommandManager 实例。

关键点
return new CommandManager() {
    @Override
    protected Command createCommand() {
        return asyncCommand();
    }
};
  • commandManager() 方法中,new CommandManager() { ... } 创建了一个匿名实现的 CommandManager 对象。
  • 匿名类实现了 CommandManagercreateCommand() 方法,所以它是一个合法的 CommandManager 实例。

5. 示例完整工作流程

现在我们结合完整的代码,梳理运行时的具体流程:

(1) Bean 创建阶段
  1. Spring 容器启动时,扫描 AppConfig
  2. Spring 调用 commandManager() 方法,获得一个匿名实现的 CommandManager 实例。
  3. Spring 将这个匿名类实例注册为 commandManager Bean。
(2) 依赖注入阶段
  1. 容器扫描到 TaskProcessingService 中的 @Autowired CommandManager commandManager;
  2. 容器查找 CommandManager 类型的 Bean,找到 commandManager
  3. commandManager Bean 注入到 TaskProcessingService 中。
(3) 运行阶段
  1. 调用 TaskProcessingService.processTasks()
    • 该方法调用了 commandManager.process()
  2. commandManager.process() 方法内部调用 createCommand() 方法。
    • createCommand() 调用了 asyncCommand() 方法,返回一个新的 AsyncCommand 实例。
  3. commandManager.process() 设置任务状态并调用 Command.execute() 执行任务。

6. 总结

  1. commandManager() 方法创建了一个匿名实现类的实例

    • 该实例是 CommandManager 的实现类。
    • Spring 将这个实例注册为容器中的 commandManager Bean。
  2. @Autowired 注解将容器中的 Bean 注入到目标对象

    • TaskProcessingService 中的 commandManager 字段指向 commandManager() 方法返回的实例。
  3. 匿名类的好处

    • 通过在 commandManager() 方法中使用匿名类,可以快速实现 CommandManager 的抽象方法,无需额外定义子类。
    • Spring 的 Bean 管理机制使得这种匿名类的实例可以被全局复用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值