1.前置通知

在目标方法执行之前执行执行的通知。

前置通知方法,可以没有参数,也可以额外接收一个JoinPoint,Spring会自动将该对象传入,代表当前的连接点,通过该对象可以获取目标对象 和 目标方法相关的信息。

注意,如果接收JoinPoint,必须保证其为方法的第一个参数,否则报错。

配置方式:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
"> <context:annotation-config></context:annotation-config>
<context:component-scan base-package="cn.tedu.service,cn.tedu.aop"></context:component-scan> <aop:config proxy-target-class="true">
<!-- 配置切入点 -->
<aop:pointcut
expression="execution(* cn.tedu.service.UserServiceImpl.addUser(..))"
id="pc01"/> <!-- 配置切面 -->
<aop:aspect ref="firstAspect">
       <<!-- 前置通知 -->
<aop:before method="before" pointcut-ref="pc01"/> </aop:aspect>
</aop:config>
</beans>
package cn.tedu.service;

import org.springframework.stereotype.Service;
/**
* UserServiceImple:目标对象
*/
@Service("userService")
public class UserServiceImple implements UserService { @Override
public void addUser(String name) {
System.out.println("增加用户。。");
} @Override
public void updateUser() {
System.out.println("修改用户。。");
} @Override
public void deleteUser() {
System.out.println("删除用户。。");
} @Override
public void query() {
System.out.println("查询用户。。");
}
}
package cn.tedu.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.springframework.stereotype.Component;
/**
* FirstAspect:切面代码
*/
@Component
public class FirstAspect {
public void before(JoinPoint jp){ // 可以选择额外的传入一个JoinPoint连接点对象,必须用方法的第一个参数接收。
Class clz = jp.getTarget().getClass();
Signature signature = jp.getSignature(); // 通过JoinPoint对象获取更多信息
String name = signature.getName();
System.out.println("1 -- before...["+clz+"]...["+name+"]...");
}
}
package cn.tedu.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.tedu.service.UserService;
/**
* AOPTest:测试代码
*/
public class AOPTest {
@Test
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.addUser("cjj"); // 一个连接点
}
}

执行结果:

1 -- before...[class cn.tedu.service.UserServiceImple]...[addUser]...
增加用户。。

2.环绕通知

在目标方法执行之前和之后都可以执行额外代码的通知。

在环绕通知中必须显式的调用目标方法,目标方法才会执行,这个显式调用时通过ProceedingJoinPoint来实现的,可以在环绕通知中接收一个此类型的形参,spring容器会自动将该对象传入,注意这个参数必须处在环绕通知的第一个形参位置。

**要注意,只有环绕通知可以接收ProceedingJoinPoint,而其他通知只能接收JoinPoint。

环绕通知需要返回返回值,否则真正调用者将拿不到返回值,只能得到一个null。

环绕通知有控制目标方法是否执行、有控制是否返回值、有改变返回值的能力。

环绕通知虽然有这样的能力,但一定要慎用,不是技术上不可行,而是要小心不要破坏了软件分层的“高内聚 低耦合”的目标。

配置方式:

 <!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pc1"/>
    public Object around(ProceedingJoinPoint jp) throws Throwable{
System.out.println("1 -- around before...");
Object obj = jp.proceed(); //--显式的调用目标方法
System.out.println("1 -- around after...");
return obj;
}

运行结果:

1 -- around before...
增加用户。。
1 -- around after...

3.后置通知

在目标方法执行之后执行的通知。

在后置通知中也可以选择性的接收一个JoinPoint来获取连接点的额外信息,但是这个参数必须处在参数列表的第一个。

配置方式:

<!-- 后置通知 -->
<aop:after-returning method="afterReturn" pointcut-ref="pc1"/>
    public void afterReturn(JoinPoint jp){
Class clz = jp.getTarget().getClass();
Signature signature = jp.getSignature();
String name = signature.getName();
System.out.println("1 -- afterReturn...["+clz+"]...["+name+"]...");
}

执行结果:

