原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9615720.html

  本文目的是简单讲解下Spring AOP的使用。

  推荐使用IDEA + Spring Boot。

  新建Spring Boot 项目,选择Aspect功能。

  创建完成后,POM文件如下:

 <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId>
<artifactId>spring-aspect-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>spring-aspect-demo</name>
<description>Demo project for Spring Boot</description> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>

  然后我们创建目标类和方法:

 package com.example.springaspectdemo;

 import org.springframework.stereotype.Component;

 @Component
public class AspectDemo {
public Integer test(String s){
System.out.println("目标方法执行-"+s);
return 123321;
}
}

  上面的代码中主要的就是@Component注解,在于将目标类扫描到Spring容器中。

  下面我们创建切面类:

 package com.example.springaspectdemo;

 import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component; @Aspect
@Component
public class AspectTest { @Pointcut(value = "execution(* *.test(..)) && args(s)")
public void pc(String s){
System.out.println("切点执行");
} @Before("pc(s)")
public void beforeTest(JoinPoint jp,String s){
System.out.println("前置通知-arg="+s);
} @After("pc(s)")
public void afterTest(String s){
System.out.println("后置终点通知-arg="+s);
} @AfterReturning(pointcut = "pc(s)", returning = "i")
public void afterReturningTest(Object i,String s){
System.out.println("后置返回通知-return="+i+"-arg="+s);
} @AfterThrowing(pointcut = "pc(s)",throwing = "e")
public void afterThrowingTest(Exception e,String s){
System.out.println("后置异常通知-"+e.getMessage()+"-arg="+s);
} @Around("pc(s)")
public void aroundTest(ProceedingJoinPoint jp,String s){
System.out.println("环绕前置通知-arg="+s);
Object[] os = jp.getArgs();
s = "caocao";
os[0] = s;
try {
jp.proceed(os);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕后置通知-arg="+s);
}
}

  创建测试用例:

 package com.example.springaspectdemo;

 import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class)
@SpringBootTest
@EnableAspectJAutoProxy
public class SpringAspectDemoApplicationTests {
@Autowired
private AspectDemo aspectDemo; @Test
public void aspecctTest(){
aspectDemo.test("huahua");
}
}

  执行结果:

环绕前置通知-arg=huahua
前置通知-arg=caocao
目标方法执行-caocao
环绕后置通知-arg=caocao
后置终点通知-arg=caocao
后置返回通知-return=null-arg=caocao

  重点解析:

  1、后置返回通知是在目标代码执行完毕,返回结果之后执行,可以对返回的结果进行处理,但是要注意,其不能和环绕通知一起作用于同一个目标方法,否则会导致无法获取到返回值,正如上面例子中执行结果最后一行的null,表示的就是返回值,如果将环绕通知的部分注释掉,则可以返回正确的结果。

  2、后置返回通知的返回值类型必须是引用类型或者包装类型,不能是原始类型,否则会报错,类型不匹配。

  3、我们可以对目标方法的参数进行修改,但只能在环绕通知中进行,在环绕通知中的第一个参数必然是ProceedJoinPoint,它是JoinPoint的子类,通过其getArgs方法可以获取到调用目标方法的参数列表,可以对其进行修改,然后再执行带参数的proceed方法,将新的参数列表传递到目标方法。而且我们可以从上面的执行结果看到,环绕通知的前置部分是先于其他所有通知而执行的,那么它修改参数之后将会作用于后面所有的通知。正如例子中,我们在环绕通知前置部分将参数"huahua"改成了"caocao",在之后的所有通知和目标方法中获取到的参数全部变成了"caocao"。

  4、异常通知不只是捕捉目标方法中的异常,还有作用于同一方法上的其他通知中发生的异常。所以并不是一旦出现异常就不会执行afterReturning通知方法。如果是目标方法执行正常,却在afterReturning中发生异常的话,那么就会同时执行afterReturning通知方法和afterThrowing通知方法。

  5、我们还可以从上面的执行结果看到各个通知的执行顺序:

    环绕通知前置部分--->前置通知--->目标方法--->环绕通知后置部分--->后置终点通知--->后置返回通知--->后置异常通知

  6、对于上面执行的一点补充:

    那就是发生异常的情况,如果在环绕通知前置部分发生异常,那么之后除了后置终点通知是必然执行的外,只有最后的异常通知会被触发,其余一概不会执行。

    如果第5点中执行顺序哪一步发生了异常,那么其前面的通知会正常执行,后面的除了后置终点通知一定会执行外,异常通知也回被触发。

    但有一个例外,那就是前置通知,在前置通知和环绕通知同时作用于一个目标方法时,前置通知的异常将不会被后置异常通知捕捉到。

  7、终上所述,推荐不要将环绕通知和其他通知一起使用。因为环绕通知会导致一些异常的情况,使其他通知的部分功能失效。

  可以使用上面的代码进行修改测试!

