元注解
@Target
用于描述注解的使用范围(即:注解可以用在什么地方)
取值(ElementType):
- TYPE:类、接口、枚举、注解
- METHOD:方法
- CONSTRUCTOR:构造器
- LOCAL_VARIABLE:局部变量
- PACKAGE:包
- PARAMERER:方法的参数
- FIELD:属性字段、枚举常量
- ANNOTATION_TYPE:注解
比较常用的是TYPE、METHOD
@Retetion
注解的保留阶段(生命周期)
取值(RetentionPoicy)
- SOURCER:仅存在于源码中,在class字节码中不存在
- CLASS:默认策略,存在class字节码中,但运行时无法获得
- RUNTIME:存在class字节码中,运行时可通过反射获取
自定义注解,只能使用RUNTIME
@Documented
能够将注解中的元素包含到javadoc中去
@Inherited
当一个类被@Inherited标记,如果子类没有被其他注解标记,那么子类会继承父类的注解
注解的属性
- 注解的属性叫成员变量。注解只有成员变量,没有方法
- 注解的成员变量名以“无形参的方法”形式来声明。
- 注解属性可以有默认值,默认值用default关键字指定
- 注解属性只能用public和default修饰
- 注解属性只能用基本数据类型 或 (String、Enum、Class、annotations) 或 以上这些类型的数组
- 属性若没有指定默认值,则注解在被使用时,必须手动指定一个值
如果一个注解内仅有一个成员变量,且变量名为value时,在使用注解时可直接写属性值到括号内
/**
* 注解的属性
* ★ 注解的属性也叫做成员变量。注解只有成员变量,没有方法。
* ★ 注解的成员变量在注解的定义中以“无形参的方法”形式来声明,
* 其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
* 注解中属性可以有默认值,默认值需要用 default 关键值指定。
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationField {
String tableName();//注解的成员变量:无形参方法
// 返回值是成员变量的类型,tableName是成员变量名称
//默认值需要用 default 关键值指定
//以下报错:
//int flag() Default 1;Default需要小写
//int flag default 1; 注解的成员变量在注解的定义中以“无形参的方法”形式来声明,所以需要使用flag()来声明
//Integer flag() default 1;报错原因是因为参数成员 变量只能使用基本的数据类型
// (char,boolean,byte,short,int,float,double)和(String,Enum,Class,annotations)等这些数据类型,以及这一些类型的数组
//注解中所有的成员对象在使用注解时都需要被使用,除了default默认值以外
//因为default值是默认的,我们不需要写,使用注解时也知道这个值是多少
int flag() default 1;
}
解析注解
注解必须通过反射机制进行解析
基础用法
需求:使用一个校验年龄的注解来判断输入的年龄必须小于20岁
1.定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ValidateAge {
int minAge() default 1;
}
2.使用注解
public class TestValidateAge{
@ValidateAge(minAge = 20)
public void validateAge(int age){
System.out.println(processAnnotion(age));
}
// 解析注释
private String processAnnotion(age){
try{
// 通过反射拿到对应的方法
Method validateAge = TestValidateAge.class.getDeclaredMethod("validateAge",int.class);
// 判断方法上是否存在指定注解
boolean isPresent = validateAge.isAnnotationPresent(ValidateAge.class);
if(isPresent){
// 获取方法上的注解
ValidateAge annotation = validateAge.getAnnotation(ValidateAge.class);
// 获取注解中的属性值
int minAge = annotation.minAge();
if(age < minAge){
return "年龄小于"+minAge+",校验通过"
}else{
return "年龄大于"+minAge+",校验不通过"
}
}
}catch(Exception e){
// TODO 异常处理
}
}
}
结合AOP使用
先了解下AOP相关知识点
- JointPoint:切入点(只有可能是方法)
// 获取切入点所在目标对象
Object targetObj = joinPoint.getTarget();
String className = joinPoint.getTarget().getClass().getName();
// 获取切入点方法的名字
String methodName = joinPoint.getSignature().getName();
// 获取方法上的注解
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature)signature;
Method method = methodSignature.getMethod();
xxxx annoObj = method.getAnnotation(xxxx.class);
// 获取方法的参数
Object[] args = joinPoint.getArgs();
- Poincut:切面
- ProceedingJoinPoint: 继承JointPoint,比JointPoint多了proceed方法,而proceed方法是用来触发目标方法执行的。所以ProceedingJoinPoint只适用于环绕通知
结合切面变成记录日志
public class LogAspect {
//@Pointcut("@annotation(com.bowen.annotation.OperationLogDetail)")
@Pointcut("execution(* net.langkaixin.tcloud..controller.*.*(..)) ")
public void operationLog() {
}
/**
* 环绕增强,相当于MethodInterceptor
*/
@Around("operationLog()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object res = null;
long time = System.currentTimeMillis();
try {
res = joinPoint.proceed();
time = System.currentTimeMillis() - time;
return res;
} finally {
try {
//方法执行完成后增加日志
addOperationLog(joinPoint, res, time);
} catch (Exception e) {
log.error("LogAspect 操作失败:" + e.getMessage());
}
}
}
private void addOperationLog(JoinPoint joinPoint, Object res, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
OperationLogDetail annotation = null;
try {
//获取抽象方法
Method method = signature.getMethod();
//获取当前类的对象
Class<?> clazz = joinPoint.getTarget().getClass();
//获取当前类有 OperationLogDetail 注解的方法
method = clazz.getMethod(method.getName(), method.getParameterTypes());
annotation = method.getAnnotation(OperationLogDetail.class);
} catch (Exception e) {
log.error("获取当前类有 OperationLogDetail 注解的方法 异常", e);
}
if (annotation != null) {
OperationLog operationLog = new OperationLog();
operationLog.setLevel(annotation.level());
operationLog.setDescribe(annotation.detail());
//operationLog.setDescribe(getDetail((signature).getParameterNames(),joinPoint.getArgs(),annotation));
operationLog.setOperationType(annotation.operationType().getValue());
operationLog.setOperationUnit(annotation.operationUnit().getValue());
if (annotation.paramType().equals(ParamType.NONE)) {
operationLog.setArgs(null);
operationLog.setReturnValue(null);
}else if(annotation.paramType().equals(ParamType.REQUEST)){
operationLog.setReturnValue(null);
}else if(annotation.paramType().equals(ParamType.RESPONSE)){
operationLog.setArgs(null);
}
}
//TODO 这里保存日志
log.info("记录日志:" + operationLog.toString());
//operationLogService.insert(operationLog);
}
}