1 -- before...[class cn.tedu.service.UserServiceImple]...[addUser]...
1 -- around before...
增加用户。。
1 -- around after...
1 -- afterReturn...[class cn.tedu.service.UserServiceImple]...[addUser]...

在后置通知中,还可以通过配置获取返回值

一定要保证JoinPoint处在参数列表的第一位,否则抛异常

配置方式:

<!-- 后置通知 -->
<aop:after-returning method="afterReturn" pointcut-ref="pc1" returning="msg"/>
    public void afterReturn(JoinPoint jp, Object msg){
Class clz = jp.getTarget().getClass();
Signature signature = jp.getSignature();
String name = signature.getName();
System.out.println("1 -- afterReturn...["+clz+"]...["+name+"]...["+msg+"]...");
}

执行结果:

1 -- before...[class cn.tedu.service.UserServiceImple]...[addUser]...
1 -- around before...
增加用户。。
1 -- around after...
1 -- afterReturn...[class cn.tedu.service.UserServiceImple]...[addUser]...[cjj]...

4.异常通知

在目标方法抛出异常时执行的通知

可以配置传入JoinPoint获取目标对象和目标方法相关信息,但必须处在参数列表第一位

另外,还可以配置参数,让异常通知可以接收到目标方法抛出的异常对象。

配置方法:

<!-- 异常通知 -->
<aop:after-throwing method="afterThrow" pointcut-ref="pc1" throwing="e"/>
    public void afterThrow(JoinPoint jp,Throwable e){
Class clz = jp.getTarget().getClass();
String name = jp.getSignature().getName();
System.out.println("1afterThrow..["+clz+"]..["+name+"].."+e.getMessage());
}

代码报异常后

执行结果:

1 -- before...[class cn.tedu.service.UserServiceImple]...[addUser]...
1 -- around before...
1 -- afterThrow..[class cn.tedu.service.UserServiceImple]..[addUser]../ by zero

5.最终通知

是在目标方法执行之后执行的通知。

和后置通知不同之处在于,后置通知是在方法正常返回后执行的通知,如果方法没有正常返-例如抛出异常,则后置通知不会执行。

而最终通知无论如何都会在目标方法调用过后执行,即使目标方法没有正常的执行完成。

另外,后置通知可以通过配置得到返回值,而最终通知无法得到。

最终通知也可以额外接收一个JoinPoint参数,来获取目标对象和目标方法相关信息,但一定要保证必须是第一个参数。

配置方式:

<!-- 最终通知 -->
<aop:after method="after" pointcut-ref="pc1" />
    public void after(JoinPoint jp){
Class clz = jp.getTarget().getClass();
String name = jp.getSignature().getName();
System.out.println("1 -- after..["+clz+"]..["+name+"]...");
}

执行结果:

1 -- before...[class cn.tedu.service.UserServiceImple]...[addUser]...
1 -- around before...
增加用户。。
1 -- around after...
1 -- afterReturn...[class cn.tedu.service.UserServiceImple]...[addUser]...[cjj]...
1 -- after..[class cn.tedu.service.UserServiceImple]..[addUser]...
cjj

