SpringBoot中搭配AOP实现自定义注解
1 springBoot的依赖
确定项目中包含可以注解的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2 自定义注解的步骤
在项目中自定义注解的步骤主要有两步,第一步:定义注解类,第二步:定义切面
2.1 定义注解类
直接创建 @interface 的类,使用注解@Target和 @Retention指定其适用范围及保留时长,如下:
@Target(ElementType.METHOD) // 指定注解的适用范围
@Retention(RetentionPolicy.RUNTIME) //指定运行时
public @interface ApiLog {
String desc() default "";
boolean timeSpan() default true;
}
注解类的内容一般很简单,类似于Enum类一样,里面是简单的方法及属性
2.2 定义切面
通过@Aspect注解指定一个类,该类必须实现
@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即方法级的注解,直接在方法上使用即可:
@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的一些概念补充
这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程
切面(Aspect)
切面是一个横切关注点的模块化,一个切面能够包含同一个类型的不同增强方法,比如说事务处理和日志处理可以理解为两个切面。切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。 Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。简单点理解,在SpringBoot中使用了Aspect注解的类就是切面
@Component
@Aspect
public class LogAspect {
}
目标对象(target)
目标对象指将要被增强的对象,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。
在我们的例子中,即是使用了@ApiLog注解的地方
连接点(JoinPoint)
程序执行过程中明确的点,如方法的调用或特定的异常被抛出。连接点由两个信息确定:
- 方法(表示程序执行点,即在哪个目标方法)
- 相对点(表示方位,即目标方法的什么位置,比如调用前,后等)
简单来说,连接点就是被拦截到的程序执行点,因为Spring只支持方法类型的连接点,所以在Spring中连接点就是被拦截到的方法。
切入点(PointCut)
切入点是对连接点进行拦截的条件定义。切入点表达式如何和连接点匹配是AOP的核心,Spring缺省使用AspectJ切入点语法。 一般认为,所有的方法都可以认为是连接点,但是我们并不希望在所有的方法上都添加通知,而切入点的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配连接点,给满足规则的连接点添加通知。
//此处的匹配规则是 com.remcarpediem.test.aop.service包下的所有类的所有函数。
@Pointcut("execution(* com.remcarpediem.test.aop.service..*(..))")
public void pointcut() {
}
这里切入点的概念其实就是确定对哪些目标对象进行切面插入功能,一开始的例子是采用注解的方式来达到切入**点的作用
@Around("@annotation(com.gcc.ApiLog)")
通知(Advice)
通知是指拦截到连接点之后要执行的代码,包括了“around”、“before”和“after”等不同类型的通知。Spring AOP框架以拦截器来实现通知模型,并维护一个以连接点为中心的拦截器链。
// @Before说明这是一个前置通知,log函数中是要前置执行的代码,JoinPoint是连接点,
@Before("pointcut()")
public void log(JoinPoint joinPoint) {
}
//@After 为后置通知
//@Around 为环绕通知
织入(Weaving)
这里的织入概念是个动作,即Spring将前面的切面、连接点、切入点关联起来并创建通知代理的过程。织入可以在编译时,类加载时和运行时完成。在编译时进行织入就是静态代理,而在运行时进行织入则是动态代理。
增强器(Adviser)
Advisor是切面的另外一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。Advisor由切入点和Advice组成。 Advisor这个概念来自于Spring对AOP的支撑,在AspectJ中是没有等价的概念的。Advisor就像是一个小的自包含的切面,这个切面只有一个通知。切面自身通过一个Bean表示,并且必须实现一个默认接口。
简单来讲,整个 aspect 可以描述为: 满足 pointcut 规则的 joinpoint 会被添加相应的 advice 操作。
将上方通过注解使用切面的方式改写一下:
@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 |
ProceedingJoinPoint对象
proceedingJoinPoin对象是JoinPoint的子类,在原本JoinPoint的基础上,放开了Proceeed()的使用,一般在环绕通知@Around时使用:
Object proceed() throws Throwable //执行目标方法
Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法
SpringBoot中搭配AOP实现自定义注解的更多相关文章
- Springboot中使用AOP统一处理Web请求日志
title: Springboot中使用AOP统一处理Web请求日志 date: 2017-04-26 16:30:48 tags: ['Spring Boot','AOP'] categories: ...
- SpringBoot图文教程5—SpringBoot 中使用Aop
有天上飞的概念,就要有落地的实现 概念+代码实现是本文的特点,教程将涵盖完整的图文教程,代码案例 文章结尾配套自测面试题,学完技术自我测试更扎实 概念十遍不如代码一遍,朋友,希望你把文中所有的代码案例 ...
- 编写SpringBoot 中的AOP
编写SpringBoot 中的AOP 在程序开发的过程中会使用到AOP的思想,面向切面进行开发,比如登录的验证,记录日志等等-频繁需要操作的步骤,在遇到这种情况时就要使用Spring 的AOP了 Sp ...
- 在SpringBoot中配置aop
前言 aop作为spring的一个强大的功能经常被使用,aop的应用场景有很多,但是实际的应用还是需要根据实际的业务来进行实现.这里就以打印日志作为例子,在SpringBoot中配置aop 已经加入我 ...
- 用AOP拦截自定义注解并获取注解属性与上下文参数(基于Springboot框架)
目录 自定义注解 定义切面 获取上下文信息JoinPoint ProceedingJoinPoint 定义测试方法 测试结果 小结 AOP可以用于日志的设计,这样话就少不了要获取上下文的信息,博主在设 ...
- 【spring boot】SpringBoot初学(6)– aop与自定义注解
前言 github: https://github.com/vergilyn/SpringBootDemo 一.AOP 官方demo:https://github.com/spring-project ...
- springboot通过AOP和自定义注解实现权限校验
自定义注解 PermissionCheck: package com.mgdd.sys.annotation; import java.lang.annotation.*; /** * @author ...
- spring AOP 和自定义注解进行身份验证
一个SSH的项目(springmvc+hibernate),需要提供接口给app使用.首先考虑的就是权限问题,app要遵循极简模式,部分内容无需验证,用过滤器不能解决某些无需验证的方法 所以最终选择用 ...
- Spring Boot系列——AOP配自定义注解的最佳实践
AOP(Aspect Oriented Programming),即面向切面编程,是Spring框架的大杀器之一. 首先,我声明下,我不是来系统介绍什么是AOP,更不是照本宣科讲解什么是连接点.切面. ...
- 使用AOP获取自定义注解的内容
目录结构: 一:自定义注解 package org.example.annotation; import java.lang.annotation.ElementType; import java.l ...
随机推荐
- 第六章:Django 综合篇 - 6:会话session
因为因特网HTTP协议的特性,每一次来自于用户浏览器的请求(request)都是无状态的.独立的.通俗地说,就是无法保存用户状态,后台服务器根本就不知道当前请求和以前及以后请求是否来自同一用户.对于静 ...
- 用VS Code搞Qt 6:Gui基础类型——QGuiApplication和QWindow
在99.996%的情况下,我们弄 Qt 应用都会使用 QApplication 类和 QWidget 类,即直接用 Widgets 库中的组件/控件.为了方便开发人员自己造轮子,Qt 也提供了一套基础 ...
- Vue实现拖拽穿梭框功能四种方式
一.使用原生js实现拖拽 点击打开视频讲解更加详细 <html lang="en"> <head> <meta charset="UTF-8 ...
- 为什么我写的z-index不生效?
前言 相信大家在工作中都遇到过这样一些奇怪的问题: 1.为什么我写的z-index没有生效? 2.为什么z-index大的元素却没有盖住z-index小的元素? 3.如何让父元素盖住子元素呢? 以上这 ...
- sql面试50题------(11-20)
文章目录 11.查询至少有一门课与学号为'01'的学生所学课程相同的学生的学号和姓名 12.查询和'01'号同学所学课程完全相同的其他同学的学号 13.查询两门及其以上不及格课程的同学的学号,姓名及其 ...
- 一篇文章带你了解服务器操作系统——Linux简单入门
一篇文章带你了解服务器操作系统--Linux简单入门 Linux作为服务器的常用操作系统,身为工作人员自然是要有所了解的 在本篇中我们会简单介绍Linux的特点,安装,相关指令使用以及内部程序的安装等 ...
- 【一】ERNIE:飞桨开源开发套件,入门学习,看看行业顶尖持续学习语义理解框架,如何取得世界多个实战的SOTA效果?
参考文章: 深度剖析知识增强语义表示模型--ERNIE_财神Childe的博客-CSDN博客_ernie模型 ERNIE_ERNIE开源开发套件_飞桨 https://github.com/Pad ...
- 还在用双层for循环吗?太慢了
前情提要 我们在开发中经常碰到这样的场景,查出两个 list 集合数据,需要根据他们相同的某个属性为连接点,进行聚合.但是平时我们使用的时候关注过性能吗?下面让我们一起来看看它的表现如何. 来个例子 ...
- Educational Codeforces Round 130 (Rated for Div. 2) C. awoo's Favorite Problem
https://codeforc.es/contest/1697/problem/C 因为规则中,两种字符串变换都与'b'有关,所以我们根据b的位置来进行考虑: 先去掉所有的'b',如果两字符串不相等 ...
- Python学习之实例3
一.文字读取并打印拼接字符串 1 with open('G:\python\char.txt') as f: #使用open()函数以只读模式打开文件 2 s=f.read() #使用read()方法 ...