spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理
目录
@Configuration注解使用
使用@Autowired/@Inject
使用@CompomentScan
在这里认识几个注解: @Controller, @Service, @Repository, @Component
同@Import注解组合使用
同@Profile注解组合使用
嵌套使用@Configuration
@Lazy初始化
配置类约束
@Configuration源码
ApplicationContext的refresh方法
ConfigurationClassPostProcessor
总结
正文
@Configuration注解提供了全新的bean创建方式。最初spring通过xml配置文件初始化bean并完成依赖注入工作。从spring3.0开始,在spring framework模块中提供了这个注解,搭配@Bean等注解,可以完全不依赖xml配置,在运行时完成bean的创建和初始化工作。例如:
复制代码
public interface IBean {
}
public class AppBean implements IBean{
}
//@Configuration申明了AppConfig是一个配置类
@Configuration
public class AppConfig {
//@Bean注解申明了一个bean,bean名称默认为方法名appBean
@Bean
IBean appBean(){
return new AppBean();
}
}
复制代码
默认情况下bean的名称和方法名称相同,你也可以使用name属性来指定,如@Bean(name = "myBean")
回到顶部
@Configuration注解使用
我们先来看看@Configuration 这个注解的定义
复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component //@Component元注解
public @interface Configuration {
String value() default "";
}
复制代码
我们看到源码里面,@Configuration 标记了@Component元注解,因此可以被@ComponentScan扫描并处理,在Spring容器初始化时Configuration类 会被注册到Bean容器中,最后还会实例化。
使用@Autowired/@Inject
因为@Configuration本身也是一个@Component,因此配置类本身也会被注册到应用上下文,并且也可以使用IOC的@Autowired/@Inject等注解来注入所需bean。我们来修改配置类如下:
复制代码
@Configuration
public class AppConfig {
@Autowired
public Environment env;
@Bean
IBean appBean(){
return new AppBean();
}
}
复制代码
使用@CompomentScan
配置类也可以自己添加注解@CompomentScan,来显式扫描需使用组件。
@Configuration 使用@Component 进行原注解,因此@Configuration 类也可以被组件扫描到(特别是使用XML元素)。
复制代码
@Configuration
@ComponentScan("abc.xxx")
public class AppConfig {
@Bean
IBean appBean(){
return new AppBean();
}
}
复制代码
在这里认识几个注解: @Controller, @Service, @Repository, @Component
@Controller: 表明一个注解的类是一个"Controller",也就是控制器,可以把它理解为MVC 模式的Controller 这个角色。这个注解是一个特殊的@Component,允许实现类通过类路径的扫描扫描到。它通常与@RequestMapping 注解一起使用。
@Service: 表明这个带注解的类是一个"Service",也就是服务层,可以把它理解为MVC 模式中的Service层这个角色,这个注解也是一个特殊的@Component,允许实现类通过类路径的扫描扫描到
@Repository: 表明这个注解的类是一个"Repository",团队实现了JavaEE 模式中像是作为"Data Access Object" 可能作为DAO来使用,当与 PersistenceExceptionTranslationPostProcessor 结合使用时,这样注释的类有资格获得Spring转换的目的。这个注解也是@Component 的一个特殊实现,允许实现类能够被自动扫描到
@Component: 表明这个注释的类是一个组件,当使用基于注释的配置和类路径扫描时,这些类被视为自动检测的候选者。
复制代码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(annotation = Component.class)
String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
@AliasFor(annotation = Component.class)
String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
@AliasFor(annotation = Component.class)
String value() default "";
}
复制代码
我们可以看到@Controller, @Service, @Repository这三个注解上都有@Component这个注解
也就是说,上面四个注解标记的类都能够通过@ComponentScan 扫描到,上面四个注解最大的区别就是使用的场景和语义不一样,比如你定义一个Service类想要被Spring进行管理,你应该把它定义为@Service 而不是@Controller因为我们从语义上讲,@Service更像是一个服务的类,而不是一个控制器的类,@Component通常被称作组件,它可以标注任何你没有严格予以说明的类,比如说是一个配置类,它不属于MVC模式的任何一层,这个时候你更习惯于把它定义为 @Component。@Controller,@Service,@Repository 的注解上都有@Component,所以这三个注解都可以用@Component进行替换。
同@Import注解组合使用
新建一个配置类,例如数据库配置类:
复制代码
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource(){
return new DataSource(){
...
};
}
}
复制代码
然后在AppConfig中用@Import来导入配置类
复制代码
@Configuration
@Import(DatabaseConfig.class)
public class AppConfig {
@Autowired
public DataSource dataSource; //注入的bean在DatabaseConfig.class中定义
@Bean
IBean appBean(){
return new AppBean();
}
}
复制代码
最后执行:
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
DatabaseConfig dataSourceConfig = context.getBean(DatabaseConfig.class);
可以看到只注册了AppConfig.class,容器自动会把@Import指向的配置类初始化。
同@Profile注解组合使用
在配置类中可以申明@Profile注解,仅当满足profile条件时,才会处理配置类,也可以将@Profile注解加载配置类中的每一个@Bean来实现更细粒度的条件控制。
复制代码
@Configuration
@Profile("develop")
public class DatabaseConfig {
@Bean
public DataSource dataSource(){
return new DataSource(){...};
}
}
复制代码
嵌套使用@Configuration
在配置类中可以创建静态内部类,并添加@Configuration注解,这样上下文只需要注册最外面的配置类,内部的配置类会自动被加载。这样做省略了@Import,因为本身就在配置类内部,无需再特别指定了。
复制代码
@Configuration
public class AppConfig {
@Autowired
public DataSource dataSource; //注入的bean在内部定义
@Configuration
public static class DatabaseConfig{
@Bean
DataSource dataSource(){
return new DataSource() {...};
}
}
@Bean
IBean appBean(){
return new AppBean();
}
}
复制代码
注意:任何嵌套的@Configuration 都必须是static 的。
@Lazy初始化
默认情况下,配置类中的Bean都随着应用上下文被初始化,可以在配置类中添加@Lazy注解来延迟初始化,当然也可以在每个@Bean注解上添加,来实现更细粒度的控制。
复制代码
@Configuration
@Lazy//延时加载
public class AppConfig {
@Bean
IBean appBean(){
return new AppBean();
}
}
复制代码
配置类约束
配置类必须为显式申明的类,而不能通过工厂类方法返回实例。允许运行时类增强。
配置类不允许标记final。
配置类必须全局可见(不允许定义在方法本地内部类中)
嵌套配置类必须申明为static 内部类
@Bean方法不可以再创建新的配置类(所有实例都当做bean处理,不解析相关配置注解)
回到顶部
@Configuration源码
ApplicationContext的refresh方法
在我之前的一篇文章spring5 源码深度解析-----ApplicationContext容器refresh过程中写过,Spring容器启动时,即ApplicationContext接口实现类的对象实例执行refresh方法时,在Bean初始化完成之前,有一个扩展点,用来操作BeanFactory,来扩展对应的功能,比喻往BeanFactory中注册BeanDefintion,我们回顾一下ApplicationContext的refresh函数:
复制代码
1 public void refresh() throws BeansException, IllegalStateException {
2 synchronized (this.startupShutdownMonitor) {
3 //准备刷新的上下文 环境
4 prepareRefresh();
5 //初始化BeanFactory,并进行XML文件读取
6 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
7 //对beanFactory进行各种功能填充
8 prepareBeanFactory(beanFactory);
9 try {
10 postProcessBeanFactory(beanFactory);
11 //激活各种beanFactory处理器
12 invokeBeanFactoryPostProcessors(beanFactory);
13 //注册拦截Bean创建的Bean处理器,这里只是注册,真正的调用实在getBean时候
14 registerBeanPostProcessors(beanFactory);
15 //为上下文初始化Message源,即不同语言的消息体,国际化处理
16 initMessageSource();
17 //初始化应用消息广播器,并放入“applicationEventMulticaster”bean中
18 initApplicationEventMulticaster();
19 //留给子类来初始化其它的Bean
20 onRefresh();
21 //在所有注册的bean中查找Listener bean,注册到消息广播器中
22 registerListeners();
23 //初始化剩下的单实例(非惰性的)
24 finishBeanFactoryInitialization(beanFactory);
25 //完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
26 finishRefresh();
27 }
28 catch (BeansException ex) {
29 if (logger.isWarnEnabled()) {
30 logger.warn("Exception encountered during context initialization - " +
31 "cancelling refresh attempt: " + ex);
32 }
33 destroyBeans();
34 cancelRefresh(ex);
35 throw ex;
36 }
37 finally {
38 resetCommonCaches();
39 }
40 }
41 }
复制代码
看到第12行,在初始化BeanFactory后,会激活各种beanFactory处理器,我们来看看invokeBeanFactoryPostProcessors方法
复制代码
1 public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) {
2
3 // Invoke BeanDefinitionRegistryPostProcessors first, if any.
4 // 1、首先调用BeanDefinitionRegistryPostProcessors
5 Set processedBeans = new HashSet<>();
6
7 // beanFactory是BeanDefinitionRegistry类型
8 if (beanFactory instanceof BeanDefinitionRegistry) {
9 BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
10 // 定义BeanFactoryPostProcessor
11 List regularPostProcessors = new ArrayList<>();
12 // 定义BeanDefinitionRegistryPostProcessor集合
13 List registryProcessors = new ArrayList<>();
14
15 // 循环手动注册的beanFactoryPostProcessors
16 for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
17 // 如果是BeanDefinitionRegistryPostProcessor的实例话,则调用其postProcessBeanDefinitionRegistry方法,对bean进行注册操作
18 if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
19 // 如果是BeanDefinitionRegistryPostProcessor类型,则直接调用其postProcessBeanDefinitionRegistry
20 BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
21 registryProcessor.postProcessBeanDefinitionRegistry(registry);
22 registryProcessors.add(registryProcessor);
23 }
24 // 否则则将其当做普通的BeanFactoryPostProcessor处理,直接加入regularPostProcessors集合,以备后续处理
25 else {
26 regularPostProcessors.add(postProcessor);
27 }
28 }
29 //略....
30 }
31
32 // 2、如果不是BeanDefinitionRegistry的实例,那么直接调用其回调函数即可-->postProcessBeanFactory
33 else {
34 // Invoke factory processors registered with the context instance.
35 invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
36 }
37 //略....
38 }
复制代码
我们看看第21行,看看其实现类,如下截图,发现其中有一个ConfigurationClassPostProcessor,这个类就是本章的重点
ConfigurationClassPostProcessor这个BeanFactoryPostProcessor,来开启整个@Configuration注解的系列类的加载的,即开启基于@Configuration的类配置代替beans标签的容器配置的相关bean的加载。
ConfigurationClassPostProcessor
复制代码
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
//生成唯一标识,用于重复处理验证
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
//解析Java类配置bean
processConfigBeanDefinitions(registry);
}
复制代码
processConfigBeanDefinitions(registry)处理逻辑:
复制代码
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List configCandidates = new ArrayList<>();
//所有已经注册的bean
String[] candidateNames = registry.getBeanDefinitionNames();
//遍历bean定义信息
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
//1.如果当前的bean是Javabean配置类(含有@Configuration注解的类),则加入到集合configCandidates中,
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Return immediately if no @Configuration classes were found
// 没有@Configuration注解的类,直接退出
if (configCandidates.isEmpty()) {
return;
}
// 多个Java配置类,按@Ordered注解排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
//初始化一个ConfigurationClassParser解析器,可以解析@Congiguration配置类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set candidates = new LinkedHashSet<>(configCandidates);
Set alreadyParsed = new HashSet<>(configCandidates.size());
do {
//2.解析Java配置类
parser.parse(candidates);
//主要校验配置类不能使用final修饰符(CGLIB代理是生成一个子类,因此原先的类不能使用final修饰)
parser.validate();
//排除已处理过的配置类
Set configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
//3.加载bean定义信息,主要实现将@bean @Configuration @Import @ImportResource @ImportRegistrar注册为bean
this.reader.loadBeanDefinit