目录
基于Java的容器配置
4.使用@Configuration注解
5.组合基于java的配置
翻译源:Spring官方文档
基于Java的容器配置
4.使用@Configuration注解
@Configuration是一个类层次的注解,用于指示作为bean定义源的对象。
@Configuration类通过public@Bean方法声明beans。
调用@Configuration类中的@Bean方法可以用于定义内部bean依赖。
注入内部依赖
@Configuration
public class AppConfig {
@Bean
public Foo foo() {
return new Foo(bar());
}
@Bean
public Bar bar() {
return new Bar();
}
}
note:名为foo的bean通过构造器注入了一个bar的bean。
ps:只有当@Bean方法声明在@Configuration类中时,通过方法调用声明内部bean依赖的方式才会有效。
查阅方法注入
方法注入是一种很少使用的高级特性。
这个功能主要用于讲一个prototype范围的bean作为依赖注入一个singleton范围的bean之中。
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 command() overridden
// to return a new prototype Command object
return new CommandManager() {
protected Command createCommand() {
return asyncCommand();
}
}
}
note:Singleton范围的CommandManager在每一次运行中使用方法createCommand()获取一个新的AsynCommand对象。
关于基于java的配置的内部运作的信息
@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();
}
}
note:clientDao作为方法被调用了两次,分别注入clientService1与clientService2,但是实际上只产生了一个ClientDao实例。
ps:所有的@Configuration类在启动时都已经被CGLIB创建为了子类。
ps:CGLIB生成的@Configuration的孩子方法,会在调用双亲方法并创建一个实例之前,检查是否有缓存的beans。
ps:以上只是Singleton的逻辑。
ps:@Component不会被CGLIB动态生成子类,即如果将@Bean方法声明在@Component之中,@Bean方法之间的互相调用就不会被拦截。
5.组合基于java的配置
使用@Import标签
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
note:ApplicationContext只需要显式指定@Configuration类ConfigB,无需再将ConfigA显式传入。
ps:@Import用于从其他@Configuration类出入@Bean方法。
ps:Spring框架4.2之后,@Import便支持对常规组件地引用,类似于AnnotationConfigApplicationContext.register方法。
注入引入的@Bean定义上的依赖
@Configuration
public class ServiceConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration
public class RepositoryConfig {
@Bean
public AccountRepository accountRepository(DataSource dataSource) {
return new JdbcAccountRepository(dataSource);
}
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return new DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
// everything wires up across configuration classes...
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
ps:可以通过@Bean方法的参数注入直接注入其他@Configuration中定义的bean。
@Configuration
public class ServiceConfig {
@Autowired
private AccountRepository accountRepository;
@Bean
public TransferService transferService() {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration
public class RepositoryConfig {
private final DataSource dataSource;
@Autowired
public RepositoryConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return new DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
// everything wires up across configuration classes...
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
ps:可以使用构造器注入其他@Configuration类中的bean依赖。
ps:当只有一个构造器时,被注入的属性可以不设置@Autowired注解。
为了定位bean所在的@Configuration类,使用完整修饰引入bean
@Configuration
public class ServiceConfig {
@Autowired
private RepositoryConfig repositoryConfig;
@Bean
public TransferService transferService() {
// navigate 'through' the config class to the @Bean method!
return new TransferServiceImpl(repositoryConfig.accountRepository());
}
}
ps:可以将RepositoryConfig设置为接口,用于解耦和。
附有条件地包含@Configuration类与@Bean方法
基于一些系统状态允许或禁止使用完整@Configuration或部分@Bean方法经常非常有用。
一个常见的实现是利用@Profile注解,使得只有当特定的profile存在于Spring Environment时,这些beans被激活。
@Profile注解可以使用@Conditional注解实现。
@Conditional注解指定特定的org.springframework.context.annotation.Conditaion实现,被指定的实现可以@Bean被注册之前被调用。
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() != null) {
// Read the @Profile annotation attributes
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
return true;
}
}
return false;
}
}
return true;
}
note:使用@Profile的Conditional实现。
结合Java与XML配置
Spring的@Configuration类支持并不以完全替代xml配置为目标。
当一些xml配置更为便利与必要时,需要考虑使用"XML-centric"途径实例化容器(使用ClassPathXmlApplicationContext),或者是"Java-centric"途径实例化容器(使用AnnotationConfigApplicationContext并且使用@ImportResource注解引入必要的xml配置)。
使用@Configuration类的XML-centric
@Configuration
public class AppConfig {
@Autowired
private Foo foo;
@Bean
public Example example() {
return new Example(foo);
}
}
<beans>
<context:annotation-config/>
<bean class="examples.AppConfig"/>
<bean class="examples.Foo"/>
</beans>
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/examples/spring.xml");
}
note:@Configuration类AppConfig直接作为一个bean定义在xml文件中即可。
@Configuration
public class AppConfig {
@Autowired
private Foo foo;
@Bean
public Example example() {
return new Example(foo);
}
}
<beans>
<!-- picks up and registers AppConfig as a bean definition -->
<context:component-scan base-package="com.examples"/>
</beans>
note:利用组件扫描直接获取AppConfig类。
通过@ImportResource使用XML的@Configuration class-centric
@Configuration
@ImportResource("classpath:/examples/springConfig.xml")
public class AppConfig {
@Autowired
Foo foo;
}
<beans>
<bean class="examples.Foo "/>
</beans>
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
}