SpringBoot搭配AOP实现自定义注解

 更新时间:2022年12月09日 08:16:32   作者:糖拌西红柿  
这篇文章主要为大家详细介绍了SpringBoot如何搭配AOP实现自定义注解,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

Java技术迷

1.springBoot的依赖

确定项目中包含可以注解的依赖

1
2
3
4
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.自定义注解的步骤

在项目中自定义注解的步骤主要有两步,第一步:定义注解类,第二步:定义切面

2.1定义注解类

直接创建 @interface的类,使用注解@Target和 @Retention指定其适用范围及保留时长,如下:

1
2
3
4
5
6
7
8
9
10
@Target(ElementType.METHOD) // 指定注解的适用范围
@Retention(RetentionPolicy.RUNTIME) //指定运行时
public @interface ApiLog {
 
    String desc() default "";
 
    boolean timeSpan() default true;
 
 
}

注解类的内容一般很简单,类似于Enum类一样,里面是简单的方法及属性

2.2定义切面

通过@Aspect注解指定一个类,该类必须实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Component
@Aspect
@Slf4j(topic = "ApiLogNote")
public class ElasticSearchExecuteLog {
 
    @Around("@annotation(com.gcc.ApiLog)")
    public Object aroundAdvice(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        //获取被调用方法
        Method method = signature.getMethod();
        //取出被调用方法的注解,方便后续使用注解中的属性
        ApiLog loglinstener = method.getAnnotation(ApiLog.class);
        log.info("----------------------method[{}]start--------------------",method.getName());
        log.info("方法描述:{}",loglinstener.desc());
        log.info("参数 :{}",point.getArgs());
        long startTime = System.currentTimeMillis();
        Object proceed = point.proceed();
        long endTime = System.currentTimeMillis();
        log.info("耗时:{}ss",endTime-startTime);
        log.info("----------------------method[{}] end--------------------\n",method.getName())
        return proceed;
    }
}

2.3使用注解

因为此例子使用的类型为METHOD即方法级的注解,直接在方法上使用即可:

1
2
3
4
5
6
@ApiLog
public JSONObject seachEsData(String indexName, SearchSourceBuilder searchSourceBuilder) {
    JSONObject resultMap = new JSONObject();
    .......
    return resultMap;
}

3.知识点补充

3.1 关于Target注解补充

注解@Target常常配合枚举类ElementType来指定注解的作用位置,也叫合法位置,即你定义了一个注解,这个注解是类注解还是方法注解还是XX注解等,具体作用的范围,取决于@Target({ElementType.TYPE})中,ElementType的枚举值,在进行自定义枚举时,根据自己的需求,决定定义的注解是哪类层级使用的注解,例如上面的例子中,@ApiLog这个自定义的注解就是方法级的注解

ElementType的枚举值有

枚举值含义
TYPE类, 接口 (包括注解类型), 或 枚举 声明
FIELD字段、包括枚举常量
METHOD方法声明
PARAMETER正式的参数声明
CONSTRUCTOR构造函数的声明
LOCAL_VARIABLE局部变量的声明
ANNOTATION_TYPE注解类型的声明
PACKAGE包声明

3.2 关于Retention注解补充

注解@Retention常常配合枚举类RetentionPolic来指定注解的各种策略,注解的保留时间,也就是何时生效,即你定义了一个注解,这个注解是编译时生效还是仅仅只是在代码中标记等等,具体作用的范围,取决于@Retention({RetentionPolic.TYPE})中,RetentionPolic的枚举值,在进行自定义枚举时,大多数都是使用RUNTIME(编译时生效)

RetentionPolic的枚举值

枚举值含义
SOURCE解只在源代码级别保留,编译时被忽略
CLASS注解将被编译器在类文件中记录 , 但在运行时不需要JVM保留。这是默认的
RUNTIME注解将被编译器记录在类文件中,在运行时保留VM,也是使用最多的(一般自定义均使用这个)

3.3 关于AOP的一些概念补充

这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程

切面

