原文链接

1. 正文

@Configuration注解提供了全新的bean创建方式。最初spring通过xml配置文件初始化bean并完成依赖注入工作。从spring3.0开始,在spring framework模块中提供了这个注解,搭配@Bean等注解,可以完全不依赖xml配置,在运行时完成bean的创建和初始化工作。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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”)**

2. @Configuration注解使用

1
2
3
4
5
6
7
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component //@Component元注解
public @interface Configuration {
String value() default "";
}

我们看到源码里面,@Configuration 标记了@Component元注解,因此可以被@ComponentScan扫描并处理,在Spring容器初始化时Configuration类 会被注册到Bean容器中,最后还会实例化。

2.1 使用@Autowired/@Inject

@Autowired/@Inject的区别
  • 注解来源

@Autowired是Spring框架提供的注解,而@Inject是Java的依赖注入标准注解。

  • 依赖查找方式

@Autowired注解在装配时,会根据类型进行匹配,如果找不到精确的类型匹配,会尝试升级为构造函数注入。而@Inject注解在装配时,会根据名称进行匹配,如果找不到精确的名称匹配,会抛出异常。

  • 注入方式

@Autowired注解支持字段注入、setter方法注入和构造函数注入。而@Inject注解只支持字段注入和setter方法注入。

因为@Configuration本身也是一个@Component,因此配置类本身也会被注册到应用上下文,并且也可以使用IOC的@Autowired/@Inject等注解来注入所需bean。我们来修改配置类如下:

1
2
3
4
5
6
7
8
9
@Configuration
public class AppConfig {
@Autowired
public Environment env;
@Bean
IBean appBean(){
return new AppBean();
}
}

2.2 使用@CompomentScan

配置类也可以自己添加注解@CompomentScan,来显式扫描需使用组件。
@Configuration 使用@Component 进行原注解,因此@Configuration 类也可以被组件扫描到(特别是使用XML元素)。

1
2
3
4
5
6
7
8
@Configuration
@ComponentScan("abc.xxx")
public class AppConfig {
@Bean
IBean appBean(){
return new AppBean();
}
}

2.3 认识几个注解:@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: 表明这个注释的类是一个组件,当使用基于注释的配置和类路径扫描时,这些类被视为自动检测的候选者。

1
2
3
4
5
6
7
8
9
10
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {

@AliasFor(annotation = Component.class)
String value() default "";

}
1
2
3
4
5
6
7
8
9
10
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {

@AliasFor(annotation = Component.class)
String value() default "";

}
1
2
3
4
5
6
7
8
9
10
@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进行替换。

2.4 同@Import注解组合使用

新建一个配置类,例如数据库配置类:

1
2
3
4
5
6
7
8
9
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource(){
return new DataSource(){
...
};
}
}

然后在AppConfig中用@Import来导入配置类

1
2
3
4
5
6
7
8
9
10
@Configuration
@Import(DatabaseConfig.class)
public class AppConfig {
@Autowired
public DataSource dataSource; //注入的bean在DatabaseConfig.class中定义
@Bean
IBean appBean(){
return new AppBean();
}
}

最后执行:

1
2
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
DatabaseConfig dataSourceConfig = context.getBean(DatabaseConfig.class);

可以看到只注册了AppConfig.class,容器自动会把@Import指向的配置类初始化。

2.5 同@Profile注解组合使用

在配置类中可以申明@Profile注解,仅当满足profile条件时,才会处理配置类,也可以将@Profile注解加载配置类中的每一个@Bean来实现更细粒度的条件控制。

1
2
3
4
5
6
7
8
@Configuration
@Profile("develop")
public class DatabaseConfig {
@Bean
public DataSource dataSource(){
return new DataSource(){...};
}
}

2.6 嵌套使用@Configuration

在配置类中可以创建静态内部类,并添加@Configuration注解,这样上下文只需要注册最外面的配置类,内部的配置类会自动被加载。这样做省略了@Import,因为本身就在配置类内部,无需再特别指定了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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 的。

2.7 @Lazy初始化

默认情况下,配置类中的Bean都随着应用上下文被初始化,可以在配置类中添加@Lazy注解来延迟初始化,当然也可以在每个@Bean注解上添加,来实现更细粒度的控制。

1
2
3
4
5
6
7
8
@Configuration
@Lazy//延时加载
public class AppConfig {
@Bean
IBean appBean(){
return new AppBean();
}
}

2.8 配置类约束

  • 配置类必须为显式申明的类,而不能通过工厂类方法返回实例。允许运行时类增强。
  • 配置类不允许标记final。
  • 配置类必须全局可见(不允许定义在方法本地内部类中)
  • 嵌套配置类必须申明为static 内部类
  • @Bean方法不可以再创建新的配置类(所有实例都当做bean处理,不解析相关配置注解)

3. @ Configuration源码

3.1 ApplicationContext的refresh方法

Spring容器启动时,即ApplicationContext接口实现类的对象实例执行refresh方法时,在Bean初始化完成之前,有一个扩展点,用来操作BeanFactory,来扩展对应的功能,比如往BeanFactory中注册BeanDefintion,ApplicationContext的refresh函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//准备刷新的上下文 环境
prepareRefresh();
//初始化BeanFactory,并进行XML文件读取
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//对beanFactory进行各种功能填充
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
//激活各种beanFactory处理器
invokeBeanFactoryPostProcessors(beanFactory);
//注册拦截Bean创建的Bean处理器,这里只是注册,真正的调用实在getBean时候
registerBeanPostProcessors(beanFactory);
//为上下文初始化Message源,即不同语言的消息体,国际化处理
initMessageSource();
//初始化应用消息广播器,并放入“applicationEventMulticaster”bean中
initApplicationEventMulticaster();
//留给子类来初始化其它的Bean
onRefresh();
//在所有注册的bean中查找Listener bean,注册到消息广播器中
registerListeners();
//初始化剩下的单实例(非惰性的)
finishBeanFactoryInitialization(beanFactory);
//完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}

看到第12行,在初始化BeanFactory后,会激活各种beanFactory处理器,我们来看看invokeBeanFactoryPostProcessors方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

// Invoke BeanDefinitionRegistryPostProcessors first, if any.
// 1、首先调用BeanDefinitionRegistryPostProcessors
Set<String> processedBeans = new HashSet<>();

// beanFactory是BeanDefinitionRegistry类型
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
// 定义BeanFactoryPostProcessor
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
// 定义BeanDefinitionRegistryPostProcessor集合
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

// 循环手动注册的beanFactoryPostProcessors
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
// 如果是BeanDefinitionRegistryPostProcessor的实例话,则调用其postProcessBeanDefinitionRegistry方法,对bean进行注册操作
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
// 如果是BeanDefinitionRegistryPostProcessor类型,则直接调用其postProcessBeanDefinitionRegistry
BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
// 否则则将其当做普通的BeanFactoryPostProcessor处理,直接加入regularPostProcessors集合,以备后续处理
else {
regularPostProcessors.add(postProcessor);
}
}
//略....
}

// 2、如果不是BeanDefinitionRegistry的实例,那么直接调用其回调函数即可-->postProcessBeanFactory
else {
// Invoke factory processors registered with the context instance.
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
//略....
}

我们看看第21行,看看其实现类,如下截图,发现其中有一个ConfigurationClassPostProcessor,这个类就是本文的重点

ConfigurationClassPostProcessor这个BeanFactoryPostProcessor,来开启整个@Configuration注解的系列类的加载的,即开启基于@Configuration的类配置代替beans标签的容器配置的相关bean的加载。

img

3.2 ConfigurationClassPostProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)处理逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> 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<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
   //2.解析Java配置类
parser.parse(candidates);
   //主要校验配置类不能使用final修饰符(CGLIB代理是生成一个子类,因此原先的类不能使用final修饰)
parser.validate();

//排除已处理过的配置类
Set<ConfigurationClass> 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.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
//清空已处理的配置类
candidates.clear();
   //再次获取容器中bean定义数量 如果大于 之前获取的bean定义数量,则说明有新的bean注册到容器中,需要再次解析
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
        //新注册的bean如果也是@Configuration配置类,则添加到数据,等待解析
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());

// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}

if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}

processConfigBeanDefinitions整个方法可以大体划分为三个阶段:

  1. 从容器中获取和Configuration有关系的BeanDefinition
  2. 以该BeanDefinition为起点,进行解析操作,得到解析结果集
  3. 将解析到的结果集加载到容器中,即构造成一个BeanDefinition放到容器中待初始化
  1. 判断类是否与@Configuration有关
    在上面第1步中,有@Configuration注解的会加入到集合当中,这个判断是在ConfigurationClassUtils#checkConfigurationClassCandidate当中实现。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
    String className = beanDef.getBeanClassName();
    if (className == null || beanDef.getFactoryMethodName() != null) {
    return false;
    }
    //获取注解元数据信息
    AnnotationMetadata metadata;
    if (beanDef instanceof AnnotatedBeanDefinition &&
    className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
    metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
    }
    else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
    Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
    metadata = new StandardAnnotationMetadata(beanClass, true);
    }
    else {
    try {
    MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
    metadata = metadataReader.getAnnotationMetadata();
    }
    catch (IOException ex) {
    return false;
    }
    }
    // 查找当前注解是否是与@Configuration相关
    // 该方法还会判断该注解上的注解是否有@Configuration,一直往上寻找
    // 因为有的注解为复合注解
    if (isFullConfigurationCandidate(metadata)) {
    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
    }
    // 查找当前注解上是否有ComponentScan、Component、Import、ImportResource注解
    //如果没有则查找Bean注解,同上,一直往上查找
    else if (isLiteConfigurationCandidate(metadata)) {
    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
    }
    else {
    return false;
    }
    return true;
    }
    我们看看isFullConfigurationCandidate和isLiteConfigurationCandidate
    1
    2
    3
    public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
    return metadata.isAnnotated(Configuration.class.getName());
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
    // Do not consider an interface or an annotation...
    if (metadata.isInterface()) {
    return false;
    }

    // Any of the typical annotations found?
    for (String indicator : candidateIndicators) {
    if (metadata.isAnnotated(indicator)) {
    return true;
    }
    }

    // Finally, let's look for @Bean methods...
    try {
    return metadata.hasAnnotatedMethods(Bean.class.getName());
    }
    catch (Throwable ex) {
    if (logger.isDebugEnabled()) {
    logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
    }
    return false;
    }
    }

    private static final Set<String> candidateIndicators = new HashSet<>(8);

    static {
    candidateIndicators.add(Component.class.getName());
    candidateIndicators.add(ComponentScan.class.getName());
    candidateIndicators.add(Import.class.getName());
    candidateIndicators.add(ImportResource.class.getName());
    }
  2. 解析Java配置类parser.parse(candidates)
    parser.parse(candidates)方法最终调用processConfigurationClass方法来处理@Configuration配置类,ConfigurationClassParser. processConfigurationClass()方法实现代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
      //判断是否需要解析
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
    return;
    }
    //判断同一个配置类是否重复加载过,如果重复加载过,则合并,否则从集合中移除旧的配置类,后续逻辑将处理新的配置类
    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    if (existingClass != null) {
    if (configClass.isImported()) {
    if (existingClass.isImported()) {
    existingClass.mergeImportedBy(configClass);
    }
    // Otherwise ignore new imported config class; existing non-imported class overrides it.
    return;
    }
    else {
    // Explicit bean definition found, probably replacing an import.
    // Let's remove the old one and go with the new one.
    this.configurationClasses.remove(configClass);
    this.knownSuperclasses.values().removeIf(configClass::equals);
    }
    }

    // Recursively process the configuration class and its superclass hierarchy.
    SourceClass sourceClass = asSourceClass(configClass);
    do {
       //【真正解析配置类】
    sourceClass = doProcessConfigurationClass(configClass, sourceClass);
    }
    while (sourceClass != null);
    //再次添加到到集合中
    this.configurationClasses.put(configClass, configClass);
    }
    doProcessConfigurationClass方法主要实现从配置类中解析所有bean,包括处理内部类,父类以及各种注解ConfigurationClassParser. doProcessConfigurationClass()解析配置类逻辑如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
    throws IOException {

    //递归处理任何成员(嵌套)类
    processMemberClasses(configClass, sourceClass);

    // 处理@PropertySource注解
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
    sourceClass.getMetadata(), PropertySources.class,
    org.springframework.context.annotation.PropertySource.class)) {
    if (this.environment instanceof ConfigurableEnvironment) {
    processPropertySource(propertySource);
    }
    else {
    logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
    "]. Reason: Environment must implement ConfigurableEnvironment");
    }
    }

    // 处理@ComponentScan
    //获取@ComponentScan注解信息
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
    sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() &&
    !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
    for (AnnotationAttributes componentScan : componentScans) {

    // 按@CmponentScan注解扫描bean
    Set<BeanDefinitionHolder> scannedBeanDefinitions =
    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
    // 遍历扫描出的bean定义是否是配置类bean
    for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
    BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
    if (bdCand == null) {
    bdCand = holder.getBeanDefinition();
    }
    //若果扫描出的bean定义是配置类(含有@COnfiguration),则继续调用parse方法,内部再次调用doProcessConfigurationClas(),递归解析
    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
    parse(bdCand.getBeanClassName(), holder.getBeanName());
    }
    }
    }
    }

    //处理@Import注解
    processImports(configClass, sourceClass, getImports(sourceClass), true);

    //处理@ImportResource注解
    AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
    if (importResource != null) {
    String[] resources = importResource.getStringArray("locations");
    Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
    for (String resource : resources) {
    String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
    configClass.addImportedResource(resolvedResource, readerClass);
    }
    }

    //处理@Bean注解
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
    //将解析出的所有@Bean注解方法添加到configClass配置类信息中
    configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    //处理接口中所有添加@Bean注解的方法,内部通过遍历所有接口,解析得到@Bean注解方法,并添加到configClass配置类信息中
    processInterfaces(configClass, sourceClass);

    // 如果有父类,则返回父类,递归执行doProcessConfigurationClass()解析父类
    if (sourceClass.getMetadata().hasSuperClass()) {
    String superclass = sourceClass.getMetadata().getSuperClassName();
    if (superclass != null && !superclass.startsWith("java") &&
    !this.knownSuperclasses.containsKey(superclass)) {
    this.knownSuperclasses.put(superclass, configClass);
    // Superclass found, return its annotation metadata and recurse
    return sourceClass.getSuperClass();
    }
    }

    // No superclass -> processing is complete
    return null;
    }
    下面看两个很重要的注解@Bean和@ComponentScan的实现过程:
  • @ComponentScan注解解析过程
    1
    Set<BeanDefinitionHolder> scannedBeanDefinitions =  this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
    @ComponentScan注解解析,从上面的代码可以看出@ComponentScan注解解析通过调用ComponentScanAnnotationParser的parse方法完成,而parse()方法内部处理了一些scanner属性(过滤器设置)和basePackages包名处理,最终通过调用ClassPathBeanDefinitionScanner.doScan方法实现扫描工作。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
    componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

    Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
    boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
    scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
    BeanUtils.instantiateClass(generatorClass));

    ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
    if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
    scanner.setScopedProxyMode(scopedProxyMode);
    }
    else {
    Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
    scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
    }

    scanner.setResourcePattern(componentScan.getString("resourcePattern"));

    for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
    for (TypeFilter typeFilter : typeFiltersFor(filter)) {
    scanner.addIncludeFilter(typeFilter);
    }
    }
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
    for (TypeFilter typeFilter : typeFiltersFor(filter)) {
    scanner.addExcludeFilter(typeFilter);
    }
    }

    boolean lazyInit = componentScan.getBoolean("lazyInit");
    if (lazyInit) {
    scanner.getBeanDefinitionDefaults().setLazyInit(true);
    }

    Set<String> basePackages = new LinkedHashSet<>();
    String[] basePackagesArray = componentScan.getStringArray("basePackages");
    for (String pkg : basePackagesArray) {
    String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
    ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
    Collections.addAll(basePackages, tokenized);
    }
    for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
    basePackages.add(ClassUtils.getPackageName(clazz));
    }
    if (basePackages.isEmpty()) {
    basePackages.add(ClassUtils.getPackageName(declaringClass));
    }

    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
    @Override
    protected boolean matchClassName(String className) {
    return declaringClass.equals(className);
    }
    });
    return scanner.doScan(StringUtils.toStringArray(basePackages));
    }
    doScan扫描basePackages下所有bean
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        //根据basePackage加载包下所有java文件,并扫描出所有bean组件
    Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        //遍历beandefition
    for (BeanDefinition candidate : candidates) {
         //解析作用域Scope
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
    candidate.setScope(scopeMetadata.getScopeName());
    String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
    if (candidate instanceof AbstractBeanDefinition) {
    postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
    }
         //通用注解解析到candidate结构中,主要是处理Lazy, primary DependsOn, Role ,Description这五个注解
    if (candidate instanceof AnnotatedBeanDefinition) {
    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
    }
         //检查当前bean是否已经注册,不存在则注册
    if (checkCandidate(beanName, candidate)) {
    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    beanDefinitions.add(definitionHolder);
           // 注册到ioc容器中,主要是一些@Component组件,@Bean注解方法并没有在此处注册,beanname和beandefinition 键值对
    registerBeanDefinition(definitionHolder, this.registry);
    }
    }
    }
    return beanDefinitions;
    }
    ClassPathBeanDefinitionScanner.scanCandidateComponents实现bean定义信息扫描
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
       // @ComponentScan("com.sl.springlearning.extension")包路径处理:packageSearchPath = classpath*:com/sl/springlearning/extension/**/*.class
    String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
    resolveBasePackage(basePackage) + '/' + this.resourcePattern;
       //获取当前包下所有的class文件
    Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
    boolean traceEnabled = logger.isTraceEnabled();
    boolean debugEnabled = logger.isDebugEnabled();
    for (Resource resource : resources) {
    if (traceEnabled) {
    logger.trace("Scanning " + resource);
    }
    if (resource.isReadable()) {
    try {
    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
            //按照scanner过滤器过滤,比如配置类本身将被过滤掉,没有@Component等组件注解的类将过滤掉
    //包含@Component注解的组件将创建BeanDefinition
    if (isCandidateComponent(metadataReader)) {
    ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
    sbd.setResource(resource);
    sbd.setSource(resource);
    if (isCandidateComponent(sbd)) {
    if (debugEnabled) {
    logger.debug("Identified candidate component class: " + resource);
    }
    candidates.add(sbd);
    }
    else {
    if (debugEnabled) {
    logger.debug("Ignored because not a concrete top-level class: " + resource);
    }
    }
    } else {
    if (traceEnabled) {
    logger.trace("Ignored because not matching any filter: " + resource);
    }
    }
    }
    catch (Throwable ex) {
    throw new BeanDefinitionStoreException(
    "Failed to read candidate component class: " + resource, ex);
    }
    }
    else {
    if (traceEnabled) {
    logger.trace("Ignored because not readable: " + resource);
    }
    }
    }
    }
    catch (IOException ex) {
    throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    }
    return candidates;
    }
  • @Bean注解解析过程

