前言

本篇文章主要介绍的是SpringBoot切面Aop的demo简单讲解。

SpringBoot Aop

说明:如果想直接获取工程那么可以直接跳到底部,通过链接下载工程代码。

切面(Aop)

一、概念

AOP(Aspect OrientedProgramming):面向切面编程,面向切面编程(也叫面向方面编程),是目前软件开发中的一个热点,也是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

二、用途

日志记录,性能统计,安全控制,权限管理,事务处理,异常处理,资源池管理。

三、详解

1.切面(Aspect):

官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”,在本例中,“切面”就是类TestAspect所关注的具体行为,例如:AServiceImpl.barA()的调用就是切面TestAspect所关注的行为之一。“切面”在ApplicationContext中aop:aspect来配置。

2.连接点(Joinpoint):

程序执行过程中的某一行为,例如,AServiceImpl.barA()的调用或者BServiceImpl.barB(String _msg, int _type)抛出异常等行为。

3.通知(Advice):

“切面”对于某个“连接点”所产生的动作,例如,TestAspect中对com.spring.service包下所有类的方法进行日志记录的动作就是一个Advice。其中,一个“切面”可以包含多个“Advice”,例如TestAspect。Advice共有如下5种类型:

  • A 前置通知(Before advice) :在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。xml中在aop:aspect里面使用aop:before元素进行声明;例如,TestAspect中的doBefore方法。注解中使用@Before声明;例如,TestAnnotationAspect中的doBefore方法。
  • B 后通知(After advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。xml中在aop:aspect里面使用aop:after元素进行声明。例如,TestAspect中的doAfter方法,所以AOPTest中调用BServiceImpl.barB抛出异常时,doAfter方法仍然执行。注解中使用@After声明。
  • C 返回后通知(After return advice):在某连接点正常完成后执行的通知,不包括抛出异常的情况。xml中在aop:aspect里面使用元素进行声明。注解中使用@AfterReturning声明;
  • D 环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。xml中在aop:aspect里面使用aop:around元素进行声明。例如,TestAspect中的doAround方法。注解中使用@Around声明。
  • E 抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。xml中在aop:aspect里面使用aop:after-throwing元素进行声明。例如,TestAspect中的doThrowing方法。注解中使用@AfterThrowing声明。
  • 通知执行顺序:前置通知→环绕通知连接点之前→连接点执行→环绕通知连接点之后→返回通知→后通知 →(如果发生异常)异常通知→后通知。

4.切入点(Pointcut)

匹配连接点的断言,在AOP中通知和一个切入点表达式关联。例如,TestAspect中的所有通知所关注的连接点,都由切入点表达式execution(* com.spring.service..(..))来决定。

注:以上的理论知识参考:https://www.cnblogs.com/yepei/p/4735298.html

开发准备

环境要求

JDK:1.8

SpringBoot:2.2.6.RELEASE

首先还是Maven的相关依赖,基本和普通springboot项目一样,就是多了spring-boot-starter-aop这jar的依赖。

pom.xml文件如下:

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/>
</parent> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>test</scope>
</dependency>
</dependencies>

application.properties的文件的配置:

banner.charset=UTF-8
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
spring.messages.encoding=UTF-8
spring.application.name=springboot-aspect
server.port=8180

代码编写

SpringBoot在使用切面的时候,只需要在自定义的一个切面类中加上 @Aspect 注解进行声明,然后在自定义切面的类方法中加上对应的注解即可。

比如这里的示例我们想做一个请求响应的加解密切面处理,业务层只需关心代码逻辑实现,而不用关心请求参数和响应参数的加解密实现。那么首先我们需要自定义一个加解密的切面类,在该类添加@Aspect注解,然后在定义一个公共的切入点(Pointcut),指向需要处理的包,然后在定义一个前置通知(添加@Before注解)和后置通知(添加@AfterReturning)方法实现即可。

这里我们就将切入点设置为控制层包里所有的请求。

切入点代码示例:


@Pointcut("execution(public * com.pancm.web.*.*(..))")
public void doOperation() {
}

然后在定义一个前置通知,实现对请求参数的数据解密,这里我们就用User这个实体类的名称,对该数据进行解密。实际在运用是可以根据自身的情况来编写。

前置通知代码示例:

	@Before("doOperation()")
public void before(JoinPoint joinPoint) throws Throwable{
Object[] objs = joinPoint.getArgs();
for (Object obj : objs) {
User user =(User) obj;
System.out.println("前置通知接受的参数:"+user);
String name =base64DeStr(user.getName());
user.setName(name);
}
}

在编写完前置通知的方法之后,我们在编写后置通知的代码,这块基本和前置通知的一样,就是把返回的数据进行加密而已。

后置通知代码示例:


@AfterReturning(returning = "object", pointcut = "doOperation()")
public void doAfterReturning(Object object) {
ResultBody resultBody = (ResultBody) object;
String str =null;
try {
str=base64EnStr(resultBody.getResult());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
resultBody.setResult(str);
System.out.println("后通知响应的参数:"+resultBody);
}

完整代码示例:


@Aspect
@Component
public class ParamAspect { @Pointcut("execution(public * com.pancm.web.*.*(..))")
public void doOperation() { } @Before("doOperation()")
public void before(JoinPoint joinPoint) throws Throwable{
Object[] objs = joinPoint.getArgs();
for (Object obj : objs) {
User user =(User) obj;
System.out.println("前置通知接受的参数:"+user);
String name =base64DeStr(user.getName());
user.setName(name);
}
} @AfterReturning(returning = "object", pointcut = "doOperation()")
public void doAfterReturning(Object object) {
ResultBody resultBody = (ResultBody) object;
String str =null;
try {
str=base64EnStr(resultBody.getResult());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
resultBody.setResult(str);
System.out.println("前置通知响应的参数:"+resultBody);
} public String base64EnStr(String str) throws UnsupportedEncodingException {
return Base64.getEncoder().encodeToString(str.getBytes("UTF-8"));
} public static String base64DeStr(String encodeStr) throws UnsupportedEncodingException {
byte[] decodeStr = Base64.getDecoder().decode(encodeStr);
return new String(decodeStr, "UTF-8");
}

实体类代码


public class User { private Long id; private String name; private Integer age; //getter 和 setter 略 }

控制层z这块的代码和普通的一样,我们这里只需简单的做下处理即可,这里为了方便理解,没有编写service层和dao的代码。:

** 控制层代码**


@RestController
@RequestMapping(value = "/api")
public class UserRestController { @GetMapping("/user")
public ResultBody findByUser(User user) {
System.out.println("用户查询接口请求的参数:"+user);
ResultBody resultBody = new ResultBody();
List<User> userList =new ArrayList<>();
User user2=new User();
user2.setId(1L);
user2.setName("xuwujing");
user2.setAge(18);
userList.add(user2);
resultBody.setCode("0");
resultBody.setResult(userList.toString());
System.out.println("用户查询接口响应的参数:"+resultBody);
return resultBody;
}
}

App 入口

和普通的SpringBoot项目基本一样!

代码如下:


@SpringBootApplication
public class AspectApp
{
public static void main( String[] args )
{
SpringApplication.run(AspectApp.class, args);
System.out.println("Aspect启动成功!");
}
}

功能测试

编写完代码之后,我们启动程序,因为是Get请求,在浏览器或者使用Postman输入地址都可以进行测试,需要注意的是这里我们需要对name的值进行base64加密请求。

输入:

http://localhost:8180/api/user?name=eHV3dWppbmc=

控制台打印:

前置通知接受的参数:{"name":"eHV3dWppbmc="}

用户查询接口请求的参数:{"name":"xuwujing"}

用户查询接口响应的参数:{"code":"0","result":"[{"age":18,"id":1,"name":"xuwujing"}]"}

后通知响应的参数:{"code":"0","result":"W3siYWdlIjoxOCwiaWQiOjEsIm5hbWUiOiJ4dXd1amluZyJ9XQ=="}

请求响应参数:

{

"code": "0",

"message": null,

"result": "W3siYWdlIjoxOCwiaWQiOjEsIm5hbWUiOiJ4dXd1amluZyJ9XQ=="

}

示例图:



其它

关于SpringBoot切面Aop的demo简单讲解的文章就讲解到这里了,如有不妥,欢迎指正!

项目地址

SpringBoot 的aop的项目工程地址:

https://github.com/xuwujing/springBoot-study/tree/master/springboot-aspect

SpringBoot整个集合的地址:

https://github.com/xuwujing/springBoot-study

SpringBoot整合系列的文章

音乐推荐

翩若惊鸿,婉若游龙,荣曜秋菊,华茂春松。仿佛兮若轻云之蔽月,飘飘兮若流风之回雪。远而望之,皎若太阳升朝霞;迫而察之,灼若芙蕖出渌波。--网易云网友评论

原创不易,如果感觉不错,希望给个推荐!您的支持是我写作的最大动力!

版权声明:

作者:虚无境

博客园出处:http://www.cnblogs.com/xuwujing

CSDN出处:http://blog.csdn.net/qazwsxpcm    

个人博客出处:http://www.panchengming.com

SpringBoot切面Aop的demo简单讲解的更多相关文章

  1. 在IDEA 、springboot中使用切面aop实现日志信息的记录到数据库

    文章目录 1.导入相关的依赖 2.创建要保存的数据信息实体类 3 .编写对应的sql语句 4.使用spring 的 aop 技术切到自定义注解上,所以先创建一个自定义注解类 5. 创建aop切面实现类 ...

  2. Springboot的日志管理&Springboot整合Junit测试&Springboot中AOP的使用

    ==============Springboot的日志管理============= springboot无需引入日志的包,springboot默认已经依赖了slf4j.logback.log4j等日 ...

  3. SpringBoot学习笔记(七):SpringBoot使用AOP统一处理请求日志、SpringBoot定时任务@Scheduled、SpringBoot异步调用Async、自定义参数

    SpringBoot使用AOP统一处理请求日志 这里就提到了我们Spring当中的AOP,也就是面向切面编程,今天我们使用AOP去对我们的所有请求进行一个统一处理.首先在pom.xml中引入我们需要的 ...

  4. Spring基础(二)_面向切面(AOP)

    面向切面编程 面向切面编程[AOP,Aspect Oriented Programming]:通过预编译方式和运行期间动态代理实现程序功能的统一维护的技术.AOP 是 Spring 框架中的一个重要内 ...

  5. SpringBoot整合MongoDB(实现一个简单缓存)

    前言 SpringBoot是常用开发框架,而MongoDB也是最近越来越火的非关系型数据库,这里使用SpringBoot+MongoDB实现一个小案例,当然MongoDB实际做缓存的可能不多,但是这里 ...

  6. Puzzle 面向服务/切面(AOP/IOC)开发框架 For .Net

    Puzzle 面向服务/切面AOP开发框架 For .Net AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效 ...

  7. WeX5的简单介绍及UI的简单讲解

    WeX5的简单介绍及UI的简单讲解 (2016-01-13 14:49:05) 标签: it 分类: WeX5的初步自学 一.WeX5的简单讲解 1.WeX5是前端快速开发框架,可开发跨端运行应用.是 ...

  8. Android事件总线分发库EventBus3.0的简单讲解与实践

    Android事件总线分发库EventBus的简单讲解与实践 导语,EventBus大家应该不陌生,EventBus是一款针对Android优化的发布/订阅事件总线.主要功能是替代Intent,Han ...

  9. Spring框架系列(五)--面向切面AOP

    背景: 当需要为多个不具有继承关系的对象引入一个公共行为,例如日志.权限验证.事务等功能时,如果使用OOP,需要为每个对象引入这些公共 行为.会产生大量重复代码,并且不利用维护.AOP就是为了解决这个 ...

随机推荐

  1. 2019-2020-1 20199325《Linux内核原理与分析》第八周作业

    Linux内核如何装载和启动一个可执行程序 1.理解编译链接的过程和ELF可执行文件格式,详细内容参考本周第一节:​ 2.编程使用exec*库函数加载一个可执行文件,动态链接分为可执行程序装载时动态链 ...

  2. 数据包的抓取[tcpdump]的应用

    [root@server ~]# yum install tcpdump [root@server ~]# yum install wireshark 1.默认情况下,直接启动tcpdump将监视第一 ...

  3. opencv-8-图像核与蒙板操作

    opencv-8-图像核与蒙板操作 opencvc++qt 开始之前 在准备开始的时候, 我大概列了一个opencv 章节列表, 按照章节进行写, 写到某些部分的时候再具体调整章节内容, 完成了之后, ...

  4. 微软宣布一批新获得Microsoft Teams认证的会议硬件

    COVID-19 疾病流行期间,微软见到了 Microsoft Teams 视频会议解决方案取得的巨大增长.然而在许多情况下,生产力与音视频硬件的质量息息相关.好消息是,该公司刚刚完成了一批第三方硬件 ...

  5. XmlSerializer .NET 序列化、反序列化

    序列化对象   要序列化对象,首先创建要序列化的对象并设置其公共属性和字段.为此,您必须确定要将XML流存储的传输格式,作为流或文件. 例如,如果XML流必须以永久形式保存,则创建一个FileStre ...

  6. 关于:Express会被Koa2取代吗?

    知会上看到有个问题<Express会被Koa2取代吗?>.刚好对Express.koa有点小研究,于是简单回答了一下. 1.先说结论 目前没有看到Express会被koa2取代的迹象. 目 ...

  7. android自定义View&自定义ViewGroup(上)

    一般自定义view需要重写的方法 void onMeasure(int widthMeasureSpec, int heightMeasureSpec) void onSizeChanged(int ...

  8. STL部分学习总结

    一.map/multimap map/multimap映射容器的元素数据是由一个Key和一个Value成的,key与映照value之间具有一一映照的关系. map/multimap容器的数据结构也采用 ...

  9. 2019 ICPC 南京网络赛 F Greedy Sequence

    You're given a permutation aa of length nn (1 \le n \le 10^51≤n≤105). For each i \in [1,n]i∈[1,n], c ...

  10. FileStream提示文件正在由另一进程使用的解决方法

    文件正在由另一进程使用…… FileStream fs = new FileStream(strFilePath, FileMode.Open,FileAccess.Read,FileShare.Re ...