Java注解与元注解的实现原理
注解(Annotation)是 Java 5 引入的元数据机制,广泛应用于框架开发、代码生成和编译期检查。本文从基础用法到实现原理进行全面讲解。
注解的基本定义
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LogExecution { String value() default ""; boolean printParams() default true; }
|
四大元注解
1. @Target:使用位置
public enum ElementType { TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE }
|
2. @Retention:保留策略
| 策略 |
说明 |
典型应用 |
| SOURCE |
编译期丢弃 |
@Override, @SuppressWarnings |
| CLASS |
保留到字节码,运行期丢弃 |
默认策略,少见 |
| RUNTIME |
运行期保留,可通过反射读取 |
@Autowired, @RequestMapping |
3. @Documented
包含在 Javadoc 中:
@Documented public @interface ApiDoc { String description(); }
|
4. @Inherited
子类自动继承父类注解:
@Inherited @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface ParentAnnotation { }
@ParentAnnotation public class Parent { }
public class Child extends Parent { }
|
注解的底层实现
编译后的注解
注解本质是一个继承 java.lang.annotation.Annotation 的接口:
public interface LogExecution extends Annotation { String value(); boolean printParams(); }
|
JDK 动态代理生成
当通过反射获取注解时,JDK 会生成动态代理:
Method method = clazz.getDeclaredMethod("doSomething"); LogExecution annotation = method.getAnnotation(LogExecution.class);
System.out.println(annotation.getClass());
|
代理类将方法调用转发给 AnnotationInvocationHandler:
class AnnotationInvocationHandler implements InvocationHandler { private final Map<String, Object> memberValues; public Object invoke(Object proxy, Method method, Object[] args) { String member = method.getName(); Object value = memberValues.get(member); return value; } }
|
反射读取注解
类级别注解
boolean hasAnnotation = clazz.isAnnotationPresent(Service.class);
Service service = clazz.getAnnotation(Service.class); String name = service.value();
|
方法级别注解
for (Method method : clazz.getDeclaredMethods()) { if (method.isAnnotationPresent(Transactional.class)) { Transactional tx = method.getAnnotation(Transactional.class); System.out.println("超时时间: " + tx.timeout()); } }
|
重复注解(JDK 8+)
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Roles { Role[] value(); }
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Repeatable(Roles.class) public @interface Role { String value(); }
@Role("ADMIN") @Role("USER") public void doSomething() { }
|
实际应用场景
1. 日志切面
@Aspect @Component public class LogAspect { @Around("@annotation(logExecution)") public Object around(ProceedingJoinPoint pjp, LogExecution logExecution) throws Throwable { long start = System.currentTimeMillis(); Object result = pjp.proceed(); long cost = System.currentTimeMillis() - start; System.out.println("方法 " + pjp.getSignature().getName() + " 执行耗时: " + cost + "ms"); return result; } }
|
2. 参数校验
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface NotNull { String message() default "字段不能为空"; }
public class Validator { public static void validate(Object obj) throws IllegalAccessException { for (Field field : obj.getClass().getDeclaredFields()) { field.setAccessible(true); if (field.isAnnotationPresent(NotNull.class) && field.get(obj) == null) { NotNull notNull = field.getAnnotation(NotNull.class); throw new ValidationException(notNull.message()); } } } }
|
3. ORM 映射
@Entity @Table(name = "t_user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "user_name", length = 50) private String username; }
|
注解处理器(APT)
编译期处理注解,生成代码:
@SupportedAnnotationTypes("com.example.AutoGenerate") @SupportedSourceVersion(SourceVersion.RELEASE_11) public class AutoGenerateProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for (Element element : roundEnv.getElementsAnnotatedWith(AutoGenerate.class)) { generateCode(element); } return true; } }
|
知名应用:
- Lombok:编译期生成 getter/setter
- Dagger:编译期生成依赖注入代码
- MapStruct:编译期生成对象映射代码
总结
- 注解是接口:编译后继承 Annotation
- 运行期通过代理访问:JDK 动态代理生成注解实例
- 元注解控制行为:Target、Retention、Documented、Inherited
- 反射是读取注解的主要方式:getAnnotation、isAnnotationPresent
- APT 实现编译期处理:代码生成、编译期检查
注解机制是 Java 框架开发的基石,Spring、JPA、JUnit 等都重度依赖注解实现声明式编程。