1.11。应用JSR 330规范注解
从Spring 3.0开始,Spring提供了对JSR-330规范注解(依赖注入)的反对。
这些注解的扫描形式与Spring注解雷同。
要应用它们,您须要在类门路中蕴含相干的jar。
如果你应用Maven, javax。 注入工件在规范Maven存储库中可用(https://repo1.maven.org/maven2/javax/inject/javax.inject/1/)。 你能够增加以下依赖到你的文件pom.xml:<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version></dependency>
1.11.1。@Inject和@Named
除了@Autowired,您能够应用@javax.inject.Inject注入:
import javax.inject.Inject;public class SimpleMovieLister { private MovieFinder movieFinder; @Inject public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } public void listMovies() { this.movieFinder.findMovies(...); // ... }}
与@Autowired一样,你能够在字段级、办法级和结构参数级应用@Inject。
此外,您能够将注入点申明为提供者,从而容许按需拜访作用域较短的bean,或者通过Provider.get()提早调用拜访其余bean。
以下示例提供了先前示例的变体:
import javax.inject.Inject;import javax.inject.Provider;public class SimpleMovieLister { private Provider<MovieFinder> movieFinder; @Inject public void setMovieFinder(Provider<MovieFinder> movieFinder) { this.movieFinder = movieFinder; } public void listMovies() { this.movieFinder.get().findMovies(...); // ... }}
如果要为应该注入的依赖项应用qualified名称,则应应用@Named批注,如以下示例所示:
import javax.inject.Inject;import javax.inject.Named;public class SimpleMovieLister { private MovieFinder movieFinder; @Inject public void setMovieFinder(@Named("main") MovieFinder movieFinder) { this.movieFinder = movieFinder; }}
与一样@Autowired,@Inject也能够与java.util.Optional或 一起应用@Nullable。
这在这里更为实用,因为@Inject它没有required属性。
以下示例展现了如何应用@Inject和 @Nullable:
public class SimpleMovieLister { @Inject public void setMovieFinder(Optional<MovieFinder> movieFinder) { // ... }}
public class SimpleMovieLister { @Inject public void setMovieFinder(@Nullable MovieFinder movieFinder) { // ... }}
1.11.2。@Named和@ManagedBean:
您能够应用@javax.inject.Named或@javax.annotation.ManagedBean代替@Component
如以下示例所示:
import javax.inject.Inject;import javax.inject.Named;@Named("movieListener") // @ManagedBean("movieListener") could be used as wellpublic class SimpleMovieLister { private MovieFinder movieFinder; @Inject public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ...}
在不指定Component名称的状况下应用@Component是很常见的。
@Named也能够以相似的形式应用,如上面的示例所示:
import javax.inject.Inject;import javax.inject.Named;@Namedpublic class SimpleMovieLister { private MovieFinder movieFinder; @Inject public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ...}
当应用@Named或时@ManagedBean,能够应用与应用Spring注解完全相同的形式来应用组件扫描,如以下示例所示:
@Configuration@ComponentScan(basePackages = "org.example")public class AppConfig { // ...}
与相比@Component,JSR-330的@Named和JSR-250的ManagedBean 注解是不可组合的。
1.11.3。JSR-330规范注解的局限性
当你应用规范注解时,你应该晓得一些重要的个性是不可用的,如下表所示:
Spring | javax.inject.* | javax.inject限度/解释 |
---|---|---|
@Autowired | @Inject | @Inject没有“required”属性。 能够与Java 8一起应用Optional |
@Component | @Named/@ManagedBean | JSR-330没有提供可组合模型, 只是提供了一种辨认已命名组件的办法。 就是说这俩注解仅仅是把类标识为bean |
@Scope("singleton") | @Singleton | JSR-330的默认作用域相似于Spring的prototype。 然而,为了放弃它与Spring的个别默认值统一, 在Spring容器中申明的JSR-330 bean在默认状况下是单例的。 为了应用除singleton之外的作用域, 您应该应用Spring的@Scope注解。 javax.inject包中还提供了一个@Scope注解。 不过,这个注解仅用于创立您本人的注解。 具体例子就看同层级包下的@Singleton注解就能够了 |
@Qualifier | @Qualifier / @Named | javax.inject.Qualifier只是用于构建自定义限定符的元注解。 具体String qualifiers (例如Spring的带有value的@Qualifier) 能够通过javax.inject.Named关联。 |
@Value | -- | no equivalent |
@Required | -- | no equivalent |
@Lazy | -- | no equivalent |
ObjectFactory | Provider | javax.inject.Provider是Spring的 间接代替办法ObjectFactory, 只是get()办法名称较短。 它也能够与Spring@Autowired或 非注解构造函数和setter办法联合应用。 |
1.12。基于Java的容器配置
本节介绍如何在Java代码中应用注解来配置Spring容器。它包含以下主题:
- 基本概念:@Bean和@Configuration
- 应用实例化Spring容器 AnnotationConfigApplicationContext
- 应用@Bean注解
- 应用@Configuration注解
- 组成基于Java的配置
- Bean定义配置文件
- PropertySource 抽象化
- 应用 @PropertySource
- 申明中的占位符解析
1.12.1。基本概念:@Bean和@Configuration
Spring的新Java配置反对中的次要组件是-带@Configuration注解的类和-带@Bean注解的办法。
@Bean注解用于批示办法实例化、配置和初始化的新对象并将由Spring IoC容器治理。
对于那些相熟Spring的<beans></beans>XML配置的人来说,@Bean注解扮演着与<bean></bean>元素雷同的角色。
您能够对任何Spring @Component应用@bean注解的办法。
然而,它们最常与@Configuration bean一起应用。
用@Configuration注解类表明它的主要用途是作为bean定义的源。
此外,@Configuration类通过调用同一类中的其余@Bean办法来定义bean间的依赖关系。
最简略的@Configuration类如下:
@Configurationpublic class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); }}
上一AppConfig类等效于以下Spring <beans/>XML:
<beans> <bean id="myService" class="com.acme.services.MyServiceImpl"/></beans>
Full @Configuration 与 “lite” @Bean?当@Bean办法在没有应用@Configuration注解的类中申明时,它们被称为在“lite”模式下解决。与 full 的@Configuration不同,lite@Bean办法不能申明bean之间的依赖关系。 Spring会失常实例化 lite @Bean,并执行依赖注入 然而独自调用这个@Bean办法的话,就仅仅是执行new 操作没有进行依赖注入在常见的场景中,@Bean办法将在@Configuration类中申明,以确保始终应用“full”模式,这样能够应用办法之间相互调用。并避免通过惯例Java调用意外地调用雷同的@Bean办法,这有助于缩小在“lite”模式下操作时难以跟踪的轻微谬误。
上面几节将深刻探讨@Bean和@Configuration注解。
然而,首先,咱们将介绍应用基于java的配置创立spring容器的各种办法。
1.12.2。应用AnnotationConfigApplicationContext实例化Spring容器
上面几节介绍了Spring3.0中引入的AnnotationConfigApplicationContext。
这个多功能的ApplicationContext实现不仅能够承受@Configuration类作为输出,还能够承受一般的@Component类和用JSR-330元数据注解的类。
当@Configuration类作为输出提供时,@Configuration类自身被注册为bean定义,类中所有申明的@bean办法也被注册为bean定义。
当@Component和JSR-330类被提供时,它们被注册为bean定义,并且会被用在在必要的中央进行注入,比方@Autowired或@Inject。
Simple Construction
与应用Spring XML文件须要实例化ClassPathXmlApplicationContext用作输出的形式简直雷同,应用@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依赖注入注解,例如@Autowired。
通过应用编程形式构建容器 register(Class<?>…)
您能够AnnotationConfigApplicationContext应用no-arg构造函数实例化一个对象,而后应用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 { ...}
ComponentScan注解启用组件扫描。
有教训的Spring用户可能相熟Springcontext:命名空间中的XML申明,如以下示例所示:
<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定义。
反对Web应用程序 AnnotationConfigWebApplicationContext
一个基于AnnotationConfigApplicationContext的WebApplicationContext变种是AnnotationConfigWebApplicationContext。
能够应用此实现来配置Spring ContextLoaderListenerservlet监听器,Spring MVC的 DispatcherServlet。
以下web.xml代码片段配置了典型的Spring MVC Web应用程序(请留神应用contextClasscontext-param和init-param),这里的启动流程后面的文章有讲过的这里不再赘述:
<web-app> <!-- 配置 ContextLoaderListener来应用AnnotationConfigWebApplicationContext 替换默认的 default XmlWebApplicationContext --> <context-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </context-param> <!-- 指定 配置类--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>com.acme.AppConfig</param-value> </context-param> <!-- 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> <!-- 配置 ContextLoaderListener来应用AnnotationConfigWebApplicationContext替换默认的 default XmlWebApplicationContext --> <init-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </init-param> <!-- 指定mvc config--> <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>
1.12.3。应用@Bean注解
@Bean是办法级别的注解,是XML<bean><bean/>元素的间接类似物。
注解提供的某些属性,例如: init-method destroy-method autowiring name *。
你能够一个注解@Configuration-annotated或在 @Component-annotated类应用@Bean。
申明一个bean
要申明一个bean,能够用注解对办法进行@Bean注解。
办法返回值的类型就是在ApplicationContext中注册的bean定义的类型。
默认状况下,Bean名称与办法名称雷同。
以下示例显示了@Bean办法申明:
@Configurationpublic class AppConfig { @Bean public TransferServiceImpl transferService() { return new TransferServiceImpl(); }}
后面的配置与上面的Spring XML齐全等效:
<beans> <bean id="transferService" class="com.acme.TransferServiceImpl"/></beans>
这两个申明都使名为transferService的bean在ApplicationContext中可用,该bean绑定到类型为TransferServiceImpl的对象实例,
如下图所示:
transferService -> com.acme.TransferServiceImpl
您还能够应用@Bean将接口(或基类)作为返回类型申明您的办法,
如以下示例所示:
@Configurationpublic class AppConfig { @Bean public TransferService transferService() { return new TransferServiceImpl(); }}
如果您始终通过申明的服务接口援用您的类型,@Bean返回类型能够平安地退出到设计决策中。然而,对于实现多个接口的组件或其实现类型可能援用的组件,申明最具体的返回类型更平安(至多与援用bean的注入点所需的返回类型雷同)。
Bean依赖
带@Bean注解的办法能够具备任意数量的参数,这些参数形容了构建该bean所需的依赖关系。
例如,如果咱们TransferService须要AccountRepository,咱们能够应用办法参数来实现该依赖关系,如以下示例所示:
@Configurationpublic class AppConfig { @Bean public TransferService transferService(AccountRepository accountRepository) { return new TransferServiceImpl(accountRepository); }}
解析机制与基于构造函数的依赖注入简直雷同。
接管生命周期回调
应用@Bean注解定义的任何类都反对惯例的生命周期回调,并且能够应用JSR-250中的@PostConstruct和@PreDestroy注解。
还齐全反对惯例的Spring生命周期回调。
如果bean实现InitializingBean,DisposableBean或Lifecycle,则容器将调用它们各自的办法。
还齐全反对规范*Aware接口集(例如BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware等)。
该@Bean注解反对指定任意初始化和销毁回调办法,就像SpringXML中的init-method和destroy-method属性的bean元素,如上面的示例所示:
public class BeanOne { public void init() { // initialization logic }}public class BeanTwo { public void cleanup() { // destruction logic }}@Configurationpublic class AppConfig { @Bean(initMethod = "init") public BeanOne beanOne() { return new BeanOne(); } @Bean(destroyMethod = "cleanup") public BeanTwo beanTwo() { return new BeanTwo(); }}
默认状况下,应用Java配置定义的具备公共close或shutdown办法的bean将主动与销毁回调一起调用。比方这样:在容器销毁的时候就会被调用,不须要继承任何接口@Beanpublic class ServiceFour { public void close(){ System.out.println("20201030-close"); }}如果您有一个公共的close或shutdown办法,并且不心愿在容器敞开时调用它,那么能够将@Bean(destroyMethod=“”)增加到Bean定义中,以禁用默认(推断)模式。@Bean(destroyMethod="")public class ServiceFour { public void close(){ System.out.println("20201030-close"); }}默认状况下,您可能心愿对应用JNDI获取的资源执行此操作,因为它的生命周期是在应用程序内部治理的。特地是,确保总是对数据源执行此操作。以下示例显示如何阻止数据源的主动销毁回调:@Bean(destroyMethod="")public DataSource dataSource() throws NamingException { return (DataSource) jndiTemplate.lookup("MyDS");}此外,对于@Bean办法,通常应用程序化JNDI查找,办法是应用SpringJndiTemplate或JndiLocatorDelegate辅助办法,或者间接InitialContext应用JNDI, 但不应用JndiObjectFactoryBean变体(这将迫使您将返回类型申明为FactoryBean类型,而不是理论的指标类型,不是指标类型的话就很难使其余的@Bean办法来穿插调用此办法)。
对于上述示例中的BeanOne,在结构过程中间接调用init()办法同样无效,如下例所示:
@Configurationpublic class AppConfig { @Bean public BeanOne beanOne() { BeanOne beanOne = new BeanOne(); beanOne.init(); return beanOne; } // ...}
当您间接应用Java工作时,您能够对对象执行任何操作,而不用总是依赖于容器生命周期。
指定Bean范畴
Spring蕴含@Scope注解,以便您能够指定bean的范畴。
应用@Scope注解
@Bean默认作用域是singleton,然而您能够应用@Scope注解笼罩它,如以下示例所示:
@Configurationpublic class MyConfiguration { @Bean @Scope("prototype") public Encryptor encryptor() { // ... }}
@Scope 和 scoped-proxy
Spring提供了一种通过作用域代理解决作用域依赖关系的不便办法。
在应用XML配置时创立这样一个代理的最简略办法是<aop:scoped-proxy/>元素。
<bean id="serviceOne" class="org.springframework.example.service.impl.ServiceOne"> <aop:scoped-proxy /> </bean>
用@Scope注解在Java中配置bean提供了与proxyMode属性雷同的反对。
默认设置为无代理(ScopedProxyMode.NO),但您能够指定ScopedProxyMode.TARGET_CLASS or ScopedProxyMode.INTERFACES。
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- http session 的作用域 将作为代理呈现 --> <bean id="userPreferences" class="com.something.UserPreferences" scope="session"> <aop:scoped-proxy/> </bean> <!-- 单例注入 的是一个代理不是 一个实例 防止只援用http session的一个实例 --> <bean id="userService" class="com.something.SimpleUserService"> <property name="userPreferences" ref="userPreferences"/> </bean></beans>
如果您应用Java Config将上述XML参考文档中的作用域代理示例移植到咱们的@Bean,它相似于以下内容:
// an HTTP Session-scoped bean exposed as a proxy@Bean@SessionScopepublic UserPreferences userPreferences() { return new UserPreferences();}@Beanpublic Service userService() { UserService service = new SimpleUserService(); // a reference to the proxied userPreferences bean service.setUserPreferences(userPreferences()); return service;}
自定义Bean命名
默认状况下,配置类应用@Bean办法的名称作为后果bean的名称。
然而,能够应用name属性笼罩此性能,如以下示例所示:
@Configurationpublic class AppConfig { @Bean(name = "myThing") public Thing thing() { return new Thing(); }}
Bean别名
正如在命名bean中所探讨的,有时须要为单个bean指定多个名称,或者称为bean别名。
为此,@Bean注解的name属性承受一个字符串数组。
上面的示例演示如何为bean设置多个别名:
@Configurationpublic class AppConfig { @Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"}) public DataSource dataSource() { // instantiate, configure and return DataSource bean... }}
bean形容
有时,提供bean的更具体的文本形容是很有帮忙的。当bean(可能通过JMX)公开以进行监督时,这一点特地有用。
要向@Bean增加阐明,能够应用@description注解,如下示例所示:
@Configurationpublic class AppConfig { @Bean @Description("Provides a basic example of a bean") public Thing thing() { return new Thing(); }}
1.12.4。应用@Configuration注解
@Configuration是一个类级别的注解,来指定以后类是一些bean definition的聚合。
@Configuration 类通过public@Bean注解办法申明Bean。
对@Configuration类上的@Bean办法的调用也能够用来定义Bean间的依赖关系。
注入bean间的依赖关系
当bean彼此依赖时,表白这种依赖就像让一个bean办法调用另一个一样简略,如以下示例所示:
@Configurationpublic class AppConfig { @Bean public BeanOne beanOne() { return new BeanOne(beanTwo()); } @Bean public BeanTwo beanTwo() { return new BeanTwo(); }}
在后面的示例中,通过构造函数注入beanOne接管对的援用beanTwo。
仅当@Bean在@Configuration类中申明该办法时,此申明bean间依赖关系的办法才无效。您不能应用一般@Component类申明bean间的依赖关系。
Lookup Method注入
如前所述,Lookup Method注入是一种高级个性,应该很少应用。
在单例范畴bean依赖于原型范畴bean的状况下,它十分有用。
将Java用于此类配置提供了实现此模式的天然办法。
以下示例演示如何应用查找办法注入:
public abstract class CommandManager { public Object process(Object commandState) { Command command = createCommand(); command.setState(commandState); return command.execute(); } // okay... but where is the implementation of this method? protected abstract Command createCommand();}
通过应用Java配置,能够创立一个笼罩CommandManager形象createCommand()办法的子类,该办法将以某种形式查找新的(原型)命令对象。以下示例显示了如何执行此操作:
@Bean@Scope("prototype")public AsyncCommand asyncCommand() { AsyncCommand command = new AsyncCommand(); return command;}@Beanpublic CommandManager commandManager() { // return CommandManager 的匿名实现类 并且实现了 createCommand()办法 // return a new 原型类的 Command object return new CommandManager() { protected Command createCommand() { return asyncCommand(); } }}
无关基于Java的配置如何在外部工作的更多信息
思考以下示例,该示例显示了一个带@Bean注解的办法被调用两次:
@Configurationpublic 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(); }}
clientDao()被clientService1()调用一次和被clientService2()调用一次。
因为此办法创立的新实例ClientDaoImpl并返回它,因而通常来说心愿有两个实例(每个服务一个)。
那必定是有问题的:在Spring中,实例化的bean singleton默认具备作用域。
这就是神奇的中央:所有@Configuration类在启动时都应用子类化CGLIB。在子类中,子办法在调用父办法并创立新实例之前,首先查看容器中是否有任何缓存(作用域)的bean。
依据bean的范畴,行为可能有所不同。 咱们在这里议论单例。
从Spring 3.2开始,不再须要将CGLIB增加到您的类门路中,因为CGLIB类曾经被从新打包org.springframework.cglib并间接蕴含在spring-core JAR中。
因为CGLIB在启动时动静增加个性,因而有一些限度。尤其是,配置类不能是final。然而,从4.3开始,配置类上容许应用任何构造函数,包含应用@Autowired或一个用于默认注入的非默认构造函数申明。如果您心愿防止任何CGLIB强加的限度,请思考在非@configuration 类上申明@Bean办法(例如,在一般@Component类上)。而后@Bean办法之间的跨办法调用不会被拦挡,然而因而您必须在构造函数或办法级别专门进行依赖的注入,间接调用非@configuration的@Bean办法Spring将不再负责依赖注入。
1.12.5。组成基于Java的配置
Spring基于Java的配置个性容许您编写注解,这能够升高配置的复杂性。
应用@Import注解
正如Spring XML文件中应用<import><import/>该元素来帮忙模块化配置一样,该@Import注解容许@Bean从另一个配置类加载定义,如以下示例所示:
@Configurationpublic 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还反对对惯例component 类的援用,相似于该AnnotationConfigApplicationContext.register办法。 如果要通过应用一些配置类作为入口点来显式定义所有组件,从而防止组件扫描,则此性能特地有用。
注入对导入@Bean定义的依赖
后面的示例无效,但过于简略。
在大多数理论状况下,Bean在配置类之间相互依赖。
应用XML时,这不是问题,因为不波及编译器,并且您能够申明ref="someBean"并信赖Spring在容器初始化期间对其进行解决。
应用@Configuration类时,Java编译器会在配置模型上施加束缚,因为对其余bean的援用必须是无效的Java语法。
侥幸的是,解决这个问题很简略。
正如咱们曾经探讨的那样,一种@Bean办法能够具备任意数量的形容Bean依赖关系的参数。
思考以下具备多个@Configuration类的更实在的场景,每个类都取决于其余类中申明的bean:
@Configurationpublic class ServiceConfig { @Bean public TransferService transferService(AccountRepository accountRepository) { return new TransferServiceImpl(accountRepository); }}@Configurationpublic 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:这意味着他们能够利用 @Autowired与@Value注入等性能雷同的其它bean。
确保以这种形式注入的依赖项只属于最简略的类型。@Configuration 类在上下文初始化过程中很早就被解决,强制以这种形式注入依赖项可能会导致意外的晚期初始化。只有有可能,就尽可能应用基于参数的注入,如后面的示例所示。另外,通过@Bean应用BeanPostProcessor和BeanFactoryPostProcessor定义要特地小心。这些办法通常应该申明为static@Bean办法,而不是触发其蕴含的配置类的实例化。如果自定义的BeanPostProcessor创立为早于AutowiredAnnotationBeanPostProcessor的bean实例,那么,@Autowired和@Value不适用于配置类自身,
以下示例阐明如何将一个bean主动连贯到另一个bean:
@Configurationpublic class ServiceConfig { @Autowired private AccountRepository accountRepository; @Bean public TransferService transferService() { return new TransferServiceImpl(accountRepository); }}@Configurationpublic class RepositoryConfig { private final DataSource dataSource; 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");}
从springframework4.3开始,@Configuration类中的构造函数注入才受反对。还要留神,如果指标bean只定义了一个构造函数,则不须要指定@Autowired。
Fully-qualifying imported beans for ease of navigation
在后面的场景中,应用@Autowired能够很好地工作并提供所需的模块性,然而精确地确定注入的bean定义的申明地位依然有些含糊。
例如,作为一个正在查看ServiceConfig的开发人员,您如何确切地晓得@autowiredaccountrepository bean是在哪里申明的?
如果这种模糊性是不可承受的,并且您心愿在IDE中从一个@Configuration类间接导航到另一个@Configuration类,能够思考主动连贯配置类自身。
上面的示例演示如何执行此操作:
@Configurationpublic 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类,能够在肯定水平上缓解这种紧耦合。
思考以下示例:
@Configurationpublic class ServiceConfig { @Autowired private RepositoryConfig repositoryConfig; @Bean public TransferService transferService() { return new TransferServiceImpl(repositoryConfig.accountRepository()); }}@Configurationpublic interface RepositoryConfig { @Bean AccountRepository accountRepository();}@Configurationpublic 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类及其依赖项与导航基于接口的代码的通常过程没有什么不同。
如果您想影响某些bean的启动创立程序,能够思考将其中一些bean申明为@Lazy(用于在首次拜访时而不是在启动时创立)或@DependsOn某些其余bean(确保在以后bean之前创立特定的其余bean,而不是后者的间接依赖关系)。
联合Java和XML配置
Spring的@Configuration类反对并不是要齐全代替Spring XML。
有些工具(如Spring XML名称空间)依然是配置容器的现实形式。
在XML不便或必要的状况下,你有一个抉择:要么通过应用ClassPathXmlApplicationContext在容器实例化在一个“以XML为核心”的形式应用,或通过应用AnnotationConfigApplicationContext搭配@ImportResource注解导入XML。
以XML为核心的@Configuration类应用
最好是从XML疏导Spring容器,并以特地的形式蕴含@Configuration类。
例如,在应用Spring XML的大型现有代码库中,更容易依据须要创立@Configuration类并从现有XML文件中蕴含它们。
在本节的前面,咱们将介绍在这种“以xml为核心”的状况下应用@Configuration类的选项。
请记住,@Configuration类最终是容器中的bean定义。
在本系列示例中,咱们创立一个@Configuration名为的类,AppConfig并将其蕴含在其中system-test-config.xml作为<bean/>定义。
因为 <context:annotation-config/>已关上,所以容器会辨认 @Configuration注解并 正确处理@Bean申明的办法AppConfig。
以下示例显示了Java中的一般配置类:
@Configurationpublic 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> <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 / xdbjdbc.username = sajdbc.password =
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml"); TransferService transferService = ctx.getBean(TransferService.class); // ...}
因为@Configuration元注解是@Component,@Configuration注解的类会主动成为组件扫描的候选类。
应用与前一个例子中形容的雷同场景,咱们能够从新定义system-test-config.xml利用组件扫描。
留神,在这种状况下,咱们不须要显式申明<context:annotation-config/>,因为<context:component-scan/>启用雷同的性能。
以下示例显示了批改后的system-test-config.xml文件:
<beans> <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 类为核心搭配 @ImportResource 的应用XML
在@Configuration类是配置容器的次要机制的应用程序中,依然可能须要至多应用一些XML。
在这些场景中,您能够应用@ImportResource只定义所需的XML。
这样做能够实现“以Java为核心”的办法来配置容器,并将XML放弃在最低限度。
上面的示例(包含一个配置类、一个定义bean的XML文件、一个属性文件和一个主类)演示了如何应用@ImportResource注解来实现“以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 / xdbjdbc.username = sajdbc.password = adsdfas
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); TransferService transferService = ctx.getBean(TransferService.class); // ...}