Java注解及其原理以及分析spring注解解析源码

注解的定义

注解是那些插入到源代码中,使用其他工具可以对其进行处理的标签

注解不会改变程序的编译方式:Java 编译器对于包含注解和不包含注解的代码会生成相同的虚拟机指令。

在 Java 中,注解是被当做一个修饰符来使用的(修饰符:如 public、private)

注解的常用用法:1. 附属文件的自动生成,例如 bean 信息类。 2. 测试、日志、事务等代码的自动生成。

单元测试例子:

import org.junit.Test;

public class SomeTest {

@Test
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> test(){
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TODO</span>

}

}

以上是我们常见的代码。以前不了解的时候,都自然而然的认为是 @Test 让我们的代码拥有单元测试的能力,实际上:@Test 注解自身并不会做任何事情,它需要工具支持才有用。例如,当测试一个类的时候,JUnit4 测试工具会去调用所有标识为 @Test 的方法。

这也就解释了当我们要引入 Test 的 Class 时提示:

所以我们可以认为:注解 = 注解定义 + 工具支持

进入 @Test,查看定义

package org.junit;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Test {
Class
<? extends Throwable> expected() default Test.None.class;

</span><span style="color: rgba(0, 0, 255, 1)">long</span> timeout() <span style="color: rgba(0, 0, 255, 1)">default</span> 0L<span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">class</span> None <span style="color: rgba(0, 0, 255, 1)">extends</span><span style="color: rgba(0, 0, 0, 1)"> Throwable {
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> <span style="color: rgba(0, 0, 255, 1)">long</span> serialVersionUID = 1L<span style="color: rgba(0, 0, 0, 1)">;

    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> None() {
    }
}

}

可以看到 @Test 注解被两个注解 @Retention 和 @Target 给注解了,这两个注解叫做元注解(一共四个:@Retention、@Target、@Document、@Inherited)

1. @Retention(指明这个注解可以保留多久,一般都为RUNTIME

RetentionPolicy.SOURCE            保留在源文件里
RetentionPolicy.CLASS             保留在 class 文件里,但是虚拟机不需要将它们载入
RetentionPolicy.RUNTIME           保留在 class 文件里,并由虚拟机将它们载入,通过反射可以获取到它们。

2. @Target(指明这个注解的使用范围)

ElementType.TYPE                 用于类和接口
ElementType.FIELD                用于成员域
ElementType.METHOD               用于方法
ElementType.PARAMETER            用于方法或者构造器里的参数
ElementType.CONSTRUCTOR          用于构造器
ElementType.LOCAL_VARIABLE       用于局部变量
ElementType.ANNOTATION_TYPE      用于注解类型声明
ElementType.PACKAGE              用于包
ElementType.TYPE_PARAMETER       类型参数,1.8 新增
ElementType.TYPE_USE             类型用法,1.8 新增

3. @Document为例如 Javadoc 这样的归档工具提供了一些提示。

4. @Inherited只能应用于对类的注解。指明当这个注解应用于一个类 A 的时候,能够自动被类 A 的子类继承。

注解可以在运行时处理

也可以在源码级别上处理

也可以在字节码级别上进行处理

注解的使用

新建一个实体类 Book

public class Book {
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> String name;

</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Book() {
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Book(String name) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.name =<span style="color: rgba(0, 0, 0, 1)"> name;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String getName() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> name;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setName(String name) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.name =<span style="color: rgba(0, 0, 0, 1)"> name;
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String toString() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> "Book{" +
            "name='" + name + '\'' +
            '}'<span style="color: rgba(0, 0, 0, 1)">;
}

}

建立一个在类上用的注解

package com.demo.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TypeAnnotation {

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果注解里只有一个属性,就可以以这种固定的写法。使用的时候可以省略名称如:@TypeAnnotation("")</span>
String value() <span style="color: rgba(0, 0, 255, 1)">default</span> ""<span style="color: rgba(0, 0, 0, 1)">;

}

建立一个在方法上用的注解

package com.demo.annotation;

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodAnnotation {

String value() </span><span style="color: rgba(0, 0, 255, 1)">default</span> ""<span style="color: rgba(0, 0, 0, 1)">;

}

假装建立一个“配置类”

package com.demo.annotation;

import com.demo.tools.Book;

import java.util.LinkedHashMap;

@TypeAnnotation
public class BookConfig {

</span><span style="color: rgba(0, 0, 255, 1)">private</span> LinkedHashMap&lt;String,Object&gt;<span style="color: rgba(0, 0, 0, 1)"> beans;

</span><span style="color: rgba(0, 0, 255, 1)">public</span> &lt;T&gt; T getBean(String name, Class&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> clazz){
    Object o </span>=<span style="color: rgba(0, 0, 0, 1)"> beans.get(name);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (T) o;
}


@MethodAnnotation
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Book book(){
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Book("QQQ"<span style="color: rgba(0, 0, 0, 1)">);
}

@MethodAnnotation(</span>"zzz"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Book book2(){
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Book("ZZZ"<span style="color: rgba(0, 0, 0, 1)">);
}

}

 

假装这是 spring 容器的初始化过程

package com.demo.annotation;

import com.demo.tools.Book;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;

public class Main {
public static void main(String[] args) throws Exception {
BookConfig config
= parseAnnotation(BookConfig.class);
Book book
= config.getBean("book", Book.class);
System.out.println(book);
book
= config.getBean("zzz", Book.class);
System.out.println(book);
book
= config.getBean("book2", Book.class);
System.out.println(book);
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> &lt;T&gt; T parseAnnotation(Class&lt;T&gt; clazz) <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!clazz.isAnnotationPresent(TypeAnnotation.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)){
        </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
    }
    T instance </span>=<span style="color: rgba(0, 0, 0, 1)"> clazz.newInstance();
    LinkedHashMap</span>&lt;String, Object&gt; hashMap = <span style="color: rgba(0, 0, 255, 1)">new</span> LinkedHashMap&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
    Field beans </span>= clazz.getDeclaredField("beans"<span style="color: rgba(0, 0, 0, 1)">);

    Method[] methods </span>=<span style="color: rgba(0, 0, 0, 1)"> clazz.getDeclaredMethods();
    </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (Method m : methods){
        </span><span style="color: rgba(0, 0, 255, 1)">if</span> (m.isAnnotationPresent(MethodAnnotation.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)){
            Object o </span>=<span style="color: rgba(0, 0, 0, 1)"> m.invoke(instance);
            MethodAnnotation t </span>= m.getAnnotation(MethodAnnotation.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
            String name;
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 注解有值用注解值作为name,否则用方法名字作为name</span>
            <span style="color: rgba(0, 0, 255, 1)">if</span> (t.value() != <span style="color: rgba(0, 0, 255, 1)">null</span> &amp;&amp; !t.value().equals(""<span style="color: rgba(0, 0, 0, 1)">)){
                name </span>=<span style="color: rgba(0, 0, 0, 1)"> t.value();
            }</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
                name </span>=<span style="color: rgba(0, 0, 0, 1)"> m.getName();
            }
            hashMap.put(name, o);
        }
    }
    beans.setAccessible(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
    beans.set(instance, hashMap);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> instance;
}

}

 

输出:

 

思路就是:用反射创建新类,利用【java.lang.reflect.Method#getAnnotation】方法获取注解类,然后获取注解里的值,然后进行操作即可。

Spring 中的注解

引入 Spring-Context 的依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>

1. 在 resource 路径下新建一个 beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">bean </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="book"</span><span style="color: rgba(255, 0, 0, 1)"> class</span><span style="color: rgba(0, 0, 255, 1)">="com.demo.tools.Book"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="name"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="AAA"</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">property</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

</beans>

这个是原始的写法,先测试这个也是为了对比

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext applicationContext
= new ClassPathXmlApplicationContext("beans.xml");
Book book
= applicationContext.getBean(Book.class);
System.out.println(book);
}
}

运行输出

 

2. 建立一个配置类(配置类的作用等同于 xml 配置文件)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BookConfig {
@Bean
public Book book(){
return new Book("BBB");
}

@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> User user(){
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> User("KKK"<span style="color: rgba(0, 0, 0, 1)">);
}

}

