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<String,Object><span style="color: rgba(0, 0, 0, 1)"> beans; </span><span style="color: rgba(0, 0, 255, 1)">public</span> <T> T getBean(String name, Class<T><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> <T> T parseAnnotation(Class<T> 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><String, Object> hashMap = <span style="color: rgba(0, 0, 255, 1)">new</span> LinkedHashMap<><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> && !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)"><</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)">></span> <span style="color: rgba(0, 0, 255, 1)"><</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)">></</span><span style="color: rgba(128, 0, 0, 1)">property</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">bean</span><span style="color: rgba(0, 0, 255, 1)">></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<? <span style="color: rgba(0, 0, 255, 1)">extends</span> Annotation><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> &&<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 进行统一实例化。