Spring新的java配置支持中的核心构件是@Configuration注解的类和@Bean注解的方法。 @Bean注解用于指示方法实例化、配置和初始化由Spring IoC容器管理的新对象。对于那些熟悉Spring的<beans/> XML配置的人来说,@Bean注解的作用与<bean/>元素相同。你可以对任何Spring @Component使用@Bean注解的方法,但是,它们最常与@Configuration bean一起使用 用@Configuration注解类表明其主要目的是作为bean定义的来源。此外,@Configuration类允许通过简单地调用同一个类中的其他@Bean方法来定义bean之间的依赖关系。最简单的@Configuration类应该是这样的:
<beans/>
<bean/>
@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。
注意:完全@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容器的各种方法。
文档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。
可以使用无参构造函数实例化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(); }
要启用组件扫描,只需将@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定义注册。
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>
@Bean是方法级别的注解,是XML < bean/>元素的直接模拟。注解支持< bean/>提供的一些属性,比如:init-method、destroy- method、自动装配和name。 你可以在@Configuration注解的类或@Component注解的类中使用@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所需的依赖关系。例如,如果我们的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应用程序服务器上,数据源是有问题的。
注意:默认情况下,使用具有公共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注解定义的bean应该具有特定的范围。你可以使用Bean作用域部分中指定的任何标准作用域。 默认的作用域是单例的,但是你可以用@Scope注解覆盖它:
@Configuration public class MyConfiguration { @Bean @Scope("prototype") public Encryptor encryptor() { // ... } }
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的名称。但是,可以使用name属性覆盖此功能。
@Configuration public class AppConfig { @Bean(name = "myFoo") public Foo foo() { return new Foo(); } }
正如在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(可能通过JMX)时,这一点特别有用。 要向@Bean添加描述,可以使用@Description注解:
@Configuration public class AppConfig { @Bean @Description("Provides a basic example of a bean") public Foo foo() { return new Foo(); } }
@Configuration是一个类级注解,指示对象是bean定义的源。@Configuration类通过公共的@Bean注解方法声明bean。在@Configuration类上调用@Bean方法也可以用来定义bean之间的依赖关系。有关一般介绍,请参见第3.12.1节“基本概念:@Bean和@Configuration”。
当@ 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(); } } }
下面的例子显示了一个@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方法之间的交叉方法调用不会被拦截,因此你必须完全依赖于构造函数或方法级别的依赖注入。
注意:根据bean的范围,行为可能有所不同。我们这里说的是单例。
注意:由于CGLIB在启动时动态添加特性,特别是配置类不能是最终的,所以存在一些限制。但是,从4.3开始,配置类上允许使用任何构造函数,包括使用@Autowired或单个非默认构造函数声明进行默认注入。如果你希望避免cglib强加的任何限制,请考虑在非-@Configuration类上声明@Bean方法,例如在普通的@Component类上声明@Bean方法。@Bean方法之间的交叉方法调用不会被拦截,因此你必须完全依赖于构造函数或方法级别的依赖注入。
正如元素在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将在配置类之间相互依赖。当使用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方法,这通常很有用。一个常见的例子是,只有在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。
Spring的@Configuration类支持的目标不是100%完全替代Spring XML。一些工具,如Spring XML名称空间,仍然是配置容器的理想方法。在XML方便或必要的情况下,你可以选择:要么以“以XML为中心”的方式实例化容器,例如使用ClassPathXmlApplicationContext,要么以“以java为中心”的方式实例化容器,根据需要使用AnnotationConfigApplicationContext和@ImportResource注解导入XML。
最好从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并只定义所需的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