运行

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(BookConfig.class);
Book book
= context.getBean(Book.class);
System.out.println(book);
}
}

输出

 

这里不打算探究 spring 有多少种注解以及使用方法,只探究注解是怎样运行以取代 xml 配置的

 

先来看下容器的创建过程,这其中就包括了注解部分

进入 AnnotationConfigApplicationContext 构造方法

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
    this();
    register(componentClasses);
    refresh();}

A:this()

public AnnotationConfigApplicationContext() {
    // 这是一个替代 ClassPathBeanDefinitionScanner 的注释解决方案,但只针对显式注册的类。
    this.reader = new AnnotatedBeanDefinitionReader(this);
    // 一个 bean 定义扫描器,它检测类路径上的 bean 候选,将相应的 bean 定义注册到给定的注册表 (BeanFactory 或 ApplicationContext)
    this.scanner = new ClassPathBeanDefinitionScanner(this);}

B:register(componentClasses);

public void register(Class<?>... componentClasses) {
    Assert.notEmpty(componentClasses, "At least one component class must be specified");
    this.reader.register(componentClasses);
}

public void register(Class<?>... componentClasses) {
for (Class<?> componentClass : componentClasses) {
registerBean(componentClass);
}
}

public void registerBean(Class<?> beanClass) {
doRegisterBean(beanClass,
null, null, null, null);
}

// 根据给定的 class 注册 bean,从类声明的注解派生其元数据
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
@Nullable Class
<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
@Nullable BeanDefinitionCustomizer[] customizers) {

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> new一个AnnotatedGenericBeanDefinition,其中包含bean的class和元数据</span>
AnnotatedGenericBeanDefinition abd = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> AnnotatedGenericBeanDefinition(beanClass);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.conditionEvaluator.shouldSkip(abd.getMetadata())) {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
}

abd.setInstanceSupplier(supplier);
ScopeMetadata scopeMetadata </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName </span>= (name != <span style="color: rgba(0, 0, 255, 1)">null</span> ? name : <span style="color: rgba(0, 0, 255, 1)">this</span>.beanNameGenerator.generateBeanName(abd, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.registry));

AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (qualifiers != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(0, 0, 255, 1)">for</span> (Class&lt;? <span style="color: rgba(0, 0, 255, 1)">extends</span> Annotation&gt;<span style="color: rgba(0, 0, 0, 1)"> qualifier : qualifiers) {
        </span><span style="color: rgba(0, 0, 255, 1)">if</span> (Primary.<span style="color: rgba(0, 0, 255, 1)">class</span> ==<span style="color: rgba(0, 0, 0, 1)"> qualifier) {
            abd.setPrimary(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
        }
        </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (Lazy.<span style="color: rgba(0, 0, 255, 1)">class</span> ==<span style="color: rgba(0, 0, 0, 1)"> qualifier) {
            abd.setLazyInit(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
        }
        </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
            abd.addQualifier(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> AutowireCandidateQualifier(qualifier));
        }
    }
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (customizers != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (BeanDefinitionCustomizer customizer : customizers) {
        customizer.customize(abd);
    }
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 包含名称和别名的bean定义的Holder</span>
BeanDefinitionHolder definitionHolder = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> BeanDefinitionHolder(abd, beanName);
definitionHolder </span>= AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.registry);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 注册bean定义</span>
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.registry);

}

C:refresh();

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 刷新前的准备
        prepareRefresh();
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取bean工厂【ConfigurableListableBeanFactory】</span>
    ConfigurableListableBeanFactory beanFactory =<span style="color: rgba(0, 0, 0, 1)"> obtainFreshBeanFactory();

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 对bean工厂的预设置,比如配置类加载器和后置处理器等等。(后置处理器能在bean初始化前后做一些工作)</span>

prepareBeanFactory(beanFactory);

    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 由子类实现的bean工厂的后置处理</span>

postProcessBeanFactory(beanFactory);

        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 执行工厂的后置处理器</span>

