Spring 基于java的容器配置


Spring新的java配置支持中的核心构件是@Configuration注解的类和@Bean注解的方法。
@Bean注解用于指示方法实例化、配置和初始化由Spring IoC容器管理的新对象。对于那些熟悉Spring的<beans/> XML配置的人来说,@Bean注解的作用与<bean/>元素相同。你可以对任何Spring @Component使用@Bean注解的方法,但是,它们最常与@Configuration bean一起使用
用@Configuration注解类表明其主要目的是作为bean定义的来源。此外,@Configuration类允许通过简单地调用同一个类中的其他@Bean方法来定义bean之间的依赖关系。最简单的@Configuration类应该是这样的:

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }

}

上面的AppConfig类等价于下面的Spring < beans/> XML:

<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

注意:完全@Configuration和“lite”@Beans模式?

当@Bean方法在没有使用@Configuration注解的类中声明时,它们被称为在“lite”模式下处理。例如,在@Component或普通旧类中声明的bean方法将被认为是“lite”。
与完整的@Configuration不同,lite @Bean方法不能轻松声明bean之间的依赖关系。通常,在“lite”模式下操作时,一个@Bean方法不应该调用另一个@Bean方法。

只在@Configuration类中使用@Bean方法是确保总是使用“full”模式的推荐方法。这将防止相同的@Bean方法意外地被多次调用,并有助于减少在“lite”模式下操作时难以跟踪的细微bug。

下面几节将深入讨论@Bean和@Configuration注解。但是,首先,我们将介绍使用基于java的配置创建spring容器的各种方法。

3.12.2 使用AnnotationConfigApplicationContext实例化Spring容器

文档Spring的AnnotationConfigApplicationContext是Spring 3.0中新增的。这个通用的ApplicationContext实现不仅可以接受@Configuration类作为输入,还可以接受普通的@Component类和用JSR-330元数据注解的类。
当@Configuration类作为输入提供时,@Configuration类本身注册为bean定义,类中所有声明的@Bean方法也注册为bean定义。
当提供@Component和JSR-330类时,它们被注册为bean定义,并且假设DI元数据(如@Autowired或@Inject)在这些类中使用。

结构简单

与在实例化ClassPathXmlApplicationContext时将Spring XML文件用作输入非常相似,@Configuration类可以在实例化AnnotationConfigApplicationContext时用作输入。这允许完全不使用xml的Spring容器:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

如上所述,AnnotationConfigApplicationContext不仅限于使用@Configuration类。任何@Component或JSR-330注解类都可以作为输入提供给构造函数。例如:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

上面假设MyServiceImpl、Dependency1和Dependency2使用Spring dependency注入注解,比如@Autowired。

使用register(Class<?>…)以编程方式构建容器

可以使用无参构造函数实例化AnnotationConfigApplicationContext,然后使用register()方法进行配置。这种方法在以编程方式构建AnnotationConfigApplicationContext时特别有用。

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(AppConfig.class, OtherConfig.class);
    ctx.register(AdditionalConfig.class);
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}
启用扫描组件scan(String…)

要启用组件扫描,只需将@Configuration类注解如下:

@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig  {
    ...
}

注意:有经验的Spring用户将熟悉与Spring上下文等价的XML声明:namespace

<beans>
    <context:component-scan base-package="com.acme"/>
</beans>

在上面的例子中,com.acme包将被扫描,寻找任何@Component注解的类,这些类将在容器中注册为Spring bean定义。AnnotationConfigApplicationContext公开了scan(String…)方法,允许使用相同的组件扫描功能:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.scan("com.acme");
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
}

注意: 请记住@Configuration类是用@Component进行元注解的,所以它们是组件扫描的候选对象!在上面的例子中,假设AppConfig是在com.acme包中声明的(或下面的任何包),它将在调用scan()期间被获取,在refresh()时,它的所有@Bean方法将被处理并作为容器中的bean定义注册。

支持带有AnnotationConfigWebApplicationContext的web应用程序

