在Spring框架中,注解是一种非常重要的特性,它极大地简化了配置和开发过程。Spring注解是Spring框架中用于简化配置和开发的核心机制,主要分为组件管理、依赖注入、配置类、AOP、Web开发等类别。
参考原文:
 Java注解底层实现原理 - 源码分析
Spring注解的底层实现逻辑
实践案例
Java注解的底层实现逻辑主要包括定义注解、使用注解和通过反射获取注解。
定义注解
注解是通过@interface关键字定义的,例如:
1 2 3 4 5 6
   | @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation {     String value() default "";      int[] numbers() default {}; }
   | 
 
这里,@Target和@Retention是元注解,分别指定了注解的应用目标和保留策略。ElementType.TYPE表示该注解可以应用于类、接口或枚举上,RetentionPolicy.RUNTIME表示注解在运行时可以通过反射获取
使用注解
使用注解时,只需在声明处添加相应的注解即可。例如:
1 2 3 4 5 6 7
   | @MyAnnotation(value="hello") public class TestClass {     public static void main(String[] args) {         MyAnnotation annotation = TestClass.class.getAnnotation(MyAnnotation.class);         System.out.println(annotation.value());      } }
   | 
 
这段代码通过反射获取了TestClass类上的MyAnnotation注解,并打印了其value属性的值。
反射获取注解
通过反射,可以在运行时获取类、方法、参数等上的注解信息。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | Class<?> clazz = TestClass.class; MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class); System.out.println(annotation.value());
  public static void main(String[] args) {          Method[] methods = MyAnnotation.class.getClassLoader()           .loadClass(("com.pdai.java.annotation.TestMyAnnotation"))           .getMethods();
           for (Method method : methods) {         if (method.isAnnotationPresent(MyAnnotation.class)) {              MyAnnotation anno = method.getAnnotation(MyAnnotation.class);              System.out.println(anno.title());          }     } }
   | 
 
源码分析注解的底层实现
从 MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class); 开始分析。
1 2 3 4 5 6
   |  @SuppressWarnings("unchecked") public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {     Objects.requireNonNull(annotationClass);     return (A) annotationData().annotations.get(annotationClass); }
 
  | 
 
annotationData().annotations.get(annotationClass);:调用 annotationData() 方法获取当前类的注解数据,然后从 annotations 映射中获取指定类型的注解实例 
(A):将获取到的注解实例强制转换为泛型类型 A 
获取注解数据集 annotationData
使用 Atomic.casAnnotationData 方法(基于 CAS 乐观锁机制)尝试将新的 AnnotationData 对象更新到当前类中。若更新成功,返回新的 AnnotationData 对象;若失败,循环会继续重试,直到成功为止。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   | 
 
 
  private AnnotationData annotationData() {     while (true) {           AnnotationData annotationData = this.annotationData;          int classRedefinedCount = this.classRedefinedCount;          if (annotationData != null &&             annotationData.redefinedCount == classRedefinedCount) {              return annotationData;          }
                   AnnotationData newAnnotationData = createAnnotationData(classRedefinedCount);                  if (Atomic.casAnnotationData(this, annotationData, newAnnotationData)) {             return newAnnotationData;          }              } }
 
  | 
 
构建注解数据集 createAnnotationData
- 调用 
AnnotationParser.parseAnnotations 方法,解析当前类的原始注解数据,将结果存储在 declaredAnnotations 映射中,键为注解类,值为注解实例 
- 处理父类继承注解:父类存在,获取父类的注解数据中的注解映射 
superAnnotations。遍历 superAnnotations,对于每个注解,检查其是否使用了@Inherited元注解(通过 AnnotationType.getInstance(annotationClass).isInherited() 判断)。若使用了 @Inherited 元注解,且 annotations 为 null,则进行懒初始化,创建一个 LinkedHashMap 来存储继承的注解。将继承的注解添加到 annotations 映射中 
- 合并注解:若 annotations 仍为 null,说明没有继承的注解,直接将 annotations 指向 declaredAnnotations。若存在继承的注解,将 declaredAnnotations 中的注解添加到 annotations 中,当前类声明的注解会覆盖继承的注解
 
