目录

基于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);
}