Spring之@Configuration源码解析
@Configuration源码解析
一、官方说明
看下一个JavaDoc中对@Configuration的说明:
1.1、@Bean注解添加Bean
Indicates that a class declares one or more @Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime, for example:
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
// instantiate, configure and return bean ...
}
}
1.2、注册一个配置类
1.2.1、通过Java进行注册配置类
@Configuration classes are typically bootstrapped using either AnnotationConfigApplicationContext or its web-capable variant, AnnotationConfigWebApplicationContext. A simple example with the former follows:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class);
ctx.refresh();
MyBean myBean = ctx.getBean(MyBean.class);
// use myBean ...
See the AnnotationConfigApplicationContext javadocs for further details, and see AnnotationConfigWebApplicationContext for web configuration instructions in a Servlet container.
1.2.2、通过XML进行注册配置类
As an alternative to registering @Configuration classes directly against an AnnotationConfigApplicationContext, @Configuration classes may be declared as normal <bean> definitions within Spring XML files:
<beans>
<context:annotation-config/>
<bean class="com.acme.AppConfig"/>
</beans>
In the example above, <context:annotation-config/> is required in order to enable ConfigurationClassPostProcessor and other annotation-related post processors that facilitate handling @Configuration classes
只有开启了context:annotation-config/,才能够启用ConfigurationClassPostProcessor和其他相关注解的后置处理器来处理@Configuration标注的类。
那么也就是说,如果不添加的话,可能就会对配置类的解析造成响应的问题。
1.3、添加扫描包逻辑
@Configuration is meta-annotated with @Component, therefore @Configuration classes are candidates for component scanning (typically using Spring XML's <context:component-scan/> element) and therefore may also take advantage of @Autowired/@Inject like any regular @Component. In particular, if a single constructor is present autowiring semantics will be applied transparently for that constructor:
@Configuration
public class AppConfig {
private final SomeBean someBean;
public AppConfig(SomeBean someBean) {
this.someBean = someBean;
}
// @Bean definition using "SomeBean"
}
@Configuration classes may not only be bootstrapped using component scanning, but may also themselves configure component scanning using the @ComponentScan annotation:
@Configuration
@ComponentScan("com.acme.app.services")
public class AppConfig {
// various @Bean definitions ...
}
See the @ComponentScan javadocs for details.
1.4、使用环境变量
Externalized values may be looked up by injecting the Spring org.springframework.core.env.Environment into a @Configuration class — for example, using the @Autowired annotation:
@Configuration
public class AppConfig {
@Autowired Environment env;
@Bean
public MyBean myBean() {
MyBean myBean = new MyBean();
myBean.setName(env.getProperty("bean.name"));
return myBean;
}
}
Properties resolved through the Environment reside in one or more "property source" objects, and @Configuration classes may contribute property sources to the Environment object using the @PropertySource annotation:
@Configuration
@PropertySource("classpath:/com/acme/app.properties")
public class AppConfig {
@Inject
Environment env;
@Bean
public MyBean myBean() {
return new MyBean(env.getProperty("bean.name"));
}
}
See the Environment and @PropertySource javadocs for further details.
1.5、获取得到@Value的值
Externalized values may be injected into @Configuration classes using the @Value annotation:
@Configuration
@PropertySource("classpath:/com/acme/app.properties")
public class AppConfig {
@Value("${bean.name}")
String beanName;
@Bean
public MyBean myBean() {
return new MyBean(beanName);
}
}
This approach is often used in conjunction with Spring's PropertySourcesPlaceholderConfigurer that can be enabled automatically in XML configuration via context:property-placeholder/ or explicitly in a @Configuration class via a dedicated static @Bean method (see "a note on BeanFactoryPostProcessor-returning @Bean methods" of @Bean's javadocs for details). Note, however, that explicit registration of a PropertySourcesPlaceholderConfigurer via a static @Bean method is typically only required if you need to customize configuration such as the placeholder syntax, etc. Specifically, if no bean post-processor (such as a PropertySourcesPlaceholderConfigurer) has registered an embedded value resolver for the ApplicationContext, Spring will register a default embedded value resolver which resolves placeholders against property sources registered in the Environment. See the section below on composing @Configuration classes with Spring XML using @ImportResource; see the @Value javadocs; and see the @Bean javadocs for details on working with BeanFactoryPostProcessor types such as PropertySourcesPlaceholderConfigurer.
1.6、添加@Import注解导入其他类
@Configuration classes may be composed using the @Import annotation, similar to the way that
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource() {
// instantiate, configure and return DataSource
}
}
@Configuration
@Import(DatabaseConfig.class)
public class AppConfig {
private final DatabaseConfig dataConfig;
public AppConfig(DatabaseConfig dataConfig) {
this.dataConfig = dataConfig;
}
@Bean
public MyBean myBean() {
// reference the dataSource() bean method
return new MyBean(dataConfig.dataSource());
}
}
Now both AppConfig and the imported DatabaseConfig can be bootstrapped by registering only AppConfig against the Spring context:new AnnotationConfigApplicationContext(AppConfig.class);
1.7、添加@Profile激活指定环境
@Configuration classes may be marked with the @Profile annotation to indicate they should be processed only if a given profile or profiles are active:
如:
@Profile("development")
@Configuration
public class EmbeddedDatabaseConfig {
@Bean
public DataSource dataSource() {
// instantiate, configure and return embedded DataSource
}
}
@Profile("production")
@Configuration
public class ProductionDatabaseConfig {
@Bean
public DataSource dataSource() {
// instantiate, configure and return production DataSource
}
}
Alternatively, you may also declare profile conditions at the @Bean method level — for example, for alternative bean variants within the same configuration class:
@Configuration
public class ProfileDatabaseConfig {
@Bean("dataSource")
@Profile("development")
public DataSource embeddedDatabase() { ... }
@Bean("dataSource")
@Profile("production")
public DataSource productionDatabase() { ... }
}
See the @Profile and org.springframework.core.env.Environment javadocs for further details.
1.8、添加@ImportResource导入配置文件
As mentioned above, @Configuration classes may be declared as regular Spring
@Configuration
@ImportResource("classpath:/com/acme/database-config.xml")
public class AppConfig {
@Inject
DataSource dataSource; // from XML
@Bean
public MyBean myBean() {
// inject the XML-defined dataSource bean
return new MyBean(this.dataSource);
}
}
1.9、内置配置类
这种事情使用的非常少。
@Configuration classes may be nested within one another as follows:
@Configuration
public class AppConfig {
@Inject
DataSource dataSource;
@Bean
public MyBean myBean() {
return new MyBean(dataSource);
}
@Configuration
static class DatabaseConfig {
@Bean
DataSource dataSource() {
return new EmbeddedDatabaseBuilder().build();
}
}
}
When bootstrapping such an arrangement, only AppConfig need be registered against the application context. By virtue of being a nested @Configuration class, DatabaseConfig will be registered automatically. This avoids the need to use an @Import annotation when the relationship between AppConfig and DatabaseConfig is already implicitly clear.
Note also that nested @Configuration classes can be used to good effect with the @Profile annotation to provide two options of the same bean to the enclosing @Configuration class.
1.10、给@Bean配置懒加载
By default, @Bean methods will be eagerly instantiated at container bootstrap time. To avoid this, @Configuration may be used in conjunction with the @Lazy annotation to indicate that all @Bean methods declared within the class are by default lazily initialized. Note that @Lazy may be used on individual @Bean methods as well.
1.11、测试配置类
The Spring TestContext framework available in the spring-test module provides the @ContextConfiguration annotation which can accept an array of component class references — typically @Configuration or @Component classes.
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {AppConfig.class, DatabaseConfig.class})
public class MyTests {
@Autowired
MyBean myBean;
@Autowired
DataSource dataSource;
@Test
public void test() {
// assertions against myBean ...
}
}
1.12、使用@Enable注解
Spring features such as asynchronous method execution, scheduled task execution, annotation driven transaction management, and even Spring MVC can be enabled and configured from @Configuration classes using their respective "@Enable" annotations. See @EnableAsync, @EnableScheduling, @EnableTransactionManagement, @EnableAspectJAutoProxy, and @EnableWebMvc for details.
1.13、@Configuration核心
1、Configuration classes must be provided as classes (i.e. not as instances returned from factory methods), allowing for runtime enhancements through a generated subclass.
2、Configuration classes must be non-final (allowing for subclasses at runtime), unless the proxyBeanMethods flag is set to false in which case no runtime-generated subclass is necessary.
3、Configuration classes must be non-local (i.e. may not be declared within a method).
4、Any nested configuration classes must be declared as static.
5、@Bean methods may not in turn create further configuration classes (any such instances will be treated as regular beans, with their configuration annotations remaining undetected).
上面这一段才是真正的核心内容。讲解了@Configuration注解标注的类该如何来进行使用。
二、@Configuration注解标注类的解析
首先看一下@Configuration注解的组成结构,如下所示:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
/**
* @since 5.2
*/
boolean proxyBeanMethods() default true;
}
表示的当前类也是一个@Component,但是比较特殊。
从上面可以看到有很多种注册@Configuration的方式,下面以两种情况为例:
- 1、手动注册;
- 2、扫描;
手动注册:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfiguration.class);
扫描:
@Configuration
public class TestConfiguration {}
2.1、@Configuration注解源码解析过程
1、生成BeanDefinition过程
一定要注意这里的BeanDefinition的类型为AnnotatedGenericBeanDefinition,对应的继承体系结构图如下所示:
继续向下解析:
注册到beanDefinitionMap中去进行保存。
2、解析BeanDefinition过程
直接来到org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry方法中来,看看如何来对BD来进行解析的。
1、首先获取得到所有的额BeanName,然后遍历;
2、判断每个BeanDefinition中的attributes属性是否有值;
3、找到BeanDefinition对应的类上是否有@Configuration,如果有,获取得到proxyBeanMethods属性的值,如果proxyBeanMethods=true,在attributes属性中存入FULL。
下面就该来对BeanDefinition中attributes属性中为full的来进行解析。
直接来到org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanFactory中
既然设置了代理类,那么在创建代理类对象之后,在执行代理方法的时候,会指定到指定的逻辑。那么就看下设置的代理逻辑是什么:
对于BeanFactoryAware来说
但是一般的配置类不会来实现这个接口,所以这个回调接口可以省略。
对于NoOp.INSTANCE来说,相当于是什么都不做,充当着一个占位符的作用。
对于BeanMethodInterceptor来说,在执行对应的方法的时候会执行到对应的方法中来:
3、代理类对象执行方法执行到拦截方法来进行操作
代理类在执行userService的时候,首先利用ThreadLocal线程对象记录一下当前执行的方法是userService,然后执行到userService的方法的时候,发现又需要执行orderService方法,那么将判断当前正在执行的方法(orderService)和上下文中保存的是否是一样的来判断是否需要从容器中来获取得到对象
注意事项
如果是这样子的话,那么获取到的orderService将不会是同一个orderService对象。