- 总结:该方法的核心逻辑是解析当前类的声明注解,若父类存在,获取父类中可继承的注解,将两者合并后创建 AnnotationData 对象。这样可以保证 AnnotationData 对象包含当前类完整的注解信息
 
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
   |  private AnnotationData createAnnotationData(int classRedefinedCount) {               Map<Class<? extends Annotation>, Annotation> declaredAnnotations =         AnnotationParser.parseAnnotations(getRawAnnotations(), getConstantPool(), this);              Class<?> superClass = getSuperclass();      Map<Class<? extends Annotation>, Annotation> annotations = null;      if (superClass != null) {          Map<Class<? extends Annotation>, Annotation> superAnnotations =             superClass.annotationData().annotations;                   for (Map.Entry<Class<? extends Annotation>, Annotation> e : superAnnotations.entrySet()) {             Class<? extends Annotation> annotationClass = e.getKey();                          if (AnnotationType.getInstance(annotationClass).isInherited()) {                  if (annotations == null) {                                           annotations = new LinkedHashMap<>((Math.max(                             declaredAnnotations.size(),                             Math.min(12, declaredAnnotations.size() + superAnnotations.size())                         ) * 4 + 2) / 3                     );                 }                                  annotations.put(annotationClass, e.getValue());             }         }     }               if (annotations == null) {                   annotations = declaredAnnotations;          } else {                   annotations.putAll(declaredAnnotations);     }     return new AnnotationData(annotations, declaredAnnotations, classRedefinedCount); }
 
  | 
 
获取当前类的原始注解数据 getRawAnnotations
- native 关键字:在 Java 里,
native 关键字用于声明本地方法。本地方法并非用 Java 实现,而是借助其他编程语言(像 C、C++)实现。Java 虚拟机(JVM)会负责加载并调用这些本地方法。通常,本地方法用于和底层系统交互,或实现对性能要求极高的操作。 
- byte[] 返回类型:该方法返回一个字节数组 byte[]。这意味着方法会返回当前类原始注解数据的字节表示形式。原始注解数据是注解在字节码层面的存储形式,可能包含注解类型、注解属性值等信息。
 
- 元注解:作用于自定义注解类型的注解类,在JDK 1.5中提供了4个标准的元注解:
@Target,@Retention,@Documented,@Inherited,在JDK 1.8中提供了两个元注解 @Repeatable和@Native 
1 2
   |  native byte[] getRawAnnotations();
 
  | 
 
获取当前类对应的常量池 getConstantPool
getConstantPool 方法是一个本地方法,其作用是获取当前类对应的常量池对象。由于需要直接访问 JVM 内部的数据结构,所以采用本地方法实现。调用该方法后,能得到一个 ConstantPool 对象,进而访问常量池中的常量信息
1 2
   |  native ConstantPool getConstantPool();
 
  | 
 
解析当前类的原始注解数据 parseAnnotations
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
   |  public static Map<Class<? extends Annotation>, Annotation> parseAnnotations(         byte[] var0, ConstantPool var1, Class<?> var2) {     if (var0 == null) {         return Collections.emptyMap();     } else {         try {             return parseAnnotations2(var0, var1, var2, (Class[])null);         } catch (BufferUnderflowException var4) {             throw new AnnotationFormatError("Unexpected end of annotations.");         } catch (IllegalArgumentException var5) {             throw new AnnotationFormatError(var5);         }     } }
  private static Map<Class<? extends Annotation>, Annotation> parseAnnotations2(         byte[] var0, ConstantPool var1, Class<?> var2, Class<? extends Annotation>[] var3) {     LinkedHashMap var4 = new LinkedHashMap();     ByteBuffer var5 = ByteBuffer.wrap(var0);     int var6 = var5.getShort() & '\uffff';
      for(int var7 = 0; var7 < var6; ++var7) {         Annotation var8 = parseAnnotation2(var5, var1, var2, false, var3);         if (var8 != null) {             Class var9 = var8.annotationType();             if (AnnotationType.getInstance(var9).retention() == RetentionPolicy.RUNTIME && var4.put(var9, var8) != null) {                 throw new AnnotationFormatError("Duplicate annotation for class: " + var9 + ": " + var8);             }         }     }
      return var4; }
 
 
  | 
 
