多年以来,Spring大量的XML配置及复杂的依赖管理饱受非议。

为了实现免XML的开发体验。Spring加入了新的配置注解以支持Java Config开发模式,当中最重要的注解就是@Configuration和@Bean。

基本概念:@Bean和@Configuration

在Spring新的Java-configuration支持中,最核心的部分就是使用@Configuration注解的类和使用@Bean注解的类。

@Bean注解用于指示一个方法实例化。配置,初始化一个新的被Spring容器管理的对象。

对于熟悉Spring <beans/> XML配置的人来说,@Bean注解跟<bean/>元素作用同样。你能够在不论什么Spring @Component中使用@Bean注解的方法。只是。它们通常和@Configuration注解的beans一块使用。

使用@Configuration注解一个类意味着它的主要目的是作为bean定义的来源。

此外。@Configuration类同意bean之间(inter-bean)的依赖,你仅仅需简单地调用该类中其它的@Bean方法。

演示样例:

  1. @Configuration
  2. public class AppConfig {
  3. @Bean
  4. public MyService myService() {
  5. return new MyServiceImpl();
  6. }
  7. }

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

  1. <beans>
  2. <bean id="myService" class="com.acme.services.MyServiceImpl"/>
  3. </beans>
  • Full @Configuration VS ‘lite’ @Beans模式

当@Bean方法声明在没有被@Conguration注解的类里,这就是所谓的以’精简’模式处理。

比如,在一个@Component中,甚至在一个普通的类中声明的bean方法都会以’精简’处理。

跟完整@Configuration不同的是,精简@Bean方法难以声明bean之间的依赖。通常,在精简模式中操作时,不应该在一个@Bean方法中调用还有一个@Bean方法。

一种推荐的方式是仅仅在@Configuration类中使用@Bean方法,这样能够确保总是使用’完整’模式,避免@Bean方法意外地被调用多次,降低那些在精简模式下产生的非常难跟踪的微妙bugs。

使用AnnotationConfigApplicationContext实例化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文件用作输入相似,在实例化一个AnnotationConfigApplicationContext时能够使用@Configuration类作为输入。这就同意Spring容器全然零XML配置:

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

如上所述,AnnotationConfigApplicationContext不局限于仅仅使用@Configuration类。不论什么@Component或JSR-330注解的类都能够作为AnnotationConfigApplicationContext构造器的输入。比如:

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

上面假设MyServiceImpl,Dependency1和Dependency2使用Spring依赖注入注解。比方@Autowired。

*register(Class<?

>…​)实例化

能够使用无參的构造器实例化AnnotationConfigApplicationContext,然后使用register()方法对容器进行配置。这样的方式在以编程方式构造一个AnnotationConfigApplicationContext时非常实用。

  1. public static void main(String[] args) {
  2. AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
  3. ctx.register(AppConfig.class, OtherConfig.class);
  4. ctx.register(AdditionalConfig.class);
  5. ctx.refresh();
  6. MyService myService = ctx.getBean(MyService.class);
  7. myService.doStuff();
  8. }
  • 启用scan(String…​)的组件扫描

想要启用组件扫描,仅仅需按例如以下方式注解你的@Configuration类:

  1. @Configuration
  2. @ComponentScan(basePackages = "com.acme")
  3. public class AppConfig {
  4. ...
  5. }

注: 有经验的Spring用户会比較熟悉来自Springcontext:命名空间的等效XML声明:

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

在上面的演示样例中。将会扫描com.acme包。查找不论什么被@Component注解的类,而且这些类将被注冊为容器里的Spring bean定义。AnnotationConfigApplicationContext暴露scan(String…​)方法来实现同样的容器扫描功能:

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

注: 记着@Configuration类是被@Component元注解的,所以它们也是组件扫描的候选者。在上面的演示样例中。假设AppConfig定义在com.acme包(或不论什么下层包)中,在调用scan()期间它也会被扫描。当refresh()时它的全部@Bean方法将被处理,并注冊为容器里的bean定义。

  • 使用AnnotationConfigWebApplicationContext支持web应用

AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext的WebApplicationContext变种。当配置Spring ContextLoaderListener servlet监听器。Spring MVC DispatcherServlet等会用到。以下的web.xml片段配置了一个典型的Spring MVC web应用。