retrieveBeanMethodMetadata方法实现了@Bean方法的解析,但并未将实现bean实例的创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
AnnotationMetadata original = sourceClass.getMetadata();
//获取所有@Bean注解的方法
Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
// 如果配置类中有多个@Bean注解的方法,则排序
if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
// Try reading the class file via ASM for deterministic declaration order...
// Unfortunately, the JVM's standard reflection returns methods in arbitrary
// order, even between different runs of the same application on the same JVM.
try {
AnnotationMetadata asm =
this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
if (asmMethods.size() >= beanMethods.size()) {
Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
for (MethodMetadata asmMethod : asmMethods) {
for (MethodMetadata beanMethod : beanMethods) {
if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
selectedMethods.add(beanMethod);
break;
}
}
}
if (selectedMethods.size() == beanMethods.size()) {
// All reflection-detected methods found in ASM method set -> proceed
beanMethods = selectedMethods;
}
}
}
catch (IOException ex) {
logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);
// No worries, let's continue with the reflection metadata we started with...
}
}
return beanMethods;
}
  1. 加载bean定义信息 this.reader.loadBeanDefinitions(configClasses)

回到ConfigurationClassPostProcessor#processConfigBeanDefinitions方法,当调用完parse方法之后,能得到一批ConfigurationClass集合,但是这时候只是获取到,而容器中还没有对应的注册信息,那么接下来就是对这批集合进行注册处理。

ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()方法的功能就是将之前解析出的configClasses配置类信息中所有配置相关的信息添加到spring的bean定义,主要是配置类中的@Bean注解方法,配置类@ImportResource和@Import(实现ImportBeanDefinitionRegistrar接口方式)的bean注册
ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()方法 实现逻辑如下:

1
2
3
4
5
6
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
//与@Import注解相关
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
  //将@Bean方法注册为bean
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
//将configClass中中ImportResource指定的资源注册为bean
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
//将configClass中ImportedRegistrar注册为bean
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

主要看下loadBeanDefinitionsForBeanMethod方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
//获取方法名
String methodName = metadata.getMethodName();

// Do we need to mark the bean as skipped by its condition?
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
if (configClass.skippedBeanMethods.contains(methodName)) {
return;
}

//获取@Bean注解的元数据信息
AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
Assert.state(bean != null, "No @Bean annotation attributes");

// Consider name and any aliases
//获取@Bean注解是否有name属性,如@Bean(name = "myBean")
List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
//默认bean的名称和方法名称相同,但是如果设置了name,就取name作为beanName
String beanName = (!names.isEmpty() ? names.remove(0) : methodName);

//创建一个BeanMethod的BeanDefinition
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
beanDef.setResource(configClass.getResource());
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));

//设置工厂方法
//后期Bean的实例化,getBean的时候,会判断BeanMethod是否存在FactoryMethod,如果存在,就使用反射调用工厂方法,返回工厂方法中的对象
if (metadata.isStatic()) {
// static @Bean method
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
beanDef.setFactoryMethodName(methodName);
}
else {
// instance @Bean method
beanDef.setFactoryBeanName(configClass.getBeanName());
beanDef.setUniqueFactoryMethodName(methodName);
}
//....
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}

上面只列出了核心代码,主要是构造了BeanDefinition,然后注册进容器,而BeanDefinition的一些属性则是由注解中获取,这部分代码省略。

另外,可以看到@Bean的方式构造的BeanDefinition的时候,与普通的不同,这种方式是会设置工厂方法去初始化,也就是说,AppConfig 类下的appBean方法被Spring当成一个工厂方法,也就是说这种方式与下列的初始化方式原理类似:

1
2
3
<bean id="appConfig" class="com.example.springboot.springbootdemo.bean.AppConfig"/>

<bean id="appBean" factory-bean="appConfig" factory-method="appBean"></bean>

4. 总结

处理逻辑理了一遍后,看一下ConfigurationClassPostProcessor处理器解析@configuration配置类主要过程:

  1. Spring容器初始化时注册默认后置处理器ConfigurationClassPostProcessor

  2. Spring容器初始化执行refresh()方法中调用ConfigurationClassPostProcessor

  3. ConfigurationClassPostProcessor处理器借助ConfigurationClassParser完成配置类解析

  4. ConfigurationClassParser配置内解析过程中完成嵌套的MemberClass、@PropertySource注解、@ComponentScan注解(扫描package下的所有Class并进行迭代解析,主要是@Component组件解析及注册)、@ImportResource、@Bean等处理

  5. 完成@Bean注册, @ImportResource指定bean的注册以及@Import(实现ImportBeanDefinitionRegistrar接口方式)的bean注册

  6.有@Bean注解的方法在解析的时候作为ConfigurationClass的一个属性,最后还是会转换成BeanDefinition进行处理, 而实例化的时候会作为一个工厂方法进行Bean的创建


本站由 卡卡龙 使用 Stellar 1.29.1主题创建

本站访问量 次. 本文阅读量 次.