Spring自定义注解

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实现自定义注解。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值