注意contextClass的context-param和init-param的使用:

  1. <web-app>
  2. <!-- 配置ContextLoaderListener使用 AnnotationConfigWebApplicationContext取代默认的XmlWebApplicationContext -->
  3. <context-param>
  4. <param-name>contextClass</param-name>
  5. <param-value>
  6. org.springframework.web.context.support.AnnotationConfigWebApplicationContext
  7. </param-value>
  8. </context-param>
  9. <!-- Configuration位置必须包括一个或多个逗号或空格分隔的全限定 @Configuration类.组件扫描中要指定该全限定包.-->
  10. <context-param>
  11. <param-name>contextConfigLocation</param-name>
  12. <param-value>com.acme.AppConfig</param-value>
  13. </context-param>
  14. <!-- 像寻常那样使用ContextLoaderListener启动根应用上下文 -->
  15. <listener>
  16. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  17. </listener>
  18. <!--像寻常那样声明一个Spring MVC DispatcherServlet -->
  19. <servlet>
  20. <servlet-name>dispatcher</servlet-name>
  21. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  22. <!-- 配置DispatcherServlet使用AnnotationConfigWebApplicationContext取代默认的XmlWebApplicationContext -->
  23. <init-param>
  24. <param-name>contextClass</param-name>
  25. <param-value>
  26. org.springframework.web.context.support.AnnotationConfigWebApplicationContext
  27. </param-value>
  28. </init-param>
  29. <!-- 再次。配置路径必须包括一个或多个逗号,或空格分隔的全限定@Configuration类 -->
  30. <init-param>
  31. <param-name>contextConfigLocation</param-name>
  32. <param-value>com.acme.web.MvcConfig</param-value>
  33. </init-param>
  34. </servlet>
  35. <!-- 将/app/*的全部请求映射到该dispatcher servlet -->
  36. <servlet-mapping>
  37. <servlet-name>dispatcher</servlet-name>
  38. <url-pattern>/app/*</url-pattern>
  39. </servlet-mapping>
  40. </web-app>

@Bean注解使用方法

@Bean是一个方法级别的注解。XML<bean/>元素的等价物。该注解提供一些<bean/>提供的元素,比如init-method, destroy-method, autowiringname

你能够在一个@Configuration注解或@Component注解的类中使用@Bean注解。

  • 声明一个bean

想要声明一个bean,仅仅需简单的使用@Bean注解一个方法。你能够使用该方法注冊一个bean定义到ApplicationContext中。类型通过方法的返回值指定。默认情况下,bean的名称和方法名一样。以下是一个简单的@Bean方法声明演示样例:

  1. @Configuration
  2. public class AppConfig {
  3. @Bean
  4. public TransferService transferService() {
  5. return new TransferServiceImpl();
  6. }
  7. }

上述的配置等价于以下的Spring XML:

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

两个声明都在ApplicationContext中创建了一个名称为transferService 的bean,该bean都绑定到一个TransferServiceImpl类型的对象实例:transferService -> com.acme.TransferServiceImpl

  • Bean依赖

一个@Bean注解的方法能够有随意数量的參数来描写叙述构建该bean所需的依赖。

举例来说,假设我们的TransferService须要一个AccountRepository,我们能够通过一个方法參数提供该依赖:

  1. @Configuration
  2. public class AppConfig {
  3. @Bean
  4. public TransferService transferService(AccountRepository accountRepository) {
  5. return new TransferServiceImpl(accountRepository);
  6. }
  7. }

这样的处理机制对于基于构造器的依赖注入非常重要,详细參考相关章节

  • 接收生命周期回调

每一个使用@Bean注解定义的类都支持常规的生命周期回调,并能够使用来自JSR-250的@PostConstruct和@PreDestroy注解,详细參考JSR-250注解,常规的Spring生命周期回调也全支持。

假设一个bean实现InitializingBean,DisposableBean或Lifecycle。它们对应的方法会被容器调用。

标准的*Aware接口集合,比方BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware等也都全部支持。

@Bean注解支持指定随意数量的初始化和销毁回调方法,相似于Spring XML bean元素的init-methoddestroy-method属性:

  1. public class Foo {
  2. public void init() {
  3. // initialization logic
  4. }
  5. }
  6. public class Bar {
  7. public void cleanup() {
  8. // destruction logic
  9. }
  10. }
  11. @Configuration
  12. public class AppConfig {
  13. @Bean(initMethod = "init")
  14. public Foo foo() {
  15. return new Foo();
  16. }
  17. @Bean(destroyMethod = "cleanup")
  18. public Bar bar() {
  19. return new Bar();
  20. }
  21. }

注: 默认情况下,使用Java config定义的beans有一个public的closeshutdown方法被自己主动注冊为销毁回调。假设你有一个public的closeshutdown方法。而且不想在容器关闭时调用它。你仅仅需简单地将@Bean(destroyMethod="")加入到bean定义以此禁用默认的判断(inferred)模式。

对于一个通过JNDI获取的资源来说,因为它的生命周期是由server管理,而不是应用。所以你可能想默认就这样做,特别是针对DataSource这样的资源:

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

当然,在上面的Foo演示样例中。直接在构造器中调用init()方法也是有效的:

  1. @Configuration
  2. public class AppConfig {
  3. @Bean
  4. public Foo foo() {
  5. Foo foo = new Foo();
  6. foo.init();
  7. return foo;
  8. }
  9. // ...
  10. }

注: 当直接使用Java时。你能够对你的对象做不论什么想做的事,而不总是须要依赖于容器的生命周期。

  • 指定bean作用域

1.使用@Scope注解

你能够为通过@Bean注解定义的bean指定一个特定的作用域。你能够使用Bean Scopes作用域中定义的不论什么标准作用域。默认作用域为singleton,但你能够使用@Scope作用域覆盖它:

  1. @Configuration
  2. public class MyConfiguration {
  3. @Bean
  4. @Scope("prototype")
  5. public Encryptor encryptor() {
  6. // ...
  7. }
  8. }

2.@Scope和scoped-proxy

Spring提供一个方便的方式来通过scoped proxies处理作用域的依赖。最简单的方式是创建一个这样的proxy,在使用XML配置时对应配置为<aop:scoped-proxy/>元素。在Java中通过@Scope注解和proxyMode属性能够达到同样的功能。默认没有proxy(ScopedProxyMode.NO),但你能够指定为ScopedProxyMode.TARGET_CLASS或ScopedProxyMode.INTERFACES。

假设你想从XML參考文档的scoped proxy演示样例过渡到使用Java的@Bean,它看起来可能例如以下所看到的:

  1. // an HTTP Session-scoped bean exposed as a proxy
  2. @Bean
  3. @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
  4. public UserPreferences userPreferences() {
  5. return new UserPreferences();
  6. }
  7. @Bean
  8. public Service userService() {
  9. UserService service = new SimpleUserService();
  10. // a reference to the proxied userPreferences bean
  11. service.setUserPreferences(userPreferences());
  12. return service;
  13. }

3.自己定义bean名称

默认情况下,配置类使用@Bean方法名作为结果bean的名称。

该功能能够被name属性覆盖。

  1. @Configuration
  2. public class AppConfig {
  3. @Bean(name = "myFoo")
  4. public Foo foo() {
  5. return new Foo();
  6. }
  7. }

4.Bean别名

有时候为单个bean起多个名称是有必要的,@Bean注解的name属性能够接收一个String数组来达到这个目的。

  1. @Configuration
  2. public class AppConfig {
  3. @Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })
  4. public DataSource dataSource() {
  5. // instantiate, configure and return DataSource bean...
  6. }
  7. }

5.Bean描写叙述

有时候为一个bean提供详细的文本描写叙述是非常有帮助的。特别是当beans被暴露(可能通过JMX)用于监控目的时。

能够使用@Description注解为一个@Bean加入描写叙述:

  1. @Configuration
  2. public class AppConfig {
  3. @Bean
  4. @Description("Provides a basic example of a bean")
  5. public Foo foo() {
  6. return new Foo();
  7. }
  8. }

@Configuration注解使用方法

@Configuration注解是一个类级别的注解,它意味着该对象是一个bean定义的来源。@Configuration类通过public的@Bean注解的方法来声明beans。调用@Configuration类上的@Bean方法可用于定义bean之间的依赖。

  • bean的依赖注入

当@Beans依赖其它bean时。仅仅须要在一个bean方法中调用还有一个bean就可以表达这样的依赖关系:

  1. @Configuration
  2. public class AppConfig {
  3. @Bean
  4. public Foo foo() {
  5. return new Foo(bar());
  6. }
  7. @Bean
  8. public Bar bar() {
  9. return new Bar();
  10. }
  11. }

在上面的演示样例中,foo bean通过构造器注入获取到一个指向bar的引用。

注: 声明bean之间依赖的方法仅仅在@Configuration类内部的@Bean方法有效,你不能使用普通的@Component类声明bean之间的引用。

  • Lookup方法注入

正如前面提到的,lookup method injection是一个高级的特性。你应该少用。当一个singleton作用域的bean依赖一个prototype作用域的bean时该方法非常实用。

使用Java配置方式对这样的类型的配置提供了一种自然的手段来实现该模式。

  1. public abstract class CommandManager {
  2. public Object process(Object commandState) {
  3. // grab a new instance of the appropriate Command interface
  4. Command command = createCommand();
  5. // set the state on the (hopefully brand new) Command instance
  6. command.setState(commandState);
  7. return command.execute();
  8. }
  9. // okay... but where is the implementation of this method?
  10. protected abstract Command createCommand();
  11. }

使用Java配置支持,你能够创建一个CommandManager的子类,在这里抽象的createCommand()方法以查找一个新的(prototype)命令对象来覆盖:

  1. @Bean
  2. @Scope("prototype")
  3. public AsyncCommand asyncCommand() {
  4. AsyncCommand command = new AsyncCommand();
  5. // inject dependencies here as required
  6. return command;
  7. }
  8. @Bean
  9. public CommandManager commandManager() {
  10. // return new anonymous implementation of CommandManager with command() overridden
  11. // to return a new prototype Command object
  12. return new CommandManager() {
  13. protected Command createCommand() {
  14. return asyncCommand();
  15. }
  16. }
  17. }
  • Java-based配置内容怎样工作

以下的演示样例展示了一个@Bean注解的方法被调用了两次:

  1. @Configuration
  2. public class AppConfig {
  3. @Bean
  4. public ClientService clientService1() {
  5. ClientServiceImpl clientService = new ClientServiceImpl();
  6. clientService.setClientDao(clientDao());
  7. return clientService;
  8. }
  9. @Bean
  10. public ClientService clientService2() {
  11. ClientServiceImpl clientService = new ClientServiceImpl();
  12. clientService.setClientDao(clientDao());
  13. return clientService;
  14. }
  15. @Bean
  16. public ClientDao clientDao() {
  17. return new ClientDaoImpl();
  18. }
  19. }

clientDao()clientService1()中调用了一次。然后在clientService2()中调用了一次。

因为这种方法创建了一个新的ClientDaoImpl实例,然后返回它,你能够期望有两个实例(每一个service都有一个实例)。这绝对会出问题:在Spring中。实例化的beans默认情况下作用域为singleton。

这就是魔法产生的地方:全部的@Configuration类在启动期间都是被CGLIB子类化过的(代理)。在子类中。子类的方法首先检查容器是否缓存(scoped)对应的beans。假设没有缓存才会调用父类的方法,创建一个新的实例。

注意在Spring3.2之后,已经不须要加入CGLIB的依赖,因为CGLIB被又一次打包到org.springframework下。并直接包括在spring-core JAR中。

注: 该行为依赖于bean的作用域,此处我们讨论的是单例。因为CGLIB动态代理的特性这里有一些限制:配置类不能为final的,它们应该有一个无參构造器。

《Spring Boot參考指南》翻译完成,欢迎各位拍砖。

Spring Java-based容器配置的更多相关文章

  1. IOC容器--1.12. 基于 Java 的容器配置

    用Java的方式配置Spring ,不使用Spring的XML配置,全权交给Java来做 JavaConfig是Spring的一个子项目,在Sring 4  之后成为核心功能 这种纯Java的配置方式 ...

  2. [转] Spring - Java Based Configuration

    PS: Spring boot注解,Configuration是生成一个config对象,@Bean指定对应的函数返回的是Bean对象,相当于XML定义,ConfigurationProperties ...

  3. Spring核心技术(十二)——基于Java的容器配置(二)

    使用@Configuration注解 @Configuration注解是一个类级别的注解,表明该对象是用来指定Bean的定义的.@Configuration注解的类通过@Bean注解的方法来声明Bea ...

  4. Spring核心技术(十一)——基于Java的容器配置(一)

    基本概念: @Bean和@Configuration Spring中新的基于Java的配置的核心就是支持@Configuration注解的类以及@Bean注解的方法. @Bean注解用来表示一个方法会 ...

  5. Spring Boot Server容器配置

    参数配置容器 server.xx开头的是所有servlet容器通用的配置,server.tomcat.xx开头的是tomcat特有的参数,其它类似. 所有参数绑定配置类:org.springframe ...

  6. [转载]Spring Java Based Configuration

    @Configuration & @Bean Annotations Annotating a class with the @Configuration indicates that the ...

  7. 【Java】Spring之基于注释的容器配置(四)

    注释是否比配置Spring的XML更好? 基于注释的配置的引入引发了这种方法是否比XML“更好”的问题.答案是每种方法都有其优点和缺点,通常,由开发人员决定哪种策略更适合他们.由于它们的定义方式,注释 ...

  8. 从零开始学 Java - Spring 集成 Memcached 缓存配置(二)

    Memcached 客户端选择 上一篇文章 从零开始学 Java - Spring 集成 Memcached 缓存配置(一)中我们讲到这篇要谈客户端的选择,在 Java 中一般常用的有三个: Memc ...

  9. String框架搭建的基本步骤,及从 IOC & DI 容器中获取 Bean(spring框架bean的配置)--有实现数据库连接池的链接

    Spring框架的插件springsource-tool-suite-3.4.0.RELEASE-e4.3.1-updatesite(是一个压缩包)导入步骤: eclipse->help-> ...

  10. Spring IoC — 基于Java类的配置

    普通的POJO只要标注@Configuration注解,就可以为Spring容器提供Bean定义的信息了,每个标注了@Bean的类方法都相当于提供一个Bean的定义信息. 基于Java类的配置方法和基 ...

随机推荐

  1. [BZOJ2051]A Problem For Fun/[BZOJ2117]Crash的旅游计划/[BZOJ4317]Atm的树

    [BZOJ2051]A Problem For Fun/[BZOJ2117]Crash的旅游计划/[BZOJ4317]Atm的树 题目大意: 给出一个\(n(n\le10^5)\)个结点的树,每条边有 ...

  2. 我的git笔记

    转眼间加入git的阵营已经快两年了,结识git,缘起github,2年前在寻找代码托管网站,当时还是用svn,起初使用google code,可是google的服务虽好,在天朝你懂得,后来发现了git ...

  3. 429. N叉树的层序遍历

    429. N叉树的层序遍历 题意 给定一个 N 叉树,返回其节点值的层序遍历. (即从左到右,逐层遍历). 解题思路 和二叉树的层次遍历的思想一样: 实现 class Solution(object) ...

  4. 一些公司的面试题目 U3D

    #include <iostream> using namespace std; int main() { int N; while (cin>>N&&N> ...

  5. IE6条件下的bug与常见的bug及其解决方法

    1.IE6条件下有双倍的margin 解决办法:给这个浮动元素增加display:inline属性 2. 图片底部有3像素问题 解决办法:display:block;或者vertical-align: ...

  6. Map和String类型之间的转换

    前提是String的格式是map或json类型的 public static void main(String[] args) { Map<String,Object> map = new ...

  7. Linux 下安装 Mongodb

    mongodb在linux下面的安装应该是很简单的,但是有一个小点需要注意,这也就是我为什么写这篇博客的原因. 首先到其官网上下载最新稳定版,解压到目录,如/usr/local/mongodb 在mo ...

  8. [Android Pro] AndroidX了解一下

    cp : https://blog.csdn.net/qq_17766199/article/details/81433706 1.说明 官方原文如下: We hope the division be ...

  9. 安装NVIDIA驱动时禁用自带nouveau驱动

    安装英伟达驱动时,一般需要禁用自带nouveau驱动,按如下命令操作: sudo vim /etc/modprobe.d/blacklist-nouveau.conf 添加如下内容: blacklis ...

  10. pytorch中检测分割模型中图像预处理探究

    Object Detection and Classification using R-CNNs 目标检测:数据增强(Numpy+Pytorch) - 主要探究检测分割模型数据增强操作有哪些? - 检 ...