注解是干什么的
注解本身不提供作用,注解只能是被看作元数据,它不包含任何业务逻辑。注解更像是一个标签,一个声明,表面被注释的这个地方,将具有某种特定的逻辑。
注解常见的种类
常见的注解有三大类:JDK的,自定义的,第三方的(比如框架)
注解三板斧
定义、使用、读取
定义:包括名字,能用到哪些地方,有效期,是否可以被继承
使用:定义好之后在允许的地方使用标注即可
光有前两步,没什么用,如最熟悉的@Override注解,为什么能验证重写是否有效,怎么不是验证重载?spring的@Autowired为什么是注入作用,而不是输出一句话?显然,他们在程序中做了实现,使得其注解具有各自的作用,也具有了意义,而赋予灵魂的一步就是读取
读取:让注解发挥作用,给注解注入灵魂
注解前置知识
首先是元注解,Java中提供了四个
@Documented | @Retention | @Target | @Inherited
分别解释下
@Documented
代表着此注解会被javadoc工具提取成文档
@Retention:
代表该注解的有效期
SOURCE
表示编译期,如@Override,只做编译时的提示,不会写入字节码中。
CLASS
表示类加载期,会保存在class文件中,但在运行class文件被丢弃,也是默认值
RUNTIME
表示运行期,也是最常用的,可以在代码运行时进行反射执行相关的操作
@Target:
表示这个注解可以放在哪
TYPE
:接口、类、枚举、注解
FIELD
:字段、枚举的常量
METHOD
:方法
PARAMETER
:参数
CONSTRUCTOR
:构造函数
LOCAL_VARIABLE
:局部变量
ANNOTATION_TYPE
:注解
PACKAGE
:包
@Inherited:
表示子类可以继承该类的注解
举个例子
自己自定义个注解,并赋予它作用,模拟aop功能,在方法前后加入
定义注解
1 2 3 4 5 6 7 8 9 10 11 12
| @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyBefore {}
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyCore {}
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyAfter {}
|
使用注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Test { @MyBefore public void init() { System.out.println("初始化。。。"); } @MyAfter public void destroy() { System.out.println("销毁。。。"); } @MyCore public void core() { System.out.println("核心方法"); } }
|
前两步简单,重要的是如何赋予注解作用
读取注解
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 class MyWork { public static void main(String[] args) throws Exception{ Class clazz = Test.class; Object obj = clazz.newInstance(); List<Method> myBeforeList = new ArrayList<>(); List<Method> myAfterList = new ArrayList<>(); List<Method> myTestList = new ArrayList<>(); Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(MyBefore.class)) { myBeforeList.add(method); continue; } if (method.isAnnotationPresent(MyCore.class)) { myTestList.add(method); continue; } if (method.isAnnotationPresent(MyAfter.class)) { myAfterList.add(method); continue; } } for (Method test : myTestList) { for (Method method : myBeforeList) { method.invoke(obj); } test.invoke(obj); for (Method method : myAfterList) { method.invoke(obj); } } } }
|
输出
这只是个举个例子,真实的应用场景绝对不会这么简单,比如我们尝试按此套路分析下@Autowired是如何工作的
源码分析
定义注解
1 2 3 4 5 6 7 8 9 10 11
| @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired {
boolean required() default true;
}
|
源码看起来不难,规定了有效期在运行期间,并且可以标注在构造器、方法、参数、常量、注解上面,并且定义了一个属性required默认值为true
使用注解
1 2 3 4 5 6 7 8 9 10
| @RestController public class Test { @Autowired private User user; @RequestMapping("/say") String sayHello() { user.setName("法外狂徒张三"); return user.getName(); } }
|
这里主要是为了体现spring给我们创建了一个对象,所有不会抛空指针,页面有值

那么spring是如何做到的呢?正所谓注解两板斧不难,重要的是第三板斧
读取注解
@Autowired注解由AutowiredAnnotationBeanPostProcessor这个后置器类进行处理
先获取标注了@Autowired注解的属性或方法并存入一个集合中,通过反射的方式注入进去
注入的方法是该后置器的postProcessProperties方法的inject方法

这两种方法同样在后置器类中对InjectionMetadata进行了重写
1 2 3 4 5 6 7 8 9 10 11
| public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { Collection<InjectedElement> checkedElements = this.checkedElements; Collection<InjectedElement> elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements); if (!elementsToIterate.isEmpty()) { for (InjectedElement element : elementsToIterate) { element.inject(target, beanName, pvs); } } }
|
如果标注的是属性那么调用AutowiredFieldElement方法

这里表明会给我的user属性附上一个user对象
如果标注的是方法那么调用AutowiredMethodElement方法
1 2 3 4 5 6 7
| if (arguments != null) { try { ReflectionUtils.makeAccessible(method); method.invoke(bean, arguments); } }
|
源码拓展
如果深入了解下@Autowired可以看下去
整个过程的调用情况,其中postProcessPropertyValues已弃用,取而代之的是postProcessProperties

