spring自定义注解-基于SpringBoot AOP条件下自定义、实现注解

生活百科1年前 (2023)发布 aixure
66 0 0

本文介绍SpringBoot条件下,借助于AOP实现自定义注解

spring自定义注解-基于SpringBoot AOP条件下自定义、实现注解

abstract.jpgMeta Annotation元注解

所谓元注解,就是Java提供的、负责修饰其他注解的注解。常见地有:

@Target注解

其定义了注解可以作用的位置,其value属性地常用取值有:

@Target注解的value是数组类型,当只有一个元素时,可以省略数组写法。示例如下所示

// 该注解可用于方法上
@Target(ElementType.METHOD)

// 该注解可用于字段、方法、构造器方法上
@Target({
    ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR
})

@Retention注解

其定义了注解的生命周期。其value属性地常用取值有:

日常开发中spring自定义注解,对于@Retention注解而言。我们用的更多的就是RetentionPolicy.RUNTIME了。示例如下

// 该注解保留到运行时
@Retention(RetentionPolicy.RUNTIME)

@Repeatable注解

默认情况下,注解不可以在同一处重复使用。为此Java 8中引入了@Repeatable注解解决该问题。通过添加@Repeatable注解表示@Family注解可在同一处重复使用。同时,我们需要在@Repeatable注解的值中指定另一个注解@Families。表示可以通过@Families注解的值来包含这个可重复的注解Family。显然此时,@Families中value属性的类型则必须是@Family注解的数组

@Target(ElementType.FIELD) // 该注解用于字段
@Retention(RetentionPolicy.RUNTIME) // 该注解保留到运行时
@Repeatable(Families.class)     // 该注解可重复
public @interface Family 
{
    String value() default "";
}

...

@Target(ElementType.FIELD) // 该注解用于字段
@Retention(RetentionPolicy.RUNTIME) // 该注解保留到运行时
public @interface Families {
    Family[] value();
}

现在,我们就可以在同一处重复使用@Family注解了。下述两种写法均可

public class User {
    @Family("Aaron")
    @Family("Bob")
    private int name1;

    @Families({
        @Family("Aaron"), @Family("Bob")
    })
    private String name2;
}

自定义注解

自定义注解的基本语法格式如下例所示。其中自定义注解可通过下述形式定义注解属性。可通过default指定属性的默认值,如果不指定默认值,则在使用注解时必须显式设置属性值,而无法使用默认值。需注意属性类型仅限下述几种:

/**
 * 自定义注解
 */

@Target(ElementType.METHOD) // 该注解用于方法
@Retention(RetentionPolicy.RUNTIME) // 该注解保留到运行时
public @interface MyLog {
    // 定义类型为long、名为value的属性
    long value();

    // 定义类型为String、名为level的属性, 默认值为 "INFO"
    String level() default "INFO";
}

基于AOP实现注解

完成自定义注解后,我们期望在方法上添加注解,能够在调用方法的前后实现日志输出(包含方法入参、方法结果等信息)。这里我们结合SpringBoot的AOP来实现对自定义注解输出日志的功能

/**
 * 实现@Mylog注解功能的切面类
 */

@Component
@Aspect
@Slf4j
public class MyLogAop {

    @Around"@annotation(com.aaron.SpringBoot1.annotation.MyLog)" )
    public void log(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法信息
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        String methodName = methodSignature.getName();  // 获取方法名
        String[] paramNames = methodSignature.getParameterNames();  // 获取方法参数名
        Object[] args = joinPoint.getArgs();    // 获取方法参数值

        // 获取注解信息
        MyLog myLog = methodSignature.getMethod().getAnnotation( MyLog.class);
        long timestamp = myLog.value();
        String level = myLog.level();

        StringBuilder sb = new StringBuilder();
        for (int i=0; i<paramNames.length; i++) {
            sb.append("<").append(paramNames[i]).append(":").append(args[i]).append(">");
        }

        // 执行开始前打印日志
        String startMsg = "[START] TS: "+ timestamp + " <"+level+">" + " MethodName: " + methodName + " Param: " + sb.toString();
        log.info(startMsg);

        // 调用目标方法
        Object res="Exec Failed";
        try {
            res = joinPoint.proceed();
        } catch (Exception e) {
           log.error("Happen Excep: {}", e.getMessage());
        }

        // 执行完成后打印日志
        String endMsg = "[END] TS: "+ timestamp + " <"+level+">" + " MethodName: " + methodName + " Result: " + res;
        log.info(endMsg);
    }
}

至此,就可以使用该注解了

@RestController
@RequestMapping("test")
public class TestController {

    @MyLog(1995832)
    @GetMapping("/demo1")
    public String test1(@RequestParam(required = false) String firstName, @RequestParam(required = false) String lastName) {
        String res = "Hello";
        if( firstName!=null ) {
            res += " " + firstName;
        }
        if( lastName!=null ) {
            res += " " + lastName;
        }
        return res;
    }
}

发送下述请求

curl "127.0.0.1:8080/test/demo1?firstName=Tony&lastName=Wang"

结果如下所示

spring自定义注解-基于SpringBoot AOP条件下自定义、实现注解

figure 1.jpg

发送下述请求

curl "127.0.0.1:8080/test/demo1?lastName=Zhu"

结果如下所示

spring自定义注解-基于SpringBoot AOP条件下自定义、实现注解

figure 2.jpgNote

如果一个注解中有一个名为value的属性。在使用该注解时,如果只设置value属性的话(要么该注解中只有一个value属性、要么其他属性均使用默认值)spring自定义注解,则可以省略掉属性名value。如下所示

public class Task {    
    @MyLog(22)
    public void runTask1() {
        ...
    }

    @MyLog(value = 22)
    public void runTask2() {
        ...
    }

限时特惠:本站每日持续更新海量各大内部网赚创业教程,会员可以下载全站资源点击查看详情
站长微信:

© 版权声明

相关文章

暂无评论

暂无评论...