smalllove 2019-10-23
注解是那些插入到源代码中,使用其他工具可以对其进行处理的标签。
注解不会改变程序的编译方式:Java编译器对于包含注解和不包含注解的代码会生成相同的虚拟机指令。
在Java中,注解是被当做一个修饰符来使用的(修饰符:如public、private)
注解的常用用法:1. 附属文件的自动生成,例如bean信息类。 2. 测试、日志、事务等代码的自动生成。
单元测试例子:
import org.junit.Test;
public class SomeTest {
@Test
public void test(){
// TODO
}
}以上是我们常见的代码。以前不了解的时候,都自然而然的认为是@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;
long timeout() default 0L;
public static class None extends Throwable {
private static final long serialVersionUID = 1L;
private 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 {
private String name;
public Book() {
}
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Book{" +
"name=‘" + name + ‘\‘‘ +
‘}‘;
}
}建立一个在类上用的注解
package com.demo.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TypeAnnotation {
// 如果注解里只有一个属性,就可以以这种固定的写法。使用的时候可以省略名称如:@TypeAnnotation("")
String value() default "";
}建立一个在方法上用的注解
package com.demo.annotation;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodAnnotation {
String value() default "";
}假装建立一个“配置类”
package com.demo.annotation;
import com.demo.tools.Book;
import java.util.LinkedHashMap;
@TypeAnnotation
public class BookConfig {
private LinkedHashMap<String,Object> beans;
public <T> T getBean(String name, Class<T> clazz){
Object o = beans.get(name);
return (T) o;
}
@MethodAnnotation
public Book book(){
return new Book("QQQ");
}
@MethodAnnotation("zzz")
public Book book2(){
return new Book("ZZZ");
}
}假装这是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);
}
public static <T> T parseAnnotation(Class<T> clazz) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
if (!clazz.isAnnotationPresent(TypeAnnotation.class)){
return null;
}
T instance = clazz.newInstance();
LinkedHashMap<String, Object> hashMap = new LinkedHashMap<>();
Field beans = clazz.getDeclaredField("beans");
Method[] methods = clazz.getDeclaredMethods();
for (Method m : methods){
if (m.isAnnotationPresent(MethodAnnotation.class)){
Object o = m.invoke(instance);
MethodAnnotation t = m.getAnnotation(MethodAnnotation.class);
String name;
// 注解有值用注解值作为name,否则用方法名字作为name
if (t.value() != null && !t.value().equals("")){
name = t.value();
}else{
name = m.getName();
}
hashMap.put(name, o);
}
}
beans.setAccessible(true);
beans.set(instance, hashMap);
return instance;
}
}输出:

思路就是:用反射创建新类,利用【java.lang.reflect.Method#getAnnotation】方法获取注解类,然后获取注解里的值,然后进行操作即可。
引入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">
<bean id="book" class="com.demo.tools.Book">
<property name="name" value="AAA"></property>
</bean>
</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");
}
}运行
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);
}
}输出

先来看下容器的创建过程,这其中就包括了注解部分
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}public AnnotationConfigApplicationContext() {
// 这是一个替代ClassPathBeanDefinitionScanner的注释解决方案,但只针对显式注册的类。
this.reader = new AnnotatedBeanDefinitionReader(this);
// 一个bean定义扫描器,它检测类路径上的bean候选,将相应的bean定义注册到给定的注册表(BeanFactory或ApplicationContext)
this.scanner = new ClassPathBeanDefinitionScanner(this);
}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) {
// new一个AnnotatedGenericBeanDefinition,其中包含bean的class和元数据
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
abd.setInstanceSupplier(supplier);
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
if (customizers != null) {
for (BeanDefinitionCustomizer customizer : customizers) {
customizer.customize(abd);
}
}
// 包含名称和别名的bean定义的Holder
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
// 注册bean定义
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 刷新前的准备
prepareRefresh();
// 获取bean工厂【ConfigurableListableBeanFactory】
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 对bean工厂的预设置,比如配置类加载器和后置处理器等等。(后置处理器能在bean初始化前后做一些工作)
prepareBeanFactory(beanFactory);
try {
// 由子类实现的bean工厂的后置处理
postProcessBeanFactory(beanFactory);
// 执行工厂的后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 注册bean的后置处理器,用来拦截bean的创建过程
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// 初始化事件派发器
initApplicationEventMulticaster();
// 由子类实现,当容器刷新的时候,可以做一些额外的事情
onRefresh();
// 检查并注册容器中的监听器
registerListeners();
// 初始化剩下的所有单实例bean
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset ‘active‘ flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring‘s core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}打断点

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

当执行bean工厂的后置处理器之后,才出现了book的bean,所以进入工厂后置处理器【org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors】
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new 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) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}进入【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);
processConfigBeanDefinitions(registry);
}进入最后一行代码【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】

// 未完待续(欠缺具体处理注解过程)