注解的定义和使用(附带AOP相关知识点)

元注解

@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);
    }
}

借鉴1
借鉴2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值