java 注解
1、什么是注解
存放在java源码的类、方法、字段、参数的一种特殊注释。
2、元注解
@Target:定义该注解标注与java那个位置,参数为一个或多个
- 类或接口:ElementType.TYPE;
- 字段:ElementType.FIELD;
- 方法:ElementType.METHOD;
- 构造方法:ElementType.CONSTRUCTOR;
- 方法参数:ElementType.PARAMETER。
当定义字段注解标注在方法上会发生什么:编译报错
@Retention
另一个重要的元注解@Retention定义了Annotation的生命周期:默认为CLASS
- 仅编译期:RetentionPolicy.SOURCE;
- 仅class文件:RetentionPolicy.CLASS;
- 运行期:RetentionPolicy.RUNTIME。
@Documented:文档扫描
@Repeatable:该注解可重复标注
@Inherited:定义子类是否可以继承父类标注的注解,仅ElementTypp.TYPE有效
3、自定义注解
@interface : 为定义注解关键字
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SourceTarget {
/**
* 数据源type、field、method
* @return
*/
String source() default "type";
}
4、怎么使用注解
一般通过反射获取到注解携带的信息
在使用反射提供的api时注意注解标记地方的修饰符,private标记的字段用getDeclaredFields()来获取之类的。
根据注解的生命周期进行处理,分别:
- SOURCE类型的注解在编译期就被丢掉了;
- CLASS类型的注解仅保存在class文件中,它们不会被加载进JVM;
- RUNTIME类型的注解会被加载进JVM,并且在运行期可以被程序读取。
获取ElementType.TYPE的注解:
Class cla = userDTO.getClass();
//验证是否带有SourceTarget注解,ElementTpe.TYPE
if(cla.isAnnotationPresent(SourceTarget.class)){
SourceTarget target = (SourceTarget) cla.getAnnotation(SourceTarget.class);
log.info("target:{}",target);
}
获取ElementType.FIELD的注解:
Field[] fields = cla.getDeclaredFields();//注意该方法获取到该字段的访问权限
for(Field field : fields){
if(field.isAnnotationPresent(SourceTarget.class)){
SourceTarget target = field.getAnnotation(SourceTarget.class);
log.info("Field target:{}",target.source());
}
}
获取ElementType.METHOD的注解:
Method[] methods = cla.getMethods();
//Method method1 = cla.getMethod("getKey");
for(Method method : methods){
if(method.getName().equals("getKey")){
//再一步验证
SourceTarget target = method.getAnnotation(SourceTarget.class);
log.info("Method target:{}",target.source());
}
}
java 反射还提供了多种获取字段、注解、方法的api。
5、注解与AOP配合实现
例子:主要是利用aop拦截带有@SourceTarget注解的方法,对该方法返回值中的@DataTarget标注的字段进行一些相应操作。
DataTarget:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DataTarget {
/**
* 键
* @return
*/
String key() default "";
/**
* 值
* @return
*/
String value() default "";
}
SourceTargetAspect:
@Slf4j
@Aspect
@Component
public class SourceTargetAspect {
@Around("@annotation(com.example.springboot.annotation.SourceTarget)")
public Object around(ProceedingJoinPoint point) {
log.info("进入around方法");
Object result = null;
try {
//执行方法
UserDTO userDTO = (UserDTO) point.proceed();
Field[] fields = userDTO.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(DataTarget.class)) {
DataTarget dataTarget = (DataTarget) field.getAnnotation(DataTarget.class);
String key = dataTarget.key();
Field field1 = userDTO.getClass().getDeclaredField(key);
//权限
field1.setAccessible(true);
field.setAccessible(true);
field.set(userDTO, field1.get(userDTO).toString());
}
}
result = userDTO;
} catch (Throwable throwable) {
log.error("SourceTargetAspect is error,message:{}",throwable.getMessage());
throwable.printStackTrace();
}
return result;
}
}
注解与切面配合,注解起到标注和携带相应信息的作用,通过两者结合可以对方法拦截做各种的处理,不过要注意反射的使用。
6、关于注解的思考
注解起一个标记的作用,配合反射来获取注解的标记信息,我们根据该标记的信息执行不同的操作,可能是一些数据的处理、事件的监听等,使用注解时要注意注解标注的访问权限。
关于学习到的一些记录与知识总结