怎么理解注解?
注解就是代码中特殊的标记,这些标记可以在编译、类加载、运行时被读取,并执行对应的处理
了解过哪些注解?
注解在开发中比较常见,比如Java常用的框架Spring和MyBatis中的注解@Controller、@Service、@Param、@Select等等,有些项目使用Lombok了时,会添加@Data、@Slf4j注解等等
Java也有原生注解,比如@Override、@Function、@Deprecated等等,Java原生的注解大部分用于【标记】和【检查】,原生的Java注解除了这些基本注解外还有一种叫做元Annotation(元注解),元注解就是用来修饰注解的注解
常用的元注解有@Retention和@Target
@Retention注解可以简单理解为设置注解的生命周期
@Target注解用来注明这个注解可以修饰哪些地方(比如方法、成员变量、还是包等等)
如何自定义一个注解然后使用?
要自定义注解,首要考虑我们要在什么时候去解析这个注解
这就是需要用到前面提到的元注解@Retention,这个注解会修饰自定义注解的生命周期
@Retention注解传入的是RetentionPolicy枚举,该枚举有三个常量,分别是SOURCE、CLASS和RUNTIME
SOURCE:当@Retention传入SOURCE时,表示这个自定义注解只会保留在源文件,当Java文件编译成class文件时该自定义注解就会被遗弃失效
CLASS:当@Retention传入CLASS时,表示这个自定义注解会被保留到class文件,当JVM加载class文件时,这个自定义注解就是被遗弃失效,这也是@Retention默认传入的值
RUNTIME:当@Retention传入RUNTIME时,表示这个自定义注解不仅保存在class文件中,JVM加载完class文件后仍然存在,这就意味着我们可以通过反射去配合这类自定义注解的使用
使用RUNTIME级别的自定义注解开发
平时在自定义注解开发的时候,通常都是使用RUNTIME级别的注解,然后通过SpringAOP+反射机制去监控是否有这个注解,然后再进行后续逻辑处理
使用RUNITIME自定义注解的案例
pom依赖
<!--SpringTest-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--SpringAOP-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
定义一个注解
/**
* @author 小埋
* FUCK注解,RUNTIME级别,作用在方法上
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Fuck {
/**
* 描述
*
* @return
*/
String description() default "描述";
}
然后通过AOP去截取它
@Component
@Aspect
public class CustomizeAspect {
private Logger logger = LoggerFactory.getLogger(CustomizeAspect.class);
/**
* 在方法前记录日志
*
* @param proceedingJoinPoint
* @description ProceedingJoinPoint is only supported for around advice
*/
@Around("@annotation(com.xiaomai.other.customizeAnnotation.Fuck)")
public void advice(ProceedingJoinPoint proceedingJoinPoint) {
//获取执行的方法
Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
logger.info("方法 : {}", method);
//获取类名
String className = proceedingJoinPoint.getSignature().getDeclaringTypeName();
logger.info("类名:{}", className);
//获取方法注解
Fuck annotation = method.getAnnotation(Fuck.class);
logger.info("注解:{}", annotation);
//获取注解参数
String description = annotation.description();
logger.info("注解参数:{}", description);
//获取方法参数名
String[] argsName = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames();
logger.info("方法参数名:{}", argsName[0]);
//获取方法参数值
Object[] args = proceedingJoinPoint.getArgs();
logger.info("参数值:{}", args[0]);
}
}
写个业务方法使用注解
@Component
public class Go {
@Fuck(description = "这是一个描述")
public void go(int param1) {
System.out.println(param1);
}
}
测试
@SpringBootTest
@RunWith(SpringRunner.class)
public class DoTest {
@Autowired
private Go go;
@Test
public void test() {
go.go(1);
}
}
已经可以通过注解去获取方法值了
自定义的注解传入的是SOURCE和CLASS时要怎么自定义操作?
想要做到这一步首先就要理解.java文件到编译为.class文件的过程
在【注解抽象语法树】这个步骤就会解析注解,然后做处理的逻辑,所以想要在编译期间处理注解相关的逻辑,就需要继承AbstractProcessor类并实现process方法,而Lombok就是这样做的