具体会调用到下面的 parseAnnotation2 方法,该方法主要都是解析注解里面的信息,解析出来的值最终会给到我们去创建代理对象用。我们重点关注的是 annotationForMap 这个方法,该方法里面就是通过动态代理来创建注解实例
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
   | 
 
 
 
 
 
 
 
  private static Annotation parseAnnotation2(     ByteBuffer var0, ConstantPool var1, Class<?> var2, boolean var3, Class<? extends Annotation>[] var4) {        int var5 = var0.getShort() & '\uffff';      Object var6 = null;      String var7 = "[unknown]";                try {         try {             var7 = var1.getUTF8At(var5);              var21 = parseSig(var7, var2);          } catch (IllegalArgumentException var18) {             var21 = var1.getClassAt(var5);           }     } catch (NoClassDefFoundError var19) {          if (var3) {              throw new TypeNotPresentException(var7, var19);         }         skipAnnotation(var0, false);          return null;     } catch (TypeNotPresentException var20) {          if (var3) {              throw var20;         }         skipAnnotation(var0, false);          return null;     }
           if (var4 != null && !contains(var4, var21)) {          skipAnnotation(var0, false);         return null;     } else {         Object var8 = null;          try {             var23 = AnnotationType.getInstance(var21);          } catch (IllegalArgumentException var17) {              skipAnnotation(var0, false);             return null;         }         Map var9 = var23.memberTypes();          LinkedHashMap var10 = new LinkedHashMap(var23.memberDefaults());          int var11 = var0.getShort() & '\uffff';          for(int var12 = 0; var12 < var11; ++var12) {               int var13 = var0.getShort() & '\uffff';              String var14 = var1.getUTF8At(var13);              Class var15 = (Class)var9.get(var14);              if (var15 == null) {                  skipMemberValue(var0);             } else {                 Object var16 = parseMemberValue(var15, var0, var1, var2);                                   if (var16 instanceof AnnotationTypeMismatchExceptionProxy) {  ((AnnotationTypeMismatchExceptionProxy)var16).setMember((Method)var23.members().get(var14));                 }                 var10.put(var14, var16);              }         }         return annotationForMap(var21, var10);      } }
 
  | 
 
通过java动态代理实例化注解代理对象
AccessController.doPrivileged:该方法用于在特权环境下执行特定操作。在 Java 安全模型里,有些操作需要特定权限才能执行,使用 doPrivileged 可确保代码在足够权限下运行 
annotationForMap 方法利用 Java 的反射和代理机制,根据给定的注解类型和成员值映射,动态创建一个注解实例。借助 AccessController 确保操作在特权环境下执行,最终返回一个实现了指定注解接口的代理对象(此处需要注意,用的代理类 AnnotationInvocationHandler) 
1 2 3 4 5 6 7 8 9 10 11 12
   |  public static Annotation annotationForMap(         final Class<? extends Annotation> var0, final Map<String, Object> var1) {          return (Annotation)AccessController.doPrivileged(new PrivilegedAction<Annotation>() {         public Annotation run() {             return (Annotation)Proxy.newProxyInstance(var0.getClassLoader(),                                                        new Class[]{var0},                                                        new AnnotationInvocationHandler(var0, var1));         }     }); }
 
  | 
 
实现 AnnotationInvocationHandler
AnnotationInvocationHandler 类中的 invoke 方法,该类实现了 InvocationHandler 接口。在 Java 动态代理机制里,InvocationHandler 接口的 invoke 方法是核心,当调用代理对象的方法时,实际上会调用 invoke 方法来处理 
- 处理特殊方法调用:
- 若被调用方法是 toString,调用 
toStringImpl 方法生成注解的字符串表示并返回 
- 若被调用方法是 hashCode,调用 
hashCodeImpl 方法计算注解的哈希码并返回 
- 若被调用方法是 
annotationType,返回注解的类型 this.type 
 