源码

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd "
> <context:annotation-config></context:annotation-config>
<context:component-scan base-package="cn.tedu.service,cn.tedu.aop"></context:component-scan> <!-- proxy-target-class属性值决定是基于接口的还是基于类的代理被创建 -->
<aop:config proxy-target-class="true">
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* cn.tedu.service.UserServiceImple.addUser(..))" id="pc1"/> <!-- 配置切入面 -->
<aop:aspect ref="firstAspect">
<!-- 前置通知 -->
<aop:before method="before" pointcut-ref="pc1"/> <!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pc1"/> <!-- 后置通知 -->
<!-- <aop:after-returning method="afterReturn" pointcut-ref="pc1"/> -->
<aop:after-returning method="afterReturn" pointcut-ref="pc1" returning="msg"/> <!-- 异常通知 -->
<aop:after-throwing method="afterThrow" pointcut-ref="pc1" throwing="e"/> <!-- 最终通知 -->
<aop:after method="after" pointcut-ref="pc1" />
</aop:aspect> </aop:config> </beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd "
> <context:annotation-config></context:annotation-config>
<context:component-scan base-package="cn.tedu.service,cn.tedu.aop"></context:component-scan> <!-- proxy-target-class属性值决定是基于接口的还是基于类的代理被创建 -->
<aop:config proxy-target-class="true">
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* cn.tedu.service.UserServiceImple.addUser(..))" id="pc1"/> <!-- 配置切入面 -->
<aop:aspect ref="firstAspect">
<!-- 前置通知 -->
<aop:before method="before" pointcut-ref="pc1"/> <!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pc1"/> <!-- 后置通知 -->
<!-- <aop:after-returning method="afterReturn" pointcut-ref="pc1"/> -->
<aop:after-returning method="afterReturn" pointcut-ref="pc1" returning="msg"/> <!-- 异常通知 -->
<aop:after-throwing method="afterThrow" pointcut-ref="pc1" throwing="e"/> <!-- 最终通知 -->
<aop:after method="after" pointcut-ref="pc1" />
</aop:aspect> </aop:config> </beans>
package cn.tedu.service;
/**
* 接口
*/
public interface UserService {
public String addUser(String name);
public void updateUser();
public void deleteUser();
public void query();
}
package cn.tedu.service;
/**
* 接口
*/
public interface UserService {
public String addUser(String name);
public void updateUser();
public void deleteUser();
public void query();
}
package cn.tedu.service;

import org.springframework.stereotype.Service;
/**
* UserServiceImple:目标对象
*/
@Service("userService")
public class UserServiceImple implements UserService { @Override
public String addUser(String name) {
// int i = 1/0;
System.out.println("增加用户。。");
return "cjj";
} @Override
public void updateUser() {
System.out.println("修改用户。。");
} @Override
public void deleteUser() {
System.out.println("删除用户。。");
} @Override
public void query() {
System.out.println("查询用户。。");
}
}
package cn.tedu.service;

import org.springframework.stereotype.Service;
/**
* UserServiceImple:目标对象
*/
@Service("userService")
public class UserServiceImple implements UserService { @Override
public String addUser(String name) {
// int i = 1/0;
System.out.println("增加用户。。");
return "cjj";
} @Override
public void updateUser() {
System.out.println("修改用户。。");
} @Override
public void deleteUser() {
System.out.println("删除用户。。");
} @Override
public void query() {
System.out.println("查询用户。。");
}
}
package cn.tedu.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.springframework.stereotype.Component;
/**
* FirstAspect:切面代码
*/
@Component
public class FirstAspect {
public void before(JoinPoint jp){ // 可以选择额外的传入一个JoinPoint连接点对象,必须用方法的第一个参数接收。
Class clz = jp.getTarget().getClass();
Signature signature = jp.getSignature(); // 通过JoinPoint对象获取更多信息
String name = signature.getName();
System.out.println("1 -- before...["+clz+"]...["+name+"]...");
} public Object around(ProceedingJoinPoint jp) throws Throwable{
System.out.println("1 -- around before...");
Object obj = jp.proceed(); //--显式的调用目标方法
System.out.println("1 -- around after...");
return obj;
} public void afterReturn(JoinPoint jp, Object msg){
Class clz = jp.getTarget().getClass();
Signature signature = jp.getSignature();
String name = signature.getName();
System.out.println("1 -- afterReturn...["+clz+"]...["+name+"]...["+msg+"]...");
} public void afterThrow(JoinPoint jp,Throwable e){
Class clz = jp.getTarget().getClass();
String name = jp.getSignature().getName();
System.out.println("1 -- afterThrow..["+clz+"]..["+name+"].."+e.getMessage());
} public void after(JoinPoint jp){
Class clz = jp.getTarget().getClass();
String name = jp.getSignature().getName();
System.out.println("1 -- after..["+clz+"]..["+name+"]...");
}
}
package cn.tedu.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.springframework.stereotype.Component;
/**
* FirstAspect:切面代码
*/
@Component
public class FirstAspect {
public void before(JoinPoint jp){ // 可以选择额外的传入一个JoinPoint连接点对象,必须用方法的第一个参数接收。
Class clz = jp.getTarget().getClass();
Signature signature = jp.getSignature(); // 通过JoinPoint对象获取更多信息
String name = signature.getName();
System.out.println("1 -- before...["+clz+"]...["+name+"]...");
} public Object around(ProceedingJoinPoint jp) throws Throwable{
System.out.println("1 -- around before...");
Object obj = jp.proceed(); //--显式的调用目标方法
System.out.println("1 -- around after...");
return obj;
} public void afterReturn(JoinPoint jp, Object msg){
Class clz = jp.getTarget().getClass();
Signature signature = jp.getSignature();
String name = signature.getName();
System.out.println("1 -- afterReturn...["+clz+"]...["+name+"]...["+msg+"]...");
} public void afterThrow(JoinPoint jp,Throwable e){
Class clz = jp.getTarget().getClass();
String name = jp.getSignature().getName();
System.out.println("1 -- afterThrow..["+clz+"]..["+name+"].."+e.getMessage());
} public void after(JoinPoint jp){
Class clz = jp.getTarget().getClass();
String name = jp.getSignature().getName();
System.out.println("1 -- after..["+clz+"]..["+name+"]...");
}
}
package cn.tedu.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.tedu.service.UserService;
/**
* AOPTest:测试代码
*/
public class AOPTest {
@Test
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
String result = userService.addUser("cjj"); // 一个连接点
System.out.println(result);
}
}
package cn.tedu.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.tedu.service.UserService;
/**
* AOPTest:测试代码
*/
public class AOPTest {
@Test
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
String result = userService.addUser("cjj"); // 一个连接点
System.out.println(result);
}
}

五种通知的执行顺序

1.在目标方法没有抛出异常的情况下

前置通知

环绕通知的调用目标方法之前的代码

目标方法

环绕通知的调用目标方法之后的代码

后置通知

最终通知

2.在目标方法抛出异常的情况下

前置通知

环绕通知的调用目标方法之前的代码

目标方法 抛出异常 异常通知

最终通知

3.如果存在多个切面

多切面执行时,采用了责任链设计模式。

切面的配置顺序决定了切面的执行顺序,多个切面执行的过程,类似于方法调用的过程,在环绕通知的proceed()执行时,去执行下一个切面或如果没有下一个切面执行目标方法,从而达成了如下的执行过程:

如果目标方法抛出异常:

五种通知的常见使用场景

环绕通知

控制事务 权限控制

后置通知

记录日志(方法已经成功调用)

异常通知

异常处理 控制事务

最终通知

记录日志(方法已经调用,但不一定成功)

 
 
posted on 2018

Spring AOP 五大通知类型的更多相关文章

  1. 利用Spring AOP的通知类型以及创建通知

    写在最前端 1.SpringAOP中共有六种通知类型,只要我们自定义一个类实现对应的接口,它们全都是org.springframework.aop包中的. 2.AOP的连接点可以是方法调用.方法调用本 ...

  2. JAVA-Spring AOP五大通知类型

    一.前置通知 在目标方法执行之前执行的通知 在前置通知方法,可以没有参数,也可以额外接收一个JoinPoint,Spring会自动将该对象传入,代表当前的连接点,通过该对象可以获取目标对象和目标方法相 ...

  3. Spring AOP 四大通知

    Spring AOP 四大通知 Spring 3.X 以前 1.前置通知,实现  MethodBeforeAdvice 接口,重写 public  void  before(Method  metho ...

  4. Spring笔记07(Spring AOP的通知advice和顾问advisor)

    1.Spring AOP的通知advice 01.接口代码: package cn.pb.dao; public interface UserDao { //主业务 String add(); //主 ...

  5. Spring AOP前置通知和后置通知

    Spring AOP AspectJ:Java社区里最完整最流行的AOP框架 在Spring2.0以上的版本中,可以使用基于AspectJ注解或基于XML配置的AOP 在Spring中启用Aspect ...

  6. 【Spring AOP】通知(五)

    一.通知介绍 1. 前置通知(Before) 在目标方法执行之前执行的通知. 前置通知方法,可以没有参数,也可以额外接收一个JoinPoint,Spring会自动将该对象传入,代表当前的连接点,通过该 ...

  7. Spring AOP(通知、连接点、切点、切面)

    一.AOP术语 通知(Advice)  切面的工作被称为通知.通知定义了切面是什么以及何时使用.除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题.5种通知类型: 前置通知(Before): ...

  8. spring aop 环绕通知around和其他通知的区别

    前言: spring 的环绕通知和前置通知,后置通知有着很大的区别,主要有两个重要的区别: 1) 目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知   是不能决定的,他们只 ...

  9. spring aop环绕通知

    [Spring实战]—— 9 AOP环绕通知   假如有这么一个场景,需要统计某个方法执行的时间,如何做呢? 典型的会想到在方法执行前记录时间,方法执行后再次记录,得出运行的时间. 如果采用Sprin ...

随机推荐

  1. 解决Maven无法下载fastdfs-client-java依赖,Dependency 'org.csource:fastdfs-client-java:1.27-SNAPSHOT' not found.

    因为fastdfs-client-java-1.27-SNAPSHOT.jar这个依赖包在maven中央仓库是没有的, 需要自己编译源码成jar本地安装到maven 的本地仓库,安装完以后就能正常引用 ...

  2. 利用requirejs实现vue的模块化开发

    通常vue都是搭配webpack+vue-cli使用的 如果不在nodejs环境下开发web应用呢? 这里提出一个解决方案: 1.加载requirejs,并且指定main函数 <script d ...

  3. 使用FakeAPP进行AI换脸必看!!

    C盘生于容量小于5G的千万别用啊!!笔者本人因为C盘只剩了3G,根本用不上这个,最后会把大小为4G的core文件必须移植到C盘当中,俺的CUDA也白安装了,而且还不小心安装成CUDA8了,应该用9好么 ...

  4. Entity Framework Core 2.1,添加种子数据

    EFCore 2.1出来有一段时间了,里面的新功能还没怎么用,今天研究下如何使用EF Core 2.1添加种子数据. 这部分的官方文档地址是:https://docs.microsoft.com/en ...

  5. Python爬虫入门教程 44-100 Charles的安装与使用-手机APP爬虫部分

    1. 第二款抓包工具Charles安装与使用 Charles和Fiddler一样,也是一款抓包工具,比Fiddler界面更加清晰,支持多平台 1.1 官方网址 https://www.charlesp ...

  6. 【Android Studio安装部署系列】三十六、从Android Studio3.1.4升级到Android studio3.2【以及创建android p模拟器(未成功)】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 概述 因为想要使用Android P模拟器,所以需要将Android Studio升级到3.2版本以上. 按照官网的说法:参考<ht ...

  7. BeetleX之WebSocket详解

    对于BeetleX来说编写WebSocket服务是一件非常简单的事情,当你实现一个Web Api应用的同时这些API方法也是WebSocket服务方法.接下来主要讲解如何通过JavaScript调用B ...

  8. 学习ASP.NET Core Razor 编程系列十八——并发解决方案

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

  9. python中的编码与解码

      编码与解码 首先,明确一点,计算机中存储的信息都是二进制的   编码/解码本质上是一种映射(对应关系),比如‘a’用ascii编码则是65,计算机中存储的就是00110101,但是显示的时候不能显 ...

  10. Asp.Net Core 轻松学-利用 Swagger 自动生成接口文档

    前言     目前市场上主流的开发模式,几乎清一色的前后端分离方式,作为服务端开发人员,我们有义务提供给各个客户端良好的开发文档,以方便对接,减少沟通时间,提高开发效率:对于开发人员来说,编写接口文档 ...