AnnotationConfigApplicationContext的WebApplicationContext变体可以与AnnotationConfigWebApplicationContext一起使用。此实现可用于配置Spring ContextLoaderListener servlet侦听器、Spring MVC DispatcherServlet等。下面是配置典型Spring MVC web应用程序的web.xml片段。注意context-param和init-param的使用:

<web-app>
    <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
        instead of the default XmlWebApplicationContext -->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>

    <!-- Configuration locations must consist of one or more comma- or space-delimited
        fully-qualified @Configuration classes. Fully-qualified packages may also be
        specified for component-scanning -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.acme.AppConfig</param-value>
    </context-param>

    <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Declare a Spring MVC DispatcherServlet as usual -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
            instead of the default XmlWebApplicationContext -->
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </init-param>
        <!-- Again, config locations must consist of one or more comma- or space-delimited
            and fully-qualified @Configuration classes -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.acme.web.MvcConfig</param-value>
        </init-param>
    </servlet>

    <!-- map all requests for /app/* to the dispatcher servlet -->
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>
</web-app>
3.12.3 使用@Bean注解

@Bean是方法级别的注解,是XML < bean/>元素的直接模拟。注解支持< bean/>提供的一些属性,比如:init-method、destroy- method、自动装配和name。
你可以在@Configuration注解的类或@Component注解的类中使用@Bean注解。

声明一个bean

要声明一个bean,只需使用@Bean注解注解一个方法。你可以使用此方法在作为方法返回值指定的类型的ApplicationContext中注册bean定义。默认情况下,bean名称将与方法名称相同。下面是一个@Bean方法声明的简单例子:

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }

}

前面的配置与下面的Spring XML完全等价:

<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

这两种声明都使名为transferService的bean在ApplicationContext中可用,绑定到类型为TransferServiceImpl的对象实例:

transferService -> com.acme.TransferServiceImpl
Bean的依赖关系

带@Bean注解的方法可以有任意数量的参数来描述构建该bean所需的依赖关系。例如,如果我们的TransferService需要一个AccountRepository,我们可以通过一个方法参数来实现依赖关系:

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }

}

解析机制与基于构造函数的依赖项注入非常相似,有关详细信息,请参阅相关部分。

接受生命周期回调

使用@Bean注解定义的任何类都支持常规的生命周期回调,并且可以使用JSR-250中的@PostConstruct和@PreDestroy注解,请参阅JSR-250注解了解更多细节。
还完全支持常规的Spring生命周期回调。如果bean实现了InitializingBean、DisposableBean或Lifecycle,容器将调用它们各自的方法。
还完全支持标准的*Aware接口集,如BeanFactoryAware、BeanNameAware、MessageSourceAware、ApplicationContextAware等。
@Bean注解支持指定任意初始化和销毁回调方法,很像Spring XML在bean元素上的init-method和destroy-method属性:

public class Foo {
    public void init() {
        // initialization logic
    }
}

public class Bar {
    public void cleanup() {
        // destruction logic
    }
}

@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public Foo foo() {
        return new Foo();
    }

    @Bean(destroyMethod = "cleanup")
    public Bar bar() {
        return new Bar();
    }

}

注意:默认情况下,使用具有公共close或shutdown方法的Java配置定义的bean会被自动征募到一个销毁回调函数中。如果你有一个公共close或shutdown方法,并且你不希望在容器关闭时调用它,那么只需将@Bean(destroyMethod="")添加到bean定义中,以禁用默认(推断)模式。

默认情况下,你可能希望对通过JNDI获得的资源这样做,因为它的生命周期是在应用程序外部管理的。特别是,请确保始终为DataSource执行此操作,因为在Java EE应用程序服务器上,数据源是有问题的。

@Bean(destroyMethod="")
public DataSource dataSource() throws NamingException {
    return (DataSource) jndiTemplate.lookup("MyDS");
}

@Bean方法,你通常会选择使用程序化的JNDI查找:直接使用Spring的JndiTemplate / JndiLocatorDelegate助手或者使用JNDI InitialContext,但不是JndiObjectFactoryBean变体,这将迫使你声明返回类型作为FactoryBean类型,而不是实际的目标类型,使其难以在其他@ bean中使用交叉引用调用方法,准备在方法内指向提供的资源。

当然,对于上面的Foo,在构造过程中直接调用init()方法同样有效:

@Configuration
public class AppConfig {
    @Bean
    public Foo foo() {
        Foo foo = new Foo();
        foo.init();
        return foo;
    }

    // ...

}

注意:当你直接在Java中工作时,你可以对对象做任何你喜欢的事情,而不总是需要依赖容器生命周期!

指定bean范围
使用@Scope注解

你可以指定使用@Bean注解定义的bean应该具有特定的范围。你可以使用Bean作用域部分中指定的任何标准作用域。
默认的作用域是单例的,但是你可以用@Scope注解覆盖它:

@Configuration
public class MyConfiguration {

    @Bean
    @Scope("prototype")
    public Encryptor encryptor() {
        // ...
    }

}
@Scope和作用域内的代理

Spring提供了一种通过作用域代理处理作用域依赖项的方便方法。使用XML配置时创建这样一个代理的最简单方法是< aop:scoped- proxy/>元素。使用@Scope注解在Java中配置bean可以提供与proxyMode属性相同的支持。默认值是no proxy (ScopedProxyMode. No),但是你可以指定ScopedProxyMode.TARGET_CLASS或ScopedProxyMode.INTERFACES。
如果你使用Java将XML参考文档(请参阅前面的链接)中的作用域代理示例移植到我们的@Bean,它将看起来如下:

// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope
public UserPreferences userPreferences() {
    return new UserPreferences();
}

@Bean
public Service userService() {
    UserService service = new SimpleUserService();
    // a reference to the proxied userPreferences bean
    service.setUserPreferences(userPreferences());
    return service;
}
定制bean命名

默认情况下,配置类使用@Bean方法的名称作为结果bean的名称。但是,可以使用name属性覆盖此功能。

@Configuration
public class AppConfig {

    @Bean(name = "myFoo")
    public Foo foo() {
        return new Foo();
    }

}
Bean的别名

正如在3.3.1节“命名bean”中所讨论的,有时需要为单个bean指定多个名称,也称为bean别名。@Bean注解的name属性接受用于此目的的字符串数组。

@Configuration
public class AppConfig {

    @Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })
    public DataSource dataSource() {
        // instantiate, configure and return DataSource bean...
    }

}
Bean的描述

有时候,提供bean更详细的文本描述是有帮助的。当出于监视目的公开bean(可能通过JMX)时,这一点特别有用。
要向@Bean添加描述,可以使用@Description注解:

@Configuration
public class AppConfig {

    @Bean
    @Description("Provides a basic example of a bean")
    public Foo foo() {
        return new Foo();
    }

}
3.12.4 使用@Configuration注解

@Configuration是一个类级注解,指示对象是bean定义的源。@Configuration类通过公共的@Bean注解方法声明bean。在@Configuration类上调用@Bean方法也可以用来定义bean之间的依赖关系。有关一般介绍,请参见第3.12.1节“基本概念:@Bean和@Configuration”。

注入bean间的依赖关系

当@ bean相互依赖时,表示这种依赖就像让一个bean方法调用另一个bean方法一样简单:

@Configuration
public class AppConfig {

    @Bean
    public Foo foo() {
        return new Foo(bar());
    }

    @Bean
    public Bar bar() {
        return new Bar();
    }

}

在上面的例子中,foo bean通过构造函数注入接收对bar的引用。

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

查找方法注入

如前所述,查找方法注入是一个高级特性,应该很少使用。当单作用域bean依赖于原型作用域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- configuration支持,你可以创建CommandManager的子类,其中抽象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();
        }
    }
}
关于基于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()和一次clientDao()。因为这个方法创建了一个新的ClientDaoImpl实例并返回它,所以你通常会期望有两个实例(每个服务一个)。这肯定会有问题:在Spring中,实例化的bean默认有一个单例范围。这就是神奇之处:所有@Configuration类都是在启动时用CGLIB子类化的。在子类中,子方法在调用父方法并创建新实例之前,首先检查容器是否有缓存的bean(作用域)。注意,从Spring 3.2开始,不再需要将CGLIB添加到类路径中,因为CGLIB类已经在org.springframework.cglib下重新打包,并直接包含在spring- core JAR中。

注意:根据bean的范围,行为可能有所不同。我们这里说的是单例。

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

3.12.5 编写基于java的配置
使用@Import注解

正如元素在Spring XML文件中用于帮助模块化配置一样,@Import注解允许从另一个配置类加载@Bean定义:

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

}

现在,在实例化上下文时不需要同时指定configA.class和configB.class,只需要显式地提供ConfigB:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);

    // now both beans A and B will be available...
    A a = ctx.getBean(A.class);
    B b = ctx.getBean(B.class);
}

这种方法简化了容器实例化,因为只需要处理一个类,而不需要开发人员在构建过程中记住大量的@Configuration类。

注意:从Spring Framework 4.2开始,@Import还支持对常规组件类的引用,类似于AnnotationConfigApplicationContext.register方法。如果你想避免组件扫描,使用几个配置类作为显式定义所有组件的入口点,这将特别有用。

在导入的@Bean定义上注入依赖项

上面的例子有效,但过于简单。在大多数实际场景中,bean将在配置类之间相互依赖。当使用XML时,这本身不是问题,因为不涉及编译器,你可以简单地声明ref=“someBean”,并相信Spring会在容器初始化期间解决它。当然,当使用@Configuration类时,Java编译器会对配置模型施加约束,因为对其他bean的引用必须是有效的Java语法。
幸运的是,解决这个问题很简单。正如我们已经讨论过的,@Bean方法可以有任意数量的参数来描述bean依赖关系。让我们考虑一个更真实的场景,其中有几个@Configuration类,每个类都依赖于其他类中声明的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");
}

还有另一种方法可以达到同样的效果。请记住@Configuration类最终只是容器中的另一个bean:这意味着它们可以像其他bean一样利用@Autowired和@Value注入等等!

注意:确保以这种方式注入的依赖关系只是最简单的那种。@Configuration类在上下文初始化过程中很早就被处理,强制以这种方式注入依赖项可能会导致意外的早期初始化。只要可能,就像上面的例子一样,使用基于参数的注入。
注意: 另外,要特别注意通过@Bean定义的BeanPostProcessor和BeanFactoryPostProcessor。这些方法通常应该声明为静态@Bean方法,而不是触发其包含的配置类的实例化。否则,@Autowired和@Value将无法处理配置类本身,因为它作为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");
}

注意:从Spring Framework 4.3开始,只支持@Configuration类中的构造函数注入。还请注意,如果目标bean只定义一个构造函数,则不需要指定@Autowired;在上面的示例中,在RepositoryConfig构造函数上不需要@Autowired。

在上面的场景中,使用@Autowired可以很好地工作,并提供了所需的模块性,但是准确地确定在哪里声明了autowired bean定义仍然有些模糊。例如,作为一个查看ServiceConfig的开发人员,你如何确切地知道@Autowired AccountRepository bean声明在哪里?它在代码中没有显式显示,这可能很好。请记住,Spring Tool Suite提供了一些工具,可以呈现显示如何连接所有内容的图表——这可能就是你所需要的全部内容。而且,你的Java IDE可以轻松地找到AccountRepository类型的所有声明和使用,并将快速显示返回该类型的@Bean方法的位置。
如果这种模糊性是不可接受的,并且你希望在IDE中从一个@Configuration类直接导航到另一个@Configuration类,请考虑自动装配配置类本身:

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

}

在上述情况下,AccountRepository的定义是完全明确的。然而,ServiceConfig现在与RepositoryConfig紧密耦合;这是一种折衷。通过使用基于接口或基于抽象类的@Configuration类,可以在一定程度上缓解这种紧密耦合。考虑以下:

@Configuration
public class ServiceConfig {

    @Autowired
    private RepositoryConfig repositoryConfig;

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(repositoryConfig.accountRepository());
    }
}

@Configuration
public interface RepositoryConfig {

    @Bean
    AccountRepository accountRepository();

}

@Configuration
public class DefaultRepositoryConfig implements RepositoryConfig {

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(...);
    }

}

@Configuration
@Import({ServiceConfig.class, DefaultRepositoryConfig.class}) // import the concrete config!
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return DataSource
    }

}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

现在ServiceConfig与具体的DefaultRepositoryConfig松散耦合,内置IDE工具仍然很有用:开发人员可以很容易地获得RepositoryConfig实现的类型层次结构。通过这种方式,导航@Configuration类及其依赖项与导航基于接口的代码的通常过程没有什么不同。

有条件地包含@Configuration类或@Bean方法

根据某些任意的系统状态,有条件地启用或禁用一个完整的@Configuration类,甚至单个的@Bean方法,这通常很有用。一个常见的例子是,只有在Spring环境中启用了特定的概要文件时,才使用@Profile注解来激活Bean(有关详细信息,请参阅3.13.1节“Bean定义概要文件”)。
@Profile注解实际上是使用一个灵活得多的名为@Conditional的注解实现的。@Conditional注解指示特定的org.springframework.context.annotation.Conditions实现, 注册@Bean之前应该考虑的条件。
Condition接口的实现只提供一个返回true或false的matches(…)方法。例如,下面是@Profile使用的实际Condition实现:

@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;
}

有关更多细节,请参见@Conditional javadocs。

结合Java和XML配置

Spring的@Configuration类支持的目标不是100%完全替代Spring XML。一些工具,如Spring XML名称空间,仍然是配置容器的理想方法。在XML方便或必要的情况下,你可以选择:要么以“以XML为中心”的方式实例化容器,例如使用ClassPathXmlApplicationContext,要么以“以java为中心”的方式实例化容器,根据需要使用AnnotationConfigApplicationContext和@ImportResource注解导入XML。

以xml为中心使用@Configuration类

最好从XML引导Spring容器,并以特别的方式包含@Configuration类。例如,在使用Spring XML的大型现有代码基中,根据需要创建@Configuration类并从现有XML文件中包含它们将更加容易。下面你将找到在这种“以xml为中心”的情况下使用@Configuration类的选项。
请记住,@Configuration类最终只是容器中的bean定义。在本例中,我们创建了一个名为AppConfig的@Configuration类,并将其作为< bean/>定义包含在system-test-config.xml中。因为打开了< context:annotation- config/>,容器将识别@Configuration注解并正确处理AppConfig中声明的@Bean方法。

@Configuration
public class AppConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }

    @Bean
    public TransferService transferService() {
        return new TransferService(accountRepository());
    }

}

system-test-config.xml:

<beans>
    <!-- enable processing of annotations such as @Autowired and @Configuration -->
    <context:annotation-config/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean class="com.acme.AppConfig"/>

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

jdbc.properties:

jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=



public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}

注意:在上面的system-test-config.xml中,AppConfig < bean/>没有声明id元素。虽然这样做是可以接受的,但是没有必要这样做,因为没有其他bean会引用它,而且不太可能通过名称显式地从容器中获取它。同样,对于DataSource bean——它只由类型自动生成,因此并不严格要求显式bean id。

因为@Configuration使用@Component进行元注解,所以@Configuration注解的类自动成为组件扫描的候选类。使用与上面相同的场景,我们可以重新定义system- test-config.xml,以利用组件扫描。注意,在本例中,我们不需要显式地声明< context:annotation-config/>,因为 context:component-scan/启用了相同的功能。
system-test-config.xml:

<beans>
    <!-- picks up and registers AppConfig as a bean definition -->
    <context:component-scan base-package="com.acme"/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>
以@Configuration类为中心使用XML和@ImportResource

在@Configuration类是配置容器的主要机制的应用程序中,仍然可能需要使用至少一些XML。在这些场景中,只需使用@ImportResource并只定义所需的XML。这样做实现了一种“以java为中心”的方法来配置容器,并将XML保持在最低限度。

@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }

}



properties-config.xml
<beans>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>



jdbc.properties
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=



public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}


原文链接:https://blog.csdn.net/nobody_1/article/details/89346517