16

SpringBoot切面Aop的demo简单讲解

 4 years ago
source link: http://www.cnblogs.com/xuwujing/p/12927081.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

本篇文章主要介绍的是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=="  }

示例图:

FrQzQjq.png!webb2UZvuV.png!web

其它

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

项目地址

SpringBoot整个集合的地址:

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

SpringBoot整合系列的文章

音乐推荐

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

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

版权声明:

作者:虚无境

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

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK