本文介绍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"
结果如下所示
figure 1.jpg
发送下述请求
curl "127.0.0.1:8080/test/demo1?lastName=Zhu"
结果如下所示
figure 2.jpgNote
如果一个注解中有一个名为value的属性。在使用该注解时,如果只设置value属性的话(要么该注解中只有一个value属性、要么其他属性均使用默认值)spring自定义注解,则可以省略掉属性名value。如下所示
public class Task {
@MyLog(22)
public void runTask1() {
...
}
@MyLog(value = 22)
public void runTask2() {
...
}
限时特惠:本站每日持续更新海量各大内部网赚创业教程,会员可以下载全站资源点击查看详情
站长微信: