Spring Framework Core - The IoC Container (12) - 基于 java 的容器配置(3) 使用@Configuration注解

12.4 使用@Configuration注解

@Configuration是一个类级注释,指示对象是bean定义的源。@Configuration类通过公共的@Bean注释方法声明bean。在@Configuration类上调用@Bean方法也可以用来定义bean之间的依赖关系。参见Basic Concepts: @Bean and @Configuration以获得一般介绍。

注入Inter-bean依赖性

当bean彼此之间有依赖关系时,表示这种依赖关系就像让一个bean方法调用另一个bean方法一样简单,如下面的示例所示:

@Configuration
public class AppConfig {

    @Bean
    public BeanOne beanOne() {
        return new BeanOne(beanTwo());
    }

    @Bean
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

在前面的示例中,beanOne通过构造函数注入接收对beanTwo的引用。

只有在@Configuration类中声明@Bean方法时,这种声明bean间依赖关系的方法才有效。您不能通过使用普通的@Component类来声明bean之间的依赖关系。

查找方法注入

如前所述,lookup method injection 是一个高级特性,您应该很少使用它。
它在singleton-scoped bean依赖于prototype-scoped bean的情况下非常有用。使用Java进行这种类型的配置为实现这种模式提供了一种自然的方法。下面的例子展示了如何使用查找方法注入:

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();
}

通过使用Java配置,您可以创建CommandManager的子类,其中以查找新的(prototype)命令对象的方式覆盖了抽象的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();
        }
    }
}

关于基于java的配置如何在内部工作的更多信息

考虑下面的示例,其中显示了两次调用@Bean注释的方法

@Configuration
public class AppConfig {

    @Bean
    public ClientService clientService1() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientService clientService2() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientDao clientDao() {
        return new ClientDaoImpl();
    }
}

在clientService1()和clientService2()分别调用一次clientDao()。因为这个方法创建了一个新的ClientDaoImpl实例并返回它,所以您通常会希望有两个实例(每个服务一个)。这肯定会有问题:在Spring中,实例化的bean默认有一个singleton范围。这就是神奇之处:所有@Configuration类都是在启动时用CGLIB子类化的。在子类中,子方法在调用父方法并创建新实例之前,首先检查容器是否有缓存的(scoped)bean。

根据bean的范围,行为可能会有所不同。我们这里说的是singleton。

从Spring 3.2开始,不再需要将CGLIB添加到类路径中,因为CGLIB类已经在org.springframework下重新打包了。cglib并直接包含在spring-core JAR中。

由于CGLIB在启动时动态添加特性,所以有一些限制。特别是,配置类不能是final。但是,从4.3开始,配置类上可以使用任何构造函数,包括使用@Autowired或单个非默认构造函数声明进行默认注入。
如果希望避免cglib强加的限制,可以考虑在非@Configuration 配置类上声明@Bean方法(例如,在普通的@Component类上声明)。然后不会拦截@Bean方法之间的交叉方法调用,因此必须完全依赖于构造函数或方法级别的依赖注入。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章