- 处理注解成员方法调用:
- 若被调用方法是注解的成员方法,从 
memberValues 映射中获取对应的值 
- 若值为 null,说明注解实例缺少该成员的值,抛出 
IncompleteAnnotationException 异常 
- 若值是 
ExceptionProxy 类型,调用 generateException 方法抛出异常 
- 若值是数组且不为空,调用 
cloneArray 方法克隆数组,避免外部修改原始数组,最后返回该值 
 
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
   |  private static final long serialVersionUID = 6182022883658399397L; private final Class<? extends Annotation> type; private final Map<String, Object> memberValues; private transient volatile Method[] memberMethods = null;
  AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {     Class[] var3 = var1.getInterfaces();     if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {         this.type = var1;         this.memberValues = var2;     } else {         throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");     } }
  public Object invoke(Object var1, Method var2, Object[] var3) {     String var4 = var2.getName();     Class[] var5 = var2.getParameterTypes();     if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {         return this.equalsImpl(var3[0]);     } else if (var5.length != 0) {         throw new AssertionError("Too many parameters for an annotation method");     } else {         switch (var4) {             case "toString":                 return this.toStringImpl();             case "hashCode":                 return this.hashCodeImpl();             case "annotationType":                 return this.type;             default:                 Object var6 = this.memberValues.get(var4);                 if (var6 == null) {                     throw new IncompleteAnnotationException(this.type, var4);                 } else if (var6 instanceof ExceptionProxy) {                     throw ((ExceptionProxy)var6).generateException();                 } else {                     if (var6.getClass().isArray() && Array.getLength(var6) != 0) {                         var6 = this.cloneArray(var6);                     }                     return var6;                 }         }     } }
 
  | 
 
其他相关问题
注解如何生效
编译期扫描处理,运行期反射处理。编译期扫描处理一般只有Java内置注解会用到,比如@Override修饰的方法,编译器会检查父类是否有相同的方法。大部分自定义的注解,都是在运行期通过反射拿到并处理。
运行期如何获取注解
运行时注解存放在class文件中的attributes属性表中。
反射获取注解的核心在:java.lang.reflect下的 AnnotatedElement接口,而AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口。
Spring加与不加注解的区别
- 配置方式:
- 不加注解:可以通过XML配置文件来定义bean,这种方式更加直观,但需要编写XML文件。
 
- 加注解:通过使用
@Configuration和@Bean注解来定义bean,这种方式更加灵活,代码更加简洁,且易于维护和修改。 
 
- 管理方式:
- 不加注解:需要手动编写XML配置文件,并通过XML配置文件来管理bean的创建和依赖关系。
 
- 加注解:通过注解来定义bean,Spring框架在启动时会扫描这些注解并自动创建和管理bean,减少了手动配置的工作量。
 
 
常用注解及解释
核心注解
- **
@Component**:用于把当前类对象存入Spring容器中。@Controller、@Service、@Repository都可以称为@Component,它们分别用于控制层、业务层和数据访问层。 
- **
@Autowired**:自动按照类型注入Spring容器中的bean。它可以作用在变量、setter方法或构造函数上。 
- **
@Qualifier**:在按照类型注入的基础上,通过名称进行注入。通常与@Autowired一起使用,用于解决相同类型bean的注入冲突。 
- **
@Inject**:由JSR-330提供,用法与@Autowired相似,但它是Java标准的一部分,而@Autowired是Spring特有的。 
- **
@Resource**:由JSR-250提供,按照bean的id进行注入,可以独立使用。 
- **
@Primary**:当存在多个相同类型的bean时,标记首选的bean进行注入。 
Java配置类相关注解
- **
@Configuration**:声明当前类为配置类,相当于传统的XML配置文件。 
- **
@Bean**:注解在方法上,声明当前方法的返回值为一个bean,替代XML中的<bean>标签。 
- **
@ComponentScan**:用于指定Spring在创建容器时要扫描的包,以找到带有@Component、@Repository、@Service、@Controller等注解的类,并注册为bean。 
切面(AOP)相关注解
- **
@Aspect**:声明一个切面。 
- **
@After**:在目标方法执行之后执行。 
- **
@Before**:在目标方法执行之前执行。 
- **
@Around**:在目标方法执行之前和之后执行,可以围绕目标方法创建一个“拦截器”。 
- **
@PointCut**:声明一个切点,即指定哪些方法将被增强。 
配置和环境相关注解
- **
@Value**:用于注入基本类型和String类型的数据,支持使用Spring EL表达式。 
- **
@Profile**:指定组件在哪个环境的情况下才能被注册到容器中。 
- **
@Conditional**:通过实现Condition接口,并重写matches方法,从而决定该bean是否被实例化。 
其他常用注解
- **
@Lazy**:用于延迟初始化bean,即只有在第一次使用时才会创建和初始化。 
- **
@Scope:用于指定bean的作用范围,如单例(singleton)或多例(prototype)**。 
- **
@EnableAsync**:在配置类中通过此注解开启对异步任务的支持。 
- **
@Async**:在实际执行的bean方法使用该注解来声明其是一个异步任务。 
- **
@EnableScheduling**:在配置类上使用,开启计划任务的支持。 
- **
@Scheduled**:用于声明一个定时任务。