元注解
1.元注解
元注解就是定义注解的注解,是 Java 提供的用于定义注解的基本注解。
|
|
---|---|
@Retention | 是注解类,实现声明类 Class,声明类别 Category,声明扩展 Extension |
@Target | 放在自定义注解的上边,表明该注解可以使用的范围 |
@Inherited | 允许子类继承父类的注解,在子类中可以获取使用父类注解 |
@Documented | 表明这个注释是由 Javadoc 记录的 |
@interface | 用来自定义注释类型 |
1.1 @Target
该注解的作用是告诉 Java 将自定义的注解放在什么地方,比如类、方法、构造器、变量上等。它的值是一个枚举类型,有如下属性值。
ElementType.CONSTRUCTOR
:用于描述构造器。ElementType.FIELD
:用于描述成员变量、对象、属性(包括enum
实例)。ElementType.LOCAL_VARIABLE
:用于描述局部变量。ElementType.METHOD
:用于描述方法。ElementType.PACKAGE
:用于描述包。ElementType.PARAMETER
:用于描述参数。ElementType.TYPE
:用于描述类、接口(包括注解类型)或enum
声明。
1.2 @Retention
该注解用于说明自定义注解的生命周期,在注解中有三个生命周期。
RetentionPolicy.RUNTIME
:始终不会丢弃,运行期也保留该注解,可以使用反射机制读取该注解的信息。自定义的注解通常使用这种方式。RetentionPolicy.CLASS
:类加载时丢弃,默认使用这种方式。RetentionPolicy.SOURCE
:编译阶段丢弃,自定义注解在编译结束之后就不再有意义,所以它们不会写入字节码。@Override
、@SuppressWarnings
都属于这类注解。
1.3 @Inherited
该注解是一个标记注解,表明被标注的类型是可以被继承的。如果一个使用了 @Inherited
修饰的 Annotation 类型被用于一个 Class,则这个 Annotation 将被用于该 Class 的子类。
1.4 @Documented
该注解表示是否将注解信息添加在 Java 文档中。
1.5 @interface
该注解用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过 default
来声明参数的默认值。
定义注解格式见以下代码:
public @interface 注解名{定义体}
2.自定义注解
有时需要自定义注解来快捷地实现功能。本实例演示如何自定义注解,以及实现业务逻辑处理。
2.1 创建自定义注解类
package com.example.demo;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface MyTestAnnotation {
String value();
}
代码解释如下。
- 使用
@Target
注解标注作用范围。 - 使用
@Retention
注解标注生命周期。 - 使用
@Documented
将注解信息添加在 Java 文档中。
2.2 实现业务逻辑
以 AOP 的方式实现业务逻辑,见以下代码:
package com.example.demo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class TestAnnotationAspect {
// 拦截被 TestAnnotation 注解的方法;如果需要拦截指定 package 指定规则名称的方法,可以使用表达式 execution(...)
@Pointcut("@annotation(com.example.demo.MyTestAnnotation)")
public void myAnnotationPointCut() {
}
@Before("myAnnotationPointCut()")
public void before(JoinPoint joinPoint) throws Throwable {
MethodSignature sign = (MethodSignature) joinPoint.getSignature();
Method method = sign.getMethod();
MyTestAnnotation annotation = method.getAnnotation(MyTestAnnotation.class);
// 获取注解参数
System.out.print("打印TestAnnotation 参数:" + annotation.value());
}
}
类注解
@Aspect
:这是一个切面注解,用于标记这个类为一个切面类。切面类包含了通知(Advice
),定义了要在哪些连接点(JoinPoint
)上执行的逻辑。@Component
:这是一个组件注解,用于将这个切面类注册为 Spring 容器中的一个组件,以便 Spring 可以自动扫描并实例化它。
切入点表达式
@Pointcut("@annotation(com.example.demo.MyTestAnnotation)")
:这是一个切入点(Pointcut
)定义,用于指定这个切面要拦截的操作。这里的切入点表达式表示拦截所有被MyTestAnnotation
注解标记的方法。
通知方法
public void myAnnotationPointCut() {}
:这是一个空方法,被上面的@Pointcut
标注,用来定义切入点逻辑。
前置通知
@Before("myAnnotationPointCut()")
:这是一个前置通知,表示在切入点myAnnotationPointCut
指定的方法(即被MyTestAnnotation
注解的方法)执行之前运行这个通知方法。public void before(JoinPoint joinPoint) throws Throwable {...}
:这是实际执行的前置通知方法。在这个方法中,可以获取到被拦截方法的签名、参数等信息,并且可以执行一些自定义的逻辑。MethodSignature sign = (MethodSignature) joinPoint.getSignature();
:获取方法签名。Method method = sign.getMethod();
:获取具体的方法对象。MyTestAnnotation annotation = method.getAnnotation(MyTestAnnotation.class);
:获取方法上的MyTestAnnotation
注解。System.out.print("打印TestAnnotation 参数:" + annotation.value());
:打印注解的参数值。
这段代码的作用是在 Spring AOP 环境下,拦截所有使用了 MyTestAnnotation
注解的方法,并在这些方法执行之前打印出注解的参数值。这种机制可以用来做很多有趣的事情,比如 日志记录、权限检查、性能监控 等,极大地增强了代码的灵活性和可维护性。
2.3 使用自定义注解
在需要使用的地方使用自定义注解,直接添加注解名即可,见以下代码:
package com.example.demo;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
@EnableAspectJAutoProxy
public class TestController {
@RequestMapping("/")
@MyTestAnnotation("测试Annotation")
public void testAnnotation() {
System.err.println("测试自定义注解");
}
}
运行上面代码,输出如下结果: