原创作品,可以转载,但是请标注出处地址: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. 一文教你看懂大数据的技术生态圈:Hadoop,hive,spark

    转自:https://www.cnblogs.com/reed/p/7730360.html 大数据本身是个很宽泛的概念,Hadoop生态圈(或者泛生态圈)基本上都是为了处理超过单机尺度的数据处理而诞 ...

  2. 201771010126 王燕《面向对象程序设计(Java)》第十二周学习总结

    实验十二  图形程序设计 实验时间 2018-11-14 1.实验目的与要求 (1) 掌握Java GUI中框架创建及属性设置中常用类的API: 创建空框架 . 在Java中,常采用框架(Frame) ...

  3. 快速搭建react项目骨架(按需加载、redux、axios、项目级目录等等)

    一.前言 最近整理了一下项目骨架,顺便自定义了一个脚手架,方便日后使用.我会从头开始,步骤一步步写明白,如果还有不清楚的可以评论区留言.先大致介绍一下这个骨架,我们采用 create-react-ap ...

  4. 巧用PHP中__get()魔术方法

    PHP中的魔术方法有很多,这些魔术方法可以让PHP脚本在某些特定的情况下自动调用.比如 __construct() 每次实例化一个类都会先调用该方法进行初始化.这里我们讲一下__get() 魔术方法的 ...

  5. Oracle报错ORA-12516 TNS:listener could not find available handler with matching protocol stack

    解决办法定位原因-- 以sysdba身份登陆PL/SQL sqlplus / as sysdba;-- 查看当前连接进程数SQL>select count(*) from v$process;- ...

  6. 过滤html标签

    public static String delHTMLTag(String htmlStr){ String regEx_script="<script[^>]*?>[\ ...

  7. VBS列出windows更新列表

    Set objSession = CreateObject("Microsoft.Update.Session") Set objSearcher = objSession.Cre ...

  8. FCC(ES6写法) Inventory Update

    依照一个存着新进货物的二维数组,更新存着现有库存(在 arr1 中)的二维数组. 如果货物已存在则更新数量 . 如果没有对应货物则把其加入到数组中,更新最新的数量. 返回当前的库存数组,且按货物名称的 ...

  9. DCOS实践分享(6):基于DCOS的大数据应用分享

    Open DC/OS大中华区官方发布会在京隆重召开   DCOS领域诞生了一个100%开源的企业级Datacenter Operating System版本,即DC/OS.Linker Network ...

  10. 最快效率求出乱序数组中第k小的数

    题目:以尽量高的效率求出一个乱序数组中按数值顺序的第k 的元素值 思路:这里很容易想到直接排序然后顺序查找,可以使用效率较高的快排,但是它的时间复杂度是O(nlgn),我们这里可以用一种简便的方法,不 ...