Spring核心技术(十一)——基于Java的容器配置(一)
2016-07-27 23:06
796 查看
基本概念: @Bean
和@Configuration
Spring中新的基于Java的配置的核心就是支持@Configuration注解的类以及
@Bean注解的方法。
@Bean注解用来表示一个方法会实例化,配置,并初始化一个新的由Spring IoC容器所管理的对象。其作用等于XML配置中的
<beans>标签下的
<bean>子标签。开发者可以用
@Bean注解来和任何的Spring
@Component来联合使用,但是,最常见的情况下,
@Bean注解还是应用到注解了
@Configuration的类下面的。
注解了
@Configuration的类就表示这个类的首要目的是用来管理Bean的配置的。而且,
@Configuration注解的类允许
inter-bean之类的依赖在类中通过方法调用来引用。最简单的配置如下:
@Configuration public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } }
上面的配置将等价于如下的XML配置:
<beans> <bean id="myService" class="com.acme.services.MyServiceImpl"/> </beans>
全
@Configuration对比轻
@Bean模式
当
@Bean注解的方法声明到了没有配置
@Configuration的类中时,这些方法就会作为轻
@Bean模式来处理。举例来说,Bean方法如果声明到了
@Component注解的类,或者普通的类之中,就是轻
@Bean模式。
和使用
@Configuration不同,轻
@Bean方法不能够声明
inter-bean的依赖,通常一个
@Bean方法不应该调用另一个
@Bean。
只有在注解了
@Configuration的类中使用
@Bean方法才是全模式。这回防止很多
@Bean方法多次调用,并且消除一些细微的bug。这些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注解。
简单构造
在Spring使用XML作为输入的时候,实例的ApplicationContext是
ClassPathXmlApplicationContext,而通过
@Configuration注解的类,实例化的
ApplicationContext是
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的类同样可以作为输入来构造Sprign容器,比如:
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类除了通过类来初始化,也可以通过无惨构造函数来进行构造,之后通过
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 { ... }
在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); }
@Congirufation注解的类和
@Component注解的类都是扫描的候选者。在上面的例子中,如果
AppConfig类在
com.acme包中(或者是其子包之中),
AppConfig都会被
scan方法扫描到,在
refresh()方法调用后,其中的
@Bean注解的方法都会作为Bean实例被注册到容器之中。
对于Web应用的支持
WebApplicationContext接口关于
AnnotationConfigApplicationContext的一个变化的版本就是
AnnotationConfigWebApplicationContext。这一实现可以在配置Spring的
ContextLoaderListener这个Servlet的listener或者Spring MVC的DispatcherServlet的时候使用。下面就是web.xml中配置Spring MVC程序的一个配置:
<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
注解
@Bean注解是一个方法级别的注解,和XML中的
<bean />标签的功能一直。该注解支持
<bean/>的一些配置属性,比如
init-method,
destroy-method,
autowiring以及
name等。
@Bean注解可以在注解了
@Configuration或者
@Component的类中使用。
声明Bean
通过将方法注解@Bean即可声明实例为Bean。开发者通过这个方法将Bean注册到
ApplicationContext,方法返回的类型就是Bean的类型。默认情况下,方法的名字就是Bean默认的名字,参考如下Bean的声明:
@Configuration public class AppConfig { @Bean public TransferService transferService() { return new TransferServiceImpl(); } }
上面的代码完全等价于一下XML:
<beans> <bean id="transferService" class="com.acme.TransferServiceImpl"/> </beans>
两种声明都可以在
ApplicationContext中声明一个名为
transferService的Bean,实例的类型为
TransferServiceImpl:
transferService -> com.acme.TransferServiceImpl
Bean依赖
由@Bean注解的方法可以有任意数量的参数来描述其依赖。距离来说,如果
TransferService的其中一个依赖为
AccountRepository的话,我们可以通过方法参数来构造:
@Configuration public class AppConfig { @Bean public TransferService transferService(AccountRepository accountRepository) { return new TransferServiceImpl(accountRepository); } }
解析的机制是和基于构造的依赖注入基本一致的,可以参考前文Spring的依赖及其注入了解相关内容。
接收生命周期回调
任何通过@Bean注解了的方法返回的类,都支持常规的生命周期回调,并可以通过使用JSR-250中的
@PostContruct以及
@PreDestroy注解。
基本的Spring生命周期也是同样支持的,如果Bean实现了
InitializingBean,
DisposableBean或者是
Lifecycle接口的话,这些方法都会被容器调用。
标准的
*Aware接口比如说
BeanFactoryAware,
BeanNameAware,
MessageSourceAware,
ApplicationContextAware等接口也是支持的。
@Bean也支持指定任意的初始化以及销毁回调函数,跟Spring XML配置中的
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(); } }
默认情况下,通过Java配置的Bean都会有一个
close或者
shutdown方法来作为自动的销毁回调。如果开发者声明了
close方法或者是
shutdown方法,但是不希望由容器来调用的话,可以在注解中标记为
@Bean(destroyMethod="")来代替默认的行为。
当然,在上面的
Foo例子当中,也可以直接调用
init()方法:
@Configuration public class AppConfig { @Bean public Foo foo() { Foo foo = new Foo(); foo.init(); return foo; } // ... }
当直接使用Java的配置的时候,开发者可以任意操作对象,而不仅仅是依赖于容器的声明周期
指定Bean的作用域
使用@Scope
注解
开发者也可以通过Java配置的方式来指定@Bean的作用域,开发者可以使用所有的标准的作用域,关于作用域的信息,可以在Spring中Bean的作用域一文中了解。
默认的作用域是
singleton,但是开发者可以通过
@Scope注解来覆盖掉默认值:
@Configuration public class MyConfiguration { @Bean @Scope("prototype") public Encryptor encryptor() { // ... } }
@Scope注解以及作用域代理
Sprnig针对那些作用域的依赖是通过代理来工作的。在XML中,可以通过使用<aop:scoped-proxy/>标签来达到这个目的。通过Java配置的的
@Scope注解也提供等价的支持,默认的没有代理配置为
ScopedProxyMode.NO,也可以指定为
ScopedProxyMode.TARGET_CLASS或者
ScopedProxyMode.INTERFACES.
Java配置如下:
// 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的别名
在前文之中有针对Bean名字的描述,有时候会给一个Bean多个名字,作为Bean的别名,@Bean注解的
name属性也支持Spring数组类型的值:
@Configuration public class AppConfig { @Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" }) public DataSource dataSource() { // instantiate, configure and return DataSource bean... } }
Bean的描述
有的时候为Bean提供额外的文本描述可以让别人更了解该Bean的作用。这一点尤其在监视容器中的Bean的时候很有效。可以通过使用
@Description注解来做到:
@Configuration public class AppConfig { @Bean @Description("Provides a basic example of a bean") public Foo foo() { return new Foo(); } }
相关文章推荐
- java 线程终止方法
- Java_编程实例_需求二
- Java I/O_FilterInputStream类
- java垃圾回收机制
- springboot 整合mybatis
- java学习之路 之 面向对象编程-面向对象-对象的关联
- java容易混淆的15个知识点
- java容易混淆的15个知识点
- java容易混淆的15个知识点
- java容易混淆的15个知识点
- java学习之路 之 面向对象编程-面向对象-关键字(this、package、import)及java中主要包的介绍
- Java中的构造方法
- 读Java源码先锋队
- 创建型模式之单例设计模式(java版)
- 148. Sort List
- Spring Boot 学习之旅
- Java语言中的访问权限修饰符
- Java线程的同步与死锁
- 解决javaweb乱码问题
- JAVA编码规范笔记