切面是一个横切关注点的模块化,一个切面能够包含同一个类型的不同增强方法,比如说事务处理和日志处理可以理解为两个切面。切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。 Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。简单点理解,在SpringBoot中使用了Aspect注解的类就是切面

1
2
3
4
@Component
@Aspect
public class LogAspect {
}

目标对象

目标对象指将要被增强的对象,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。

在我们的例子中,即是使用了@ApiLog注解的地方

连接点

程序执行过程中明确的点,如方法的调用或特定的异常被抛出。连接点由两个信息确定:

方法(表示程序执行点,即在哪个目标方法)

相对点(表示方位,即目标方法的什么位置,比如调用前,后等)

简单来说,连接点就是被拦截到的程序执行点,因为Spring只支持方法类型的连接点,所以在Spring中连接点就是被拦截到的方法。

切入点

切入点是对连接点进行拦截的条件定义。切入点表达式如何和连接点匹配是AOP的核心,Spring缺省使用AspectJ切入点语法。 一般认为,所有的方法都可以认为是连接点,但是我们并不希望在所有的方法上都添加通知,而切入点的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配连接点,给满足规则的连接点添加通知。

1
2
3
4
//此处的匹配规则是 com.remcarpediem.test.aop.service包下的所有类的所有函数。
@Pointcut("execution(* com.remcarpediem.test.aop.service..*(..))")
public void pointcut() {
}

这里切入点的概念其实就是确定对哪些目标对象进行切面插入功能,一开始的例子是采用注解的方式来达到切入**点的作用

1
@Around("@annotation(com.gcc.ApiLog)")

通知

通知是指拦截到连接点之后要执行的代码,包括了“around”、“before”和“after”等不同类型的通知。Spring AOP框架以拦截器来实现通知模型,并维护一个以连接点为中心的拦截器链。

1
2
3
4
5
6
7
8
// @Before说明这是一个前置通知,log函数中是要前置执行的代码,JoinPoint是连接点,
@Before("pointcut()")
public void log(JoinPoint joinPoint) {
}
 
​​​​​​​//@After 为后置通知
 
//@Around 为环绕通知

织入(Weaving)

这里的织入概念是个动作,即Spring将前面的切面、连接点、切入点关联起来并创建通知代理的过程。织入可以在编译时,类加载时和运行时完成。在编译时进行织入就是静态代理,而在运行时进行织入则是动态代理。

增强器(Advisor)

Advisor是切面的另外一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。Advisor由切入点和Advice组成。 Advisor这个概念来自于Spring对AOP的支撑,在AspectJ中是没有等价的概念的。Advisor就像是一个小的自包含的切面,这个切面只有一个通知。切面自身通过一个Bean表示,并且必须实现一个默认接口。

简单来讲,整个 aspect 可以描述为: 满足 pointcut 规则的 joinpoint 会被添加相应的 advice 操作。

将上方通过注解使用切面的方式改写一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Component
@Aspect
@Sl4fj
public class ElasticSearchExecuteLog {
  
  // 不使用注解,而通过基础的规则配置选择切入点,表达式是指com.gcc.controller
 // 包下的所有类的所有方法
 @Pointcut("execution(* com.gcc.controller..*(..))")
 public void aspect() {}
  
  // 通知,在符合aspect切入点的方法前插入如下代码,并且将连接点作为参数传递
 @Before("aspect()")
 public void log(JoinPoint joinPoint) { //连接点作为参数传入
   // 获得类名,方法名,参数和参数名称。
   Signature signature = joinPoint.getSignature();
   String className = joinPoint.getTarget().getClass().getName();
   String methodName = joinPoint.getSignature().getName();
   Object[] arguments = joinPoint.getArgs();
   MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
   String[] argumentNames = methodSignature.getParameterNames();
   StringBuilder sb = new StringBuilder(className + "." + methodName + "(");
       for (int i = 0; i< arguments.length; i++) {
                  Object argument = arguments[i];
                  sb.append(argumentNames[i] + "->");
                  sb.append(argument != null ? argument.toString() : "null ");
       }
   sb.append(")");
   log.info(sb.toString());
  }
}

3.4关于AOP中一些类及函数的使用

JoinPoint对象

JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.

方法作用返回对象
getSignature()获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息Signature
getArgs()获取 获取传入目标方法的参数对象Object[]
getTarget()获取被代理的对象Object

proceedingJoinPoin对象

proceedingJoinPoin对象是JoinPoint的子类,在原本JoinPoint的基础上,放开了Proceeed()的使用,一般在环绕通知@Around时使用:

1
2
Object proceed() throws Throwable //执行目标方法
Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法

以上就是SpringBoot搭配AOP实现自定义注解的详细内容,更多关于SpringBoot AOP自定义注解的资料请关注脚本之家其它相关文章!

蓄力AI

微信公众号搜索 “ 脚本之家 ” ,选择关注

程序猿的那些事、送书等活动等着你

原文链接:https://www.cnblogs.com/TheGCC/p/16954775.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 reterry123@163.com 进行投诉反馈,一经查实,立即处理!

相关文章

  • Java实现发送短信验证码+redis限制发送的次数功能

    Java实现发送短信验证码+redis限制发送的次数功能

    这篇文章主要介绍了Java实现发送短信验证码+redis限制发送的次数,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • CGLIB代理的使用与原理解析

    CGLIB代理的使用与原理解析

    这篇文章主要介绍了CGLIB代理的使用与原理解析,静态代理和JDK 代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理,这就是Cglib代理,需要的朋友可以参考下
    2023-09-09
  • SpringBoot解决ajax跨域问题的方法

    SpringBoot解决ajax跨域问题的方法

    这篇文章主要为大家详细介绍了SpringBoot解决ajax跨域问题的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • 在日志中记录Java异常信息的正确姿势分享

    在日志中记录Java异常信息的正确姿势分享

    这篇文章主要介绍了在日志中记录Java异常信息的正确姿势,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java调用.dll文件的方法

    Java调用.dll文件的方法

    因为项目的需求,要在JAVA项目中调用Windows的Dll(动态链接库)文件,之前用Jni调用过C写的Dll文件,比较麻烦,这里不多说,网上也有很多这方面的文档。在网上找到一个开源的组件JNative,使用后感觉比较方便
    2013-04-04
  • java自动生成接口文档完整代码示例

    java自动生成接口文档完整代码示例

    在软件开发中,编写接口文档是一项必要但繁琐的任务,为了简化这一过程,可以通过使用Swagger2和Swagger-UI来自动生成接口文档,这篇文章主要介绍了java自动生成接口文档的相关资料,需要的朋友可以参考下
    2021-07-07
  • Java编程中静态内部类与同步类的写法示例

    Java编程中静态内部类与同步类的写法示例

    这篇文章主要介绍了Java编程中静态内部类与同步类的写法示例,用于构建静态对象以及实现线程同步等,需要的朋友可以参考下
    2015-09-09
  • SpringBoot使用 Sleuth 进行分布式跟踪的过程分析

    SpringBoot使用 Sleuth 进行分布式跟踪的过程分析

    Spring Boot Sleuth是一个分布式跟踪解决方案,它可以帮助您在分布式系统中跟踪请求并分析性能问题,Spring Boot Sleuth是Spring Cloud的一部分,它提供了分布式跟踪的功能,本文将介绍如何在Spring Boot应用程序中使用Sleuth进行分布式跟踪,感兴趣的朋友一起看看吧
    2023-10-10
  • java基础篇之Date类型最常用的时间计算(相当全面)

    java基础篇之Date类型最常用的时间计算(相当全面)

    这篇文章主要给大家介绍了关于java基础篇之Date类型最常用的时间计算的相关资料,Java中的Date类是用来表示日期和时间的类,它提供了一些常用的方法来处理日期和时间的操作,需要的朋友可以参考下
    2023-12-12
  • Java操作mongodb增删改查的基本操作实战指南

    Java操作mongodb增删改查的基本操作实战指南

    MongoDB是一个基于分布式文件存储的数据库,由c++语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案,下面这篇文章主要给大家介绍了关于Java操作mongodb增删改查的基本操作实战指南,需要的朋友可以参考下
    2023-05-05

最新评论