整个过程的源码调用非常的多,建议自己动手debug
,不然会蒙圈
疑问一如何具备匹配Autowired的能力?
在创建出AutowiredAnnotationBeanPostProcessor后置类的时候进行了初始化,将Autowired这个注解类型放入了集合中,方便后面匹配使用,那么如何进行该后置器类的初始化?
1 2 3 4 5
| public AutowiredAnnotationBeanPostProcessor() { this.autowiredAnnotationTypes.add(Autowired.class); this.autowiredAnnotationTypes.add(Value.class); }
|
疑问二:那么如何进行AutowiredAnnotationBeanPostProcessor后置器类的初始化?
在spring容器启动的时候会调用关键的refresh方法,其中有一步就是去注册所有的后置器类,自然也包括AutowiredAnnotationBeanPostProcessor这个类
疑问三:AutowiredAnnotationBeanPostProcessor激活之后如何去获取到注解标注的属性或方法
如图11步,该后置器类有一个findAutowiringMetadata方法,找到的话最后返回一个InjectionMetadata集合,后面利用这个集合中的元素来进行注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) { if (InjectionMetadata.needsRefresh(metadata, clazz)) { synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { if (metadata != null) { metadata.clear(pvs); } metadata = buildAutowiringMetadata(clazz); this.injectionMetadataCache.put(cacheKey, metadata); } } } return metadata; }
|
那么如何获取这个元素的?核心方法为buildAutowiringMetadata
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
| private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) { ReflectionUtils.doWithLocalFields(targetClass, field -> { MergedAnnotation<?> ann = findAutowiredAnnotation(field); if (ann != null) { if (Modifier.isStatic(field.getModifiers())) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation is not supported on static fields: " + field); } return; } boolean required = determineRequiredStatus(ann); currElements.add(new AutowiredFieldElement(field, required)); } }); ReflectionUtils.doWithLocalMethods(targetClass, method -> { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod); if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { if (Modifier.isStatic(method.getModifiers())) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation is not supported on static methods: " + method); } return; } if (method.getParameterCount() == 0) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation should only be used on methods with parameters: " + method); } } boolean required = determineRequiredStatus(ann); PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); currElements.add(new AutowiredMethodElement(method, required, pd)); } });
elements.addAll(0, currElements); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz); }
|
而这两个方法中都有一个findAutowiredAnnotation方法,作用是找到带有@Autowired和@Value注解的属性和方法
1 2 3 4 5 6 7 8 9 10
| private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) { MergedAnnotations annotations = MergedAnnotations.from(ao); for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) { MergedAnnotation<?> annotation = annotations.get(type); if (annotation.isPresent()) { return annotation; } } return null; }
|
最后将AutowiredFieldElement和AutowiredMethodElement对象的封装成InjectionMetadata,完成对注解的收集,如果找不到就返回null
疑问四:在拥有收集好注解的集合后,在哪里去使用?
这个问题也可以改为如何进行依赖注入的?
这时候refresh的registerBeanPostProcessors方法也执行完了,然后来执行finishBeanFactoryInitialization

中间一大串调用就不说了,可以自己debug去看,主要说下调用到后置器类的postProcessProperties方法
可以粗略理解为finishBeanFactoryInitialization — > xxxxxxx —-> postProcessProperties
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs; }
|
在点进去,里面有个InjectedElement抽象类,而之前的这两个封装对象也就用到了

1 2 3 4 5 6 7 8 9 10 11
| public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { Collection<InjectedElement> checkedElements = this.checkedElements; Collection<InjectedElement> elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements); if (!elementsToIterate.isEmpty()) { for (InjectedElement element : elementsToIterate) { element.inject(target, beanName, pvs); } } }
|
接下来就对应上开头的那段代码了
疑问五:开头的那段代码中的bean时怎么获取的?
因为开头只展示了属性注入了对象,没说这个对象是怎么获取的,获取的方法为resolveFieldValue,这个方法是在属性注入前调用的

别看方法一大堆,真正有用的就是红框中的那个,其他是缓存的处理
点进去之后一般执行else方法

这个方法也是一大堆,主要讲了matchingBeans集合的获取与调用,这个集合存的是依赖对象的所有实现类
1 2 3 4 5 6 7
| @Nullable public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); }
|
这里利用获取的map集合进行bean的实例化返回,具体判断规则感兴趣的可以看源码
总结
任何的注解都是三板斧,难点在于如何读取注解并赋予它作用,推荐看springboot的一些注解,它开发利用注解替代了很多的配置文件,可以看看是怎么做到的,看源码实现可以学到很多东西,不过需要一定的内功,比如良好的基础,尤其是对反射这块需要很清楚,还有设计模式也是非常重要的,源码中也有很多地方得到了体现