共计 24098 个字符,预计需要花费 61 分钟才能阅读完成。
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 well
public 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;
@Named
public 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 类如下:
@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>
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 办法申明:
@Configuration
public 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 将接口(或基类)作为返回类型申明您的办法,
如以下示例所示:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {return new TransferServiceImpl();
}
}
如果您始终通过申明的服务接口援用您的类型,@Bean 返回类型能够平安地退出到设计决策中。然而,对于实现多个接口的组件或其实现类型可能援用的组件,申明最具体的返回类型更平安(至多与援用 bean 的注入点所需的返回类型雷同)。
Bean 依赖
带 @Bean 注解的办法能够具备任意数量的参数,这些参数形容了构建该 bean 所需的依赖关系。
例如,如果咱们 TransferService 须要 AccountRepository,咱们能够应用办法参数来实现该依赖关系,如以下示例所示:
@Configuration
public 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}
}
@Configuration
public class AppConfig {@Bean(initMethod = "init")
public BeanOne beanOne() {return new BeanOne();
}
@Bean(destroyMethod = "cleanup")
public BeanTwo beanTwo() {return new BeanTwo();
}
}
默认状况下,应用 Java 配置定义的具备公共 close 或 shutdown 办法的 bean 将主动与销毁回调一起调用。比方这样:在容器销毁的时候就会被调用,不须要继承任何接口
@Bean
public 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()办法同样无效,如下例所示:
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {BeanOne beanOne = new BeanOne();
beanOne.init();
return beanOne;
}
// ...
}
当您间接应用 Java 工作时,您能够对对象执行任何操作,而不用总是依赖于容器生命周期。
指定 Bean 范畴
Spring 蕴含 @Scope 注解,以便您能够指定 bean 的范畴。
应用 @Scope 注解
@Bean 默认作用域是 singleton,然而您能够应用 @Scope 注解笼罩它,如以下示例所示:
@Configuration
public 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
@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 = "myThing")
public Thing thing() {return new Thing();
}
}
Bean 别名
正如在命名 bean 中所探讨的,有时须要为单个 bean 指定多个名称,或者称为 bean 别名。
为此,@Bean 注解的 name 属性承受一个字符串数组。
上面的示例演示如何为 bean 设置多个别名:
@Configuration
public class AppConfig {@Bean({"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 Thing thing() {return new Thing();
}
}
1.12.4。应用 @Configuration 注解
@Configuration 是一个类级别的注解,来指定以后类是一些 bean definition 的聚合。
@Configuration 类通过 public@Bean 注解办法申明 Bean。
对 @Configuration 类上的 @Bean 办法的调用也能够用来定义 Bean 间的依赖关系。
注入 bean 间的依赖关系
当 bean 彼此依赖时,表白这种依赖就像让一个 bean 办法调用另一个一样简略,如以下示例所示:
@Configuration
public 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;
}
@Bean
public CommandManager commandManager() {// return CommandManager 的匿名实现类 并且实现了 createCommand()办法
// return a new 原型类的 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();
}
}
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 从另一个配置类加载定义,如以下示例所示:
@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 还反对对惯例 component 类的援用,相似于该 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:这意味着他们能够利用 @Autowired 与 @Value 注入等性能雷同的其它 bean。
确保以这种形式注入的依赖项只属于最简略的类型。@Configuration 类在上下文初始化过程中很早就被解决,强制以这种形式注入依赖项可能会导致意外的晚期初始化。只有有可能,就尽可能应用基于参数的注入,如后面的示例所示。另外,通过 @Bean 应用 BeanPostProcessor 和 BeanFactoryPostProcessor 定义要特地小心。这些办法通常应该申明为 static@Bean 办法,而不是触发其蕴含的配置类的实例化。如果自定义的 BeanPostProcessor 创立为早于 AutowiredAnnotationBeanPostProcessor 的 bean 实例, 那么,@Autowired 和 @Value 不适用于配置类自身,
以下示例阐明如何将一个 bean 主动连贯到另一个 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;
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 类,能够思考主动连贯配置类自身。
上面的示例演示如何执行此操作:
@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 类及其依赖项与导航基于接口的代码的通常过程没有什么不同。
如果您想影响某些 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 中的一般配置类:
@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>
<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);
// ...
}
因为 @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 / xdb
jdbc.username = sa
jdbc.password = adsdfas
public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}