Spring基础系列--AOP实践的更多相关文章

  1. Spring基础系列--AOP织入逻辑跟踪

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9619910.html 其实在之前的源码解读里面,关于织入的部分并没有说清楚,那些前置.后 ...

  2. Spring基础系列-AOP源码分析

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9560803.html 一.概述 Spring的两大特性:IOC和AOP. AOP是面向切 ...

  3. Spring基础系列-Spring事务不生效的问题与循环依赖问题

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9476550.html 一.提出问题 不知道你是否遇到过这样的情况,在ssm框架中开发we ...

  4. Spring基础系列-Web开发

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9996902.html SpringBoot基础系列-web开发 概述 web开发就是集成 ...

  5. Spring Boot系列——AOP配自定义注解的最佳实践

    AOP(Aspect Oriented Programming),即面向切面编程,是Spring框架的大杀器之一. 首先,我声明下,我不是来系统介绍什么是AOP,更不是照本宣科讲解什么是连接点.切面. ...

  6. 【spring基础】AOP概念与动态代理详解

    一.代理模式 代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一 ...

  7. spring基础概念AOP与动态代理理解

    一.代理模式 代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一 ...

  8. Spring基础20——AOP基础

    1.什么是AOP AOP(Aspect-Oriented Programming)即面向切面编程,是一种新的方法论,是对那个传统OOP面向对象编程的补充.AOP的主要编程对象是切面(aspect),而 ...

  9. Spring基础系列-参数校验

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9953744.html Spring中使用参数校验 概述 ​ JSR 303中提出了Bea ...

随机推荐

  1. [NodeJs Windows编译学习]

    https://blog.csdn.net/gesturexiaoxin/article/details/80162944

  2. Kubernetes中的RBAC

    Kubernetes中,授权有ABAC(基于属性的访问控制).RBAC(基于角色的访问控制).Webhook.Node.AlwaysDeny(一直拒绝)和AlwaysAllow(一直允许)这6种模式. ...

  3. Error: Unable to access xxx.jar

    在cmd中运行java -jar xxx.jar出现如下错误: Error: Unable to access xxx.jar 解决方法: 使用绝对路径:java -jar D:\Program Fi ...

  4. angular.js学习笔记(一)

    1.angular单项数据绑定 2.不要使用控制器的时候: 任何形式的DOM操作:控制器只应该包含业务逻辑.DOM操作则属于应用程序的表现层逻辑操作,向来以测试难度之高闻名于业界.把任何表现层的逻辑放 ...

  5. JS加密对应的c#解码

      escape不编码字符有69个:*,+,-,.,/,@,_,0-9,a-z,A-Z encodeURI不编码字符有82个:!,#,$,&,',(,),*,+,,,-,.,/,:,;,=,? ...

  6. CESSNA: Resilient Edge-Computing

    CESSNA: 弹性边缘计算 本文为SIGCOMM 2018 Workshop (Mobile Edge Communications, MECOMM)论文. 笔者翻译了该论文.由于时间仓促,且笔者英 ...

  7. Spring Cloud 微服务架构全链路实践

    阅读目录: 1. 网关请求流程 2. Eureka 服务治理 3. Config 配置中心 4. Hystrix 监控 5. 服务调用链路 6. ELK 日志链路 7. 统一格式返回 Java 微服务 ...

  8. 1.9 From Native to HTML5

    The mobile technology has become more and more mature, and it has evolved from a ridiculous situatio ...

  9. 用Java实现给图片添加文字

    package image; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java. ...

  10. 十几分钟让你学会MySQL布尔和延迟盲注手工操作

    作者:Max老白Gān丶链接:http://www.lofter.com/lpost/1fefbc76_12d25dc31来源:LOFTER 注入常用到的几个函数   1 mid(str,1,3) 字 ...