目录
Spring自定义注解
1. 前言
Spring的一个核心功能是IOC,就是将Bean初始化加载到容器中,Bean是如何加载到容器的,可以使用Spring注解方式或者Spring XML配置方式。Spring注解方式减少了配置文件内容,更加便于管理,并且使用注解可以大大提高了开发效率!更多注解相关知识查看Spring常用注解。
这里来分享一下如何使用AOP来实现自定义注解,首先来了解下什么是AOP。
2. AOP
aspect object programming 面向切面编程,将相同逻辑的重复代码横向抽取出来,使用动态代理技术
将这些重复的代码织入到目标对象方法中,实现和原来一样的功能。一般在事物管理、日志管理等场景下使用,更多知识学习 Spring AOP
3. 常用注解学习
更多注解:@Indicated、@Repeatable 查看 jdk文档
3.1 @Target
定义注解修饰的目标,即用于描述注解的使用范围,常用类型如下,详见 java.lang.annotation.ElementType
-
ElementType.TYPE,类、接口(包括 annotation类型的接口)、枚举
-
ElementType.FIELD,属性
-
ElementType.METHOD,方法
-
ElementType.PARAMETER,参数
-
ElementType.CONSTRUCTOR,构造函数
3.2 @Retention
定义注解的生命周期,即会被保留到哪个阶段,详见 java.lang.annotation.RetentionPolicy
- RetentionPolicy.SOURCE,这种类型的注解只在源代码级别保留,编译器将被忽略
- RetentionPolicy.CLASS,这种类型的注解编译器会被保留,在class文件中存在,但JVM将会忽略
- RetentionPolicy.RUNTIME,这种类型的注解将会被JVM保留,所以它们能在运行时被JVM或其他反射机制的代码所读取和使用
3.3 @Documented
表明这个注解应该被 javadoc 工具记录,默认情况下 javadoc 是不包括注解的,如何使用IDEA生成javadoc
3.4 @Order
定义注解组件的顺序,值越小优先级越高
3.5 @Aspect
定义切面类
4. 实现自定义注解
4.1. 定义注解类
使用 class 定义类,而这注解类使用 @interface 修饰。这里我们定义一个日志注解类,范围是METHOD,生命周期是RUNTIME,用于打印系统接口的访问记录
/**
* @author dkangel
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyLog {
/**
* 日志内容
*/
String logMsg() default "";
/**
* 类名(该方法没有默认值,不赋值会报检查异常)
*/
Class<?> className();
/**
* 日志级别
*/
LogType logType() default LogType.DEBUG;
/**
* 日志级别枚举
*/
enum LogType {
DEBUG,
INFO,
WARN,
ERROR,
FATAL
}
}
4.2. 定义切面类
切面类使用 @Aspect 修饰,这里在环绕中实现对注解内容的解析
@Component
@Aspect
@Order(value = 1)
public class LogAspect {
@Pointcut(value = "@annotation(com.example.demo.annotation.MyLog)")
public void annotationPointCut() {
}
@Around("annotationPointCut()")
public void advice(ProceedingJoinPoint joinPoint) {
System.out.println("环绕开始");
try {
// 获取所有参数
System.out.println("---------------参数列表开始-------------------------");
Object[] args = joinPoint.getArgs();
// 获取到方法的所有参数名称的字符串数组
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String[] parameterNames = signature.getParameterNames();
// 参数列表
for (int i = 0; i < parameterNames.length; i++) {
System.out.println("参数名:" + parameterNames[i] + " = " + args[i]);
}
System.out.println("---------------参数列表结束-------------------------");
// 自定义注解
System.out.println("---------------自定义注解开始-------------------------");
Method method = signature.getMethod();
MyLog myLog = method.getAnnotation(MyLog.class);
System.out.println("logMsg: " + myLog.logMsg());
System.out.println("className: " + myLog.className());
System.out.println("logType: " + myLog.logType());
System.out.println("---------------自定义注解结束-------------------------");
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕结束");
}
@Before(value = "annotationPointCut()")
public void beforePointCut() {
System.out.println("beforePointCut()");
}
@After(value = "annotationPointCut()")
public void afterPointCut() {
System.out.println("afterPointCut()");
}
/**
* 结果返回之后
*
* @param myLog 注解对象
* @param result 业务返回值
*/
@AfterReturning(value = "annotationPointCut() && @annotation(myLog)", returning = "result")
public void afterPointCut(MyLog myLog, Object result) {
System.out.println("AfterReturning()");
System.out.println("logMsg: " + myLog.logMsg());
System.out.println("className: " + myLog.className());
System.out.println("logType: " + myLog.logType());
System.out.println(result);
}
}
4.3. 编写Controller
@RestController(value = "/")
public class TestController {
@MyLog(logMsg = "Enter testController#test()", className = TestController.class, logType = MyLog.LogType.INFO)
@RequestMapping(value = "test", method = RequestMethod.GET)
public String test(@RequestParam(value = "name", required = false, defaultValue = "yc") String name) {
return name;
}
}
4.4. 启动项目,查看结果
访问:http://localhost:8080/test?name=dkangel
结果如下,这里我们使用了自定义注解来打印每个接口的访问记录
环绕开始
---------------参数列表开始-------------------------
参数名:name = dkangel
---------------参数列表结束-------------------------
---------------自定义注解开始-------------------------
logMsg: Enter testController#test()
className: class com.example.demo.controller.TestController
logType: INFO
---------------自定义注解结束-------------------------
beforePointCut()
AfterReturning()
logMsg: Enter testController#test()
className: class com.example.demo.controller.TestController
logType: INFO
dkangel
afterPointCut()
环绕结束
到这里我们就实现了如何使用AOP实现自定义注解。