invokeBeanFactoryPostProcessors(beanFactory);

        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 注册bean的后置处理器,用来拦截bean的创建过程</span>

registerBeanPostProcessors(beanFactory);

        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Initialize message source for this context.</span>

initMessageSource();

        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 初始化事件派发器</span>

initApplicationEventMulticaster();

        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 由子类实现,当容器刷新的时候,可以做一些额外的事情</span>

onRefresh();

        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 检查并注册容器中的监听器</span>

registerListeners();

        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 初始化剩下的所有单实例bean</span>

finishBeanFactoryInitialization(beanFactory);

        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Last step: publish corresponding event.</span>

finishRefresh();
}

    </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (BeansException ex) {
        </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (logger.isWarnEnabled()) {
            logger.warn(</span>"Exception encountered during context initialization - " +
                    "cancelling refresh attempt: " +<span style="color: rgba(0, 0, 0, 1)"> ex);
        }

        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Destroy already created singletons to avoid dangling resources.</span>

destroyBeans();

        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Reset 'active' flag.</span>

cancelRefresh(ex);

        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Propagate exception to caller.</span>
        <span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)"> ex;
    }

    </span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)"> {
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Reset common introspection caches in Spring's core, since we
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> might not ever need metadata for singleton beans anymore...</span>

resetCommonCaches();
}
}
}

DEBUG 开始

打断点

 当获取 Bean 工厂之后,出现了 bookConfig 的 bean 定义(当前出现的 6 个 bean 定义均是在 B:register 阶段完成的),但是还没有 book 的 bean 定义,继续往下走

 进入工厂后置处理器【org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
</strong></span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (beanFactory.getTempClassLoader() == <span style="color: rgba(0, 0, 255, 1)">null</span> &amp;&amp;<span style="color: rgba(0, 0, 0, 1)"> beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
    beanFactory.addBeanPostProcessor(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> LoadTimeWeaverAwareProcessor(beanFactory));
    beanFactory.setTempClassLoader(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}

}

进入第一行方法【org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List<org.springframework.beans.factory.config.BeanFactoryPostProcessor>)】

 我发现过了invokeBeanDefinitionRegistryPostProcessors方法,bean 工厂的 beanDefinitionMap 的内容发生了变化,那么进入这个方法【org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors

private static void invokeBeanDefinitionRegistryPostProcessors(
        Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
</span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
    <strong>postProcessor.postProcessBeanDefinitionRegistry(registry);</strong>
}

}

进入【org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

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);
<strong>processConfigBeanDefinitions(registry);</strong>

}

进入最后一行代码【org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

 

 此时可以看的已有的BeanDefinitionNames,下面进入循环

只有当循环到 bookConfig 时,才进入 else if 并添加。继续走,通过下面的注释我们可以知道上面的逻辑是来寻找 @Configuration 标记的类,如果没有即返回。

然后开始解析所有配置类

 

进入parser.parse(candidates);方法【org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)】,有个

 

进入 parse 方法后最终进入【org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass

进入 doProcessConfigurationClass 方法【org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

这里就是我们要找的读取解析注解的方法了。可以看到里面写了对各种注解的处理方式(比如:@ComponentScan、@Import 等等),包括对配置类里面定义的 bean 的处理。

针对本案例,可以看到专门检索配置类里面被 @Bean 标记的 Method 的方法【org.springframework.context.annotation.ConfigurationClassParser#retrieveBeanMethodMetadata

private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
    AnnotationMetadata original = sourceClass.getMetadata();
    Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
    // 标记 @Bean 的方法超过 1 个,有下面说的顺序问题,所以要用 asm;否则直接返回
    if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
        // 尝试通过 ASM 读取.class 文件以确定声明顺序
        // 不幸的是,JVM 的标准反射以任意顺序返回方法,甚至在相同 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;
}

获取到之后即添加到 ConfigurationClass 的 beanMethods 集合

 

看到这里,可以得知注解的作用:标记作用

以本例来看:无非就是标记了一个 @Bean 注解,之后扫描工具能够解析并得到 MethodMetadata 对象,再到后面由 Spring 进行统一实例化。