目录
AnnotationConfigApplicationContext
AnnotationConfigWebApplicationContext
Conditionally Include @Configuration Classes or @Bean Methods
容器扩展点
通常,应用程序开发者,不需要继承ApplicationContext的实现类。相反,Spring IoC 容器可以通过插入特殊的集成接口来实现扩展。
BeanPostProcessor
BeanPostProcessor定义了回调方法,通过实现这个回调方法,可以提供自己的(或重写容器默认的)实例化逻辑、依赖分析逻辑等。如果想在Spring 容器完成实例化、配置和初始化bean 后,实例化一些自定义的逻辑,可以插入一个或多个BeanPostProcessor的实现。
开发者可以配置多个BeanPostProcessor 实例, 并通过设置order属性来控制这些BeanPostProcessor执行的顺序,仅BeanPostProcessor实现Ordered接口才可以设置order属性。如果编写自己的BeanPostProcessor ,也应该考虑实现Ordered 接口。
接口方法:
@Nullable
default Object postProcessAfterInitialization(Object bean,String beanName)throws BeansException
@Nullable
default Object postProcessBeforeInitialization(Object bean,String beanName)throws BeansException
BeanPostProcessor实例在bean(或对象)实例上操作。也就是说,spring ioc容器实例化一个bean实例,然后BeanPostProcessor实例执行它们的工作。
BeanPostProcessor实例的作用域是每个容器。只有在使用容器层次结构时,这才相关。如果在一个容器中定义BeanPostProcessor,则它只对该容器中的bean进行后期处理。换句话说,在一个容器中定义的bean不会由在另一个容器中定义的BeanPostProcessor进行后期处理,即使两个容器都是同一层次结构的一部分。
要更改实际的bean定义(即定义bean的蓝图),您需要使用BeanFactoryPostProcessor。
BeanPostProcessor会在容器的初始化方法(InitializingBean.afterPropertiesSet() 或者任何init方法)调用前和所有初始化回调之后被调用。
BeanPostProcessor可以对bean采取任何措施,包括完全忽略回调。一个BeanPostProcessor,通常会检查回调接口或使用代理包装一个bean 。一些Spring AOP 基础设施类,为了提供包装式的代理逻辑,被实现为BeanPostProcessor。
ApplicationContext 会自动地检测所有定义在配置元文件中,并实现了BeanPostProcessor 接口的bean。该ApplicationContext注册这些bean作为后置处理器,使它们可以在bean 创建完成后被调用。bean后置处理器可以像其他bean一样被部署到容器中。
注意,当通过在配置类上使用@bean注解工厂方法声明BeanPostProcessor时,工厂方法的返回类型应该是实现类本身,或者至少是org.springframework.beans.factory.config.BeanPostProcessor接口,清楚地指示后置处理器特性。否则,ApplicationContext在完全创建之前无法按类型自动检测它。由于BeanPostProcessor需要提前实例化,以便应用于上下文中其他bean的初始化,因此这种早期类型检测非常关键。
编程式注册BeanPostProcessor实例
虽然官方推荐注册BeanPostProcessor的方法是通过ApplicationContext自动检测(如前所述),但可以使用ConfigurableBeanFactory 的addBeanPostProcessor 方法注册它们。当您需要在注册之前评估条件逻辑,甚至需要跨层次结构中的上下文复制bean后处理器时,这一点非常有用。但是,以编程方式添加的BeanPostProcessor实例不遵守Ordered 接口。在这里,注册的顺序决定了执行的顺序。还要注意,无论显式排序如何指定,以编程方式注册的BeanPostProcessor实例总是在通过自动检测注册的实例之前进行处理。
BeanPostProcessor实例和AOP自动代理
实现BeanPostProcessor接口的类是特殊的,容器对它们的处理方式不同。所有BeanPostProcessor实例和它们直接引用的bean都在启动时实例化,作为ApplicationContext的特殊启动阶段的一部分。接下来,所有BeanPostProcessor实例都以有序方式注册,并应用于容器中的所有其他bean。由于AOP自动代理是作为BeanPostProcessor本身实现的,因此BeanPostProcessor实例或它们直接引用的bean都不符合自动代理的条件,因此,它们中没有Aspect被weave进去。
对于任何这样的bean,您都应该看到一条信息日志消息:bean somebean不适合由所有BeanPostProcessor接口处理(例如:不适合自动代理)。
如果通过使用autowiring或@Resource(可能会返回到autowiring)将bean注入到BeanPostProcessor中,spring可能会在搜索类型匹配的依赖项候选者时访问意外的bean,因此使它们不符合自动代理或其他类型的bean后置处理。
例如,如果有一个用@Resource注解的依赖项,其中字段或setter名称与bean的声明名称不直接对应,并且没有使用name属性,那么spring将访问其他bean以按类型匹配它们。
示例
package scripting;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
// simply return the instantiated bean as-is
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean; // we could potentially return any object reference here...
}public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
return bean;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans ...><lang:groovy id="messenger"
script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
<lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
</lang:groovy><!--
when the above bean (messenger) is instantiated, this custom
BeanPostProcessor implementation will output the fact to the system console
-->
<bean class="scripting.InstantiationTracingBeanPostProcessor"/></beans>
BeanFactoryPostProcessor
BeanFactoryPostProcessor操作bean的配置元数据,也就是说,Spring 的IoC容器允许BeanFactoryPostProcessor来读取配置元数据,并可以在容器实例化任何bean ( 除了BeanFactoryPostProcessor )之前修改它.
可以设置多个BeanFactoryPostProcessor实例,如果实现了Ordered接口,则可以通过order属性按序执行。
Spring预置的BeanFactoryPostProcessor有PropertyOverrideConfigurer和PropertyPlaceholderConfigurer。
FactoryBean
当开发者需要向容器请求一个真实的FactoryBean 实例(而不是由它生成的bean ),且调用ApplicationContext 的getBean()方法时,在bean 的id 之前加连字符"&" 。所以对于一个给定id为myBean 的FactoryBean ,调用容器的getBean("myBean")方法返回的是FactoryBean 的产品;而调用getBean("&myBean")方法则返回FactoryBean 实例本身。
基于注解的配置
Spring 2.5 也添加了对JSR-250 注解的支持,如@Resource 、@PostConstruct 和@PreDestroy 。Spring3.0添加了对JSR-330 注解的支持,包含在javax.inject 包下,如@Inject 、@Qualifier、@Named 和@Provided等。使用这些注解也需要在Spring 容器中注册特定的BeanPostProcessor 。
注意: 基于注解的配置注入会在基于XML 配置注入之前执行,因此同时使用两种方式,会使后面的配置覆盖前面装配的属性
<context:annotation-config/>
隐式注册的后处理器包括AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor和前面提到的RequiredAnnotationBeanPostProcessor
@Required
@Required 注解应用于bean 属性的setter 方法。
@Autowired
可以使用@Autowired 注解到传统的setter 方法中。JSR-330的@Inject 注解可以代替以上示例中Spring 的@Autowired 注解
推荐使用@Autowired 的required属性(不是@Required )注解。一方面,required 属性表示了属性对于自动装配目的不是必需的,如果它不能被自动装配,那么属性就会被忽略
@Primary
因为通过类型的自动装配可能有多个候选者,那么在选择过程中通常是需要更多控制的。达成这个目的的一种做法是Spring 的@Primary 注解。当一个依赖有多个候选者bean 时,@Primary 指定了一个优先提供的特殊bean。
@Configuration
public class MovieConfiguration {@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }@Bean
public MovieCatalog secondMovieCatalog() { ... }// ...
}
<bean class="example.SimpleMovieCatalog" primary="true">
<!-- inject any dependencies required by this bean -->
</bean><bean class="example.SimpleMovieCatalog">
<!-- inject any dependencies required by this bean -->
</bean>
@Qualifier
达成更多控制目的的另一种做法是Spring的@Qualifier 注解。开发者可以用特定的参数来关联限定符的值,缩小类型的集合匹配,为每一个参数来选择一个特定的bean。
@Qualifier可以应用于字段,构造函数参数或方法参数。
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;// ...
}
public class MovieRecommender {private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}// ...
}
<bean class="example.SimpleMovieCatalog">
<qualifier value="main"/><!-- inject any dependencies required by this bean -->
</bean><bean class="example.SimpleMovieCatalog">
<qualifier value="action"/><!-- inject any dependencies required by this bean -->
</bean><bean id="movieRecommender" class="example.MovieRecommender"/>
具有@Qualifier("main")的bean仅会注入具有相同qualifier的字段或方法参数中。
可以创建自己的自定义Qualifier注解。
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {String value();
}
public class MovieRecommender {@Autowired
@Genre("Action")
private MovieCatalog actionCatalog;private MovieCatalog comedyCatalog;
@Autowired
public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
this.comedyCatalog = comedyCatalog;
}// ...
}
可以添加<qualifier/>到bean元素。
<bean class="example.SimpleMovieCatalog">
<qualifier type="Genre" value="Action"/>
<!-- inject any dependencies required by this bean -->
</bean><bean class="example.SimpleMovieCatalog">
<qualifier type="example.Genre" value="Comedy"/>
<!-- inject any dependencies required by this bean -->
</bean>
自定义@Qualifier也可以不指定value
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {}
public class MovieRecommender {
@Autowired
@Offline
private MovieCatalog offlineCatalog;// ...
}
自定义@Qualifier也可以定义其他属性
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {String genre();
Format format();
}
public class MovieRecommender {@Autowired
@MovieQualifier(format=Format.VHS, genre="Action")
private MovieCatalog actionVhsCatalog;@Autowired
@MovieQualifier(format=Format.VHS, genre="Comedy")
private MovieCatalog comedyVhsCatalog;// ...
}
Xml配置:
<bean class="example.SimpleMovieCatalog">
<qualifier type="MovieQualifier">
<attribute key="format" value="VHS"/>
<attribute key="genre" value="Action"/>
</qualifier>
<!-- inject any dependencies required by this bean -->
</bean>
CustomAutowireConfigurer
CustomAutowireConfigurer 是一个BeanFactoryPostProcessor,允许您注册自己的自定义限定符注解类型,即使它们没有用spring的@ualifier注解进行注解
<bean id="customAutowireConfigurer"
class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>example.CustomQualifier</value>
</set>
</property>
</bean>
AutowireCandidateResolver决定自动注入候选:
@Resource
Spring 也支持使用JSR-250的@Resource 注解在字段或bean属性的setter 方法上注入
@Resource使用name属性,默认情况下Spring 解析这个值作为要注入的bean的名称
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
如果没有明确地指定name值,那么默认的名称就从字段名称或setter 方法中派生出来。
当@Resource未指定name属性时,和@Autowired类似,会优先使用primary匹配。
@PostConstruct和
@PreDestroy
ClassPath扫描和受管组件
自Spring3.0 开始,很多由Spring JavaConfig 项目提供的特性成为Spring 框架核心的一部分。这就允许开发人员使用Java 来定义bean
@Component及其延伸
- @Repository
- @Service
- @Controller
元注解
Spring 提供了很多元注解。元注解就是能被应用到另一个注解上的注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {// ....
}//Service注解将被像Component注解一样被对待。
元注解也可以被用于创建组合注解。例如, Spring MVC 的@RestController注解就是@Controller和@ResponseBody的组合 。
另外,组合注解可能从元注解中任意重新声明属性来允许用户自定义。这个在开发者只想暴露一个元注解的子集时会特别有用
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {/**
* Alias for {@link Scope#proxyMode}.
* <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
*/
@AliasFor(annotation = Scope.class)
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;}
自动检测类并注册bean定义
需要添加@ComponentScan到自己的@Configuration类上,其中的base-package元素是这两个类的公共父类包。
@Configuration
@ComponentScan(basePackages = "org.example")//或者 @ComponentScan("org.example")
public class AppConfig {
...
}
<context:component-scan base-package="org.example"/>
<context:component-scan>隐式启用了<context:annotation-config>。AutowiredAnnotationBeanPostProcessor
和 CommonAnnotationBeanPostProcessor也被隐式启用。
开发人员可以任意选择使用逗号、分号、空格分隔的列表将每个类引人父包。
使用过滤器来自定义扫描
在@ComponentScan 注解中添加includeFilters 或excludeFilters 参数(或者作为component-scan元素的include-filter或exclude-filter子元素),可以扩展扫描 。每个过滤器元素需要type 和expression 属性。
过滤类型 | 示例 | 描述 |
annotation (default) | org.example.SomeAnnotation | 匹配应用了指定注解的类 |
assignable | org.example.SomeClass | 指定类和接口的超类(接口) |
aspectj | org.example..*Service+ | Aspectj |
regex | org\.example\.Default.* | 正则表达式匹配类名称。 |
custom | org.example.MyTypeFilter | 自定义Filter的实现类 |
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
...
}
<beans>
<context:component-scan base-package="org.example">
<context:include-filter type="regex"
expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
在组件中定义Bean元数据
@Bean
@Qualifier
自动检测组件命名
组件命名由BeanNameGenerator策略执行,默认情况,Spring的原型注解(@Component
, @Repository
, @Service
, @Controller)的value属性提供了组件的名称。如果没有指定,则使用Java Bean规范生成。也可以指定
@ComponentScan的nameGenerator属性,此属性为BeanNameGenerator
的实现。
@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
...
}
<beans>
<context:component-scan base-package="org.example"
name-generator="org.example.MyNameGenerator" />
</beans>
提供自动检查组件的Scope
@Scope注解。默认为singleton,
也可以指定@ComponentScan的scopeResolver属性,此属性为ScopeMetadataResolver的实现
@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
...
}
<beans>
<context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver"/>
</beans>
JSR-330 规范注解
@Inject
@javax.inject.Inject可以代替@Autowired
@Named
and @ManagedBean
@javax.inject.Named
or javax.annotation.ManagedBean可以代替@Component
基于Java 的容器配置
Spring 中新的Java 配置支持的核心就是@Configuration注解的类和@Bean注解的方法。
AnnotationConfigApplicationContext
简单构造
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 注解的类都可以作为构造方法的输入
使用register(Class<?>…)编程式构建容器
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注解的类上加
@ComponentScan
使用scan
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.acme");
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
}
AnnotationConfigWebApplicationContext
@Bean
@Configuration
组合基于Java的配置
@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();
}
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {@Bean
public DataSource dataSource() {
// return new DataSource
}
}
Conditionally Include @Configuration
Classes or @Bean
Methods
@Profile
@Conditional
组合基于Java的和基于XML的配置
环境抽象
bean定义的profile
@Profile
@Profile定义仅在定义的profile中,bean生效。
@Configuration
@Profile("development")
public class StandaloneDataConfig {@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
支持:!,& ,| 操作符
@Profile({"p1", "p2"}) #如果p1,p2是active,则生效
@Profile({"p1", "!p2"}) #如果p1 active 或者p2不是active,则生效
Xml定义profile
<beans profile="development" ...>
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
Activating a Profile
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();
-Dspring.profiles.active="profile1,profile2"
默认profile
@Configuration
@Profile("default")
public class DefaultDataConfig {@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build();
}
}
-Dspring.profiles.default=
PropertySource抽象
PropertySource 是key-value的一个简单抽象,StandardEnvironment包含2个PropertySource对象:JVM属性(System.getProperties()),系统环境变量(System.getenv())
StandardServletEnvironment变量优先级
- 1、ServletConfig parameters (if applicable — for example, in case of a DispatcherServlet context)
- 2、ServletContext parameters (web.xml context-param entries)
- 3、JNDI environment variables (java:comp/env/ entries)
- 4、JVM system properties (-D command-line arguments)
- 5、JVM system environment (operating system environment variables)
@PropertySource
@PropertySource注解提供了一种方便的机制来将PropertySource增加到Spring的Environment中
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {@Autowired
Environment env;@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
任何@PropertySource中形如${...}的占位符,都可以被解析为Environment中的属性资源。
占位符解析
以前,占位符的值是只能对JVM系统属性或环境变量来解析的。如今,因为环境抽象已经继承到了容器中,很容易通过容器将占位柯:解析集成。这意味着开发者可以任意地配置占位符。
注册LoadTimeWeaver
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
<beans>
<context:load-time-weaver/>
</beans>
ApplicationContext的附加能力
MessageSource接口--国际化
标准和自定义事件
标准事件
- ContextRefreshedEvent
- ContextStartedEvent
- ContextStoppedEvent
- ContextClosedEvent
- RequestHandledEvent
自定义事件
继承ApplicationEvent
基于注解的事件监听器
@EventListener
异步监听器
@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
// BlackListEvent is processed in a separate thread
}
排序监听器
@EventListener
@Order(42)
public void processBlackListEvent(BlackListEvent event) {
// notify appropriate parties via notificationAddress...
}
泛型事件