一、收集注入元数据

在Spring依赖注入15-Spring源码-依赖注入(1)中讲了依赖注入的收集注解元数据。我们接着上文的查找元数据详细的展开说明一下:创建需要注入的元信息AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata

  1. private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
  2. // 用于判断某个类是否可能 被标记指定的某个注解(@Authowired).见代码片段1
  3. // @Autowired、@Value 以及 @Inject
  4. if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
  5. return InjectionMetadata.EMPTY;
  6. }
  7. // 判断当前类或其字段或其方法是否标注了autowiredAnnotationTypes中的注解,没有的话直接返回空
  8. List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
  9. Class<?> targetClass = clazz;
  10. do {
  11. final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
  12. // 遍历targetClass中的字段。见代码片段2
  13. ReflectionUtils.doWithLocalFields(targetClass, field -> {
  14. // 获取field上的@Autowired注解信息.见代码片段3
  15. MergedAnnotation<?> ann = findAutowiredAnnotation(field);
  16. if (ann != null) {
  17. // 如果字段是静态类型是不会进行注入的
  18. if (Modifier.isStatic(field.getModifiers())) {
  19. return;
  20. }
  21. // 获取@Autowired注解中的required属性
  22. boolean required = determineRequiredStatus(ann);
  23. // 将装成AutowiredFieldElement添加进currElements
  24. currElements.add(new AutowiredFieldElement(field, required));
  25. }
  26. });
  27. // 遍历targetClass中的方法
  28. ReflectionUtils.doWithLocalMethods(targetClass, method -> {
  29. // 找到桥接方法.见代码片段4
  30. Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
  31. // 判断方法的可见性,如果不可见则直接返回
  32. if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
  33. return;
  34. }
  35. // 获取method上的@Autowired注解信息
  36. MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
  37. if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
  38. // 如果是静态方法是不会进行注入的
  39. if (Modifier.isStatic(method.getModifiers())) {
  40. if (logger.isInfoEnabled()) {
  41. logger.info("Autowired annotation is not supported on static methods: " + method);
  42. }
  43. return;
  44. }
  45. // 方法注入没有参数就违背了初衷,就是在脱裤子放屁
  46. if (method.getParameterCount() == 0) {
  47. if (logger.isInfoEnabled()) {
  48. logger.info("Autowired annotation should only be used on methods with parameters: " + method);
  49. }
  50. }
  51. // 获取@Autowired注解中的required属性
  52. boolean required = determineRequiredStatus(ann);
  53. // 将方法和目标类型封装成属性描述符
  54. PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
  55. // 封装成AutowiredMethodElement添加进currElements
  56. currElements.add(new AutowiredMethodElement(method, required, pd));
  57. }
  58. });
  59. // 将currElements整个添加进elements
  60. elements.addAll(0, currElements);
  61. // 获取targetClass的父类,进行下一次循环
  62. targetClass = targetClass.getSuperclass();
  63. }
  64. // 当targetClass为空或者targetClass等于Object.class时会退出循环
  65. while (targetClass != null && targetClass != Object.class);
  66. // 将elements和clazz封装成InjectionMetadata返回
  67. return InjectionMetadata.forElements(elements, clazz);
  68. }

代码片段1,用于判断某个类是否可能 被标记指定的某个注解

  1. // 用于判断某个类是否可能 被标记指定的某个注解
  2. public static boolean isCandidateClass(Class<?> clazz, Class<? extends Annotation> annotationType) {
  3. return isCandidateClass(clazz, annotationType.getName());
  4. }
  5. public static boolean isCandidateClass(Class<?> clazz, String annotationName) {
  6. // 如果注解所在包是java开头,任何类上都可能标记这个注解
  7. // Java开头的注解包就是JDK自带的注解,任何类都可能标记.这个时候返回True就没什么问题
  8. if (annotationName.startsWith("java.")) {
  9. return true;
  10. }
  11. // 注解所在包不是java开头的情况
  12. // 可以认为是自定义注解,例如Spring的@Service @Autowired @Value 对Spring来说就是其自定义注解
  13. // 如果类的包是以java开头,则不可能标记自定义的注解
  14. if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(clazz)) {
  15. return false;
  16. }
  17. return true;
  18. }
  19. // 这里的class就是自己的class类。如果类的包是以java开头,则不可能标记自定义的注解
  20. // 如果类的包不是以java开头
  21. static boolean hasPlainJavaAnnotationsOnly(Class<?> type) {
  22. return (type.getName().startsWith("java.") || type == Ordered.class);
  23. }

代码片段2,遍历targetClass中的字段

  1. public static void doWithLocalFields(Class<?> clazz, ReflectionUtils.FieldCallback fc) {
  2. // 获取类上的所有的字段,包括共有的字段和自由的字段
  3. Field[] var2 = getDeclaredFields(clazz);
  4. int var3 = var2.length;
  5. for(int var4 = 0; var4 < var3; ++var4) {
  6. Field field = var2[var4];
  7. try {
  8. // 处理回调方法
  9. fc.doWith(field);
  10. } catch (IllegalAccessException var7) {
  11. throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + var7);
  12. }
  13. }
  14. }

代码片段3,获取field上的@Autowired注解信息

  1. @Nullable
  2. private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
  3. // 创建MergedAnnotations实例。该实例是定义了大量的关于注解的API操作
  4. MergedAnnotations annotations = MergedAnnotations.from(ao);
  5. for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
  6. MergedAnnotation<?> annotation = annotations.get(type);
  7. // 判断是否包含@Authorwied
  8. if (annotation.isPresent()) {
  9. return annotation;
  10. }
  11. }
  12. return null;
  13. }

关于MergedAnnotations的用法可以参考 17-spring源码-组合注解的处理

代码片段4 找到桥接方法

  1. public static Method findBridgedMethod(Method bridgeMethod) {
  2. // 判断方法是否为桥接方法。参考 https://www.cnblogs.com/guangshan/p/4661305.html
  3. // 桥接方法的进阶版本:https://cloud.tencent.com/developer/article/1656258
  4. if (!bridgeMethod.isBridge()) {
  5. return bridgeMethod;
  6. } else {
  7. // 继续的寻找bridgeMethod.DeclaringClass
  8. Method bridgedMethod = (Method)cache.get(bridgeMethod);
  9. if (bridgedMethod == null) {
  10. List<Method> candidateMethods = new ArrayList();
  11. MethodFilter filter = (candidateMethod) -> {
  12. return isBridgedCandidateFor(candidateMethod, bridgeMethod);
  13. };
  14. // 递归该类及其所有父类上的所有方法,符合筛选条件就添加进来。
  15. ReflectionUtils.doWithMethods(bridgeMethod.getDeclaringClass(), candidateMethods::add, filter);
  16. if (!candidateMethods.isEmpty()) {
  17. bridgedMethod = candidateMethods.size() == 1 ? (Method)candidateMethods.get(0) : searchCandidates(candidateMethods, bridgeMethod);
  18. }
  19. if (bridgedMethod == null) {
  20. bridgedMethod = bridgeMethod;
  21. }
  22. cache.put(bridgeMethod, bridgedMethod);
  23. }
  24. return bridgedMethod;
  25. }
  26. }

桥接方法的进阶版本,从JVM的角度解释了桥接方法:

小结

收集注入元数据过程,首先调用AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition方法,然后调用findAutowiringMetadata方法查找元数据,如果找到相应类的注入元数据 ,就会调用buildAutowiringMetadata方法创建InjectionMetadata,最后将新创建的注入元数据保存在injectionMetadataCache缓存起来