Spring只支持XML方式而没有实现注解的方式(也叫AspectJ方式)的AOP,所以要使用@Aspect注解,只能引入AspectJ相关的 jar 包:

aopalliance-1.0.jar 和 aspectjweaver.jar

Spring的配置文件 applicationContext.xml 中引入context、aop对应的命名空间;

配置自动扫描的包,同时使切面类中相关方法中的注解生效,需自动地为匹配到的方法所在的类生成代理对象。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"><!-- 配置自动扫描的包 --><context:component-scan base-package="com.qcc.beans.aop"></context:component-scan><!-- 自动为切面方法中匹配的方法所在的类生成代理对象。 --><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

AOP常用的实现方式有两种,

1、采用声明的方式来实现(基于XML),

2、是采用注解的方式来实现(基于AspectJ)。

AOP中一些比较重要的概念

Joinpoint(连接点):程序执行时的某个特定的点,在Spring中就是某一个方法的执行 。

Pointcut(切点):说的通俗点,spring中AOP的切点就是指一些方法的集合,而这些方法是需要被增强、被代理的。一般都是按照一定的约定规则来表示的,如正则表达式等。切点是由一类连接点组成。

Advice(通知):还是说的通俗点,就是在指定切点上要干些什么。

Advisor(通知器):其实就是切点和通知的结合 。

注意:

1、环绕方法通知,环绕方法通知要注意必须给出调用之后的返回值,否则被代理的方法会停止调用并返回null,除非你真的打算这么做。

2、只有环绕通知才可以使用JoinPoint的子类ProceedingJoinPoint,各连接点类型可以调用代理的方法,并获取、改变返回值。

1、<aop:pointcut>如果位于<aop:aspect>元素中,则命名切点只能被当前<aop:aspect>内定义的元素访问到。为了能被整个<aop:config>元素中定义的所有增强访问,则必须在<aop:config>下定义切点。

2、如果在<aop:config>元素下直接定义<aop:pointcut>,必须保证<aop:pointcut>在<aop:aspect>之前定义。<aop:config>下还可以定义<aop:advisor>,三者在<aop:config>中的配置有先后顺序的要求:首先必须是<aop:pointcut>,然后是<aop:advisor>,最后是<aop:aspect>。而在<aop:aspect>中定义的<aop:pointcut>则没有先后顺序的要求,可以在任何位置定义。<aop:pointcut>:用来定义切入点,该切入点可以重用;<aop:advisor>:用来定义只有一个通知和一个切入点的切面;<aop:aspect>:用来定义切面,该切面可以包含多个切入点和通知,而且标签内部的通知和切入点定义是无序的;和advisor的区别就在此,advisor只包含一个通知和一个切入点。

3、在使用spring框架配置AOP的时候,不管是通过XML配置文件还是注解的方式都需要定义pointcut"切入点"。

例如定义切入点表达式 execution(* com.sample.service.impl..*.*(..))

execution()是最常用的切点函数,其语法如下所示:

整个表达式可以分为五个部分:

(1)、execution(): 表达式主体。

(2)、第一个*号:表示返回类型,*号表示所有的类型。

(3)、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。

(4)、第二个*号:表示类名,*号表示所有的类。

(5)、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

一、基于XML配置的Spring AOP

采用声明的方式实现(在XML文件中配置),大致步骤为:配置文件中配置pointcut, 在java中用编写实际的aspect 类, 针对对切入点进行相关的业务处理。

业务接口:

package com.spring.service;
public interface IUserManagerService {//查找用户public String findUser();//添加用户public void addUser();
}

业务实现:

package com.spring.service.impl;
import com.spring.service.IUserManagerService;
public class UserManagerServiceImpl implements IUserManagerService{
private String name;public void setName(String name){this.name=name;}public String getName(){return this.name;}public String findUser(){System.out.println("============执行业务方法findUser,查找的用户是:"+name+"=============");return name;}public void addUser(){System.out.println("============执行业务方法addUser=============");//throw new RuntimeException();}
}

切面类:

package com.spring.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class AopAspect {/*** 前置通知:目标方法调用之前执行的代码* @param jp*/public void doBefore(JoinPoint jp){System.out.println("===========执行前置通知============");}/*** 后置返回通知:目标方法正常结束后执行的代码* 返回通知是可以访问到目标方法的返回值的* @param jp* @param result*/public void doAfterReturning(JoinPoint jp,String result){System.out.println("===========执行后置通知============");System.out.println("返回值result==================="+result);}/*** 最终通知:目标方法调用之后执行的代码(无论目标方法是否出现异常均执行)* 因为方法可能会出现异常,所以不能返回方法的返回值* @param jp*/public void doAfter(JoinPoint jp){System.out.println("===========执行最终通知============");}/*** * 异常通知:目标方法抛出异常时执行的代码* 可以访问到异常对象* @param jp* @param ex*/public void doAfterThrowing(JoinPoint jp,Exception ex){System.out.println("===========执行异常通知============");}/*** 环绕通知:目标方法调用前后执行的代码,可以在方法调用前后完成自定义的行为。* 包围一个连接点(join point)的通知。它会在切入点方法执行前执行同时方法结束也会执行对应的部分。* 主要是调用proceed()方法来执行切入点方法,来作为环绕通知前后方法的分水岭。* * 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法。* 而且环绕通知必须有返回值,返回值即为目标方法的返回值* @param pjp* @return* @throws Throwable*/public Object doAround(ProceedingJoinPoint pjp) throws Throwable{System.out.println("======执行环绕通知开始=========");// 调用方法的参数Object[] args = pjp.getArgs();// 调用的方法名String method = pjp.getSignature().getName();// 获取目标对象Object target = pjp.getTarget();// 执行完方法的返回值// 调用proceed()方法,就会触发切入点方法执行Object result=pjp.proceed();System.out.println("输出,方法名:" + method + ";目标对象:" + target + ";返回值:" + result);System.out.println("======执行环绕通知结束=========");return result;}
}

Spring配置:

<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"><!-- 声明一个业务类 --><bean id="userManager" class="com.spring.service.impl.UserManagerServiceImpl"><property name="name" value="lixiaoxi"></property></bean>  <!-- 声明通知类 --><bean id="aspectBean" class="com.spring.aop.AopAspect" /><aop:config><aop:aspect ref="aspectBean"><aop:pointcut id="pointcut" expression="execution(* com.spring.service.impl.UserManagerServiceImpl..*(..))"/><aop:before method="doBefore" pointcut-ref="pointcut"/> <aop:after-returning method="doAfterReturning" pointcut-ref="pointcut" returning="result"/><aop:after method="doAfter" pointcut-ref="pointcut" /> <aop:around method="doAround" pointcut-ref="pointcut"/> <aop:after-throwing method="doAfterThrowing" pointcut-ref="pointcut" throwing="ex"/></aop:aspect></aop:config>
</beans>

测试类:

package com.spring.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.spring.service.IUserManagerService;
public class TestAop {public static void main(String[] args) throws Exception{ApplicationContext act =  new ClassPathXmlApplicationContext("applicationContext3.xml");IUserManagerService userManager = (IUserManagerService)act.getBean("userManager");userManager.findUser();System.out.println("\n");userManager.addUser();}
}

二、使用注解配置AOP

采用注解来做aop, 主要是将写在spring 配置文件中的连接点写到注解里面。

业务接口和业务实现与上边一样,具体切面类如下:

package com.spring.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;@Aspect
public class AopAspectJ {/**  * 必须为final String类型的,注解里要使用的变量只能是静态常量类型的  */  public static final String EDP="execution(* com.spring.service.impl.UserManagerServiceImpl..*(..))";/*** 切面的前置方法 即方法执行前拦截到的方法* 在目标方法执行之前的通知* @param jp*/@Before(EDP)public void doBefore(JoinPoint jp){System.out.println("=========执行前置通知==========");}/*** 在方法正常执行通过之后执行的通知叫做返回通知* 可以返回到方法的返回值 在注解后加入returning * @param jp* @param result*/@AfterReturning(value=EDP,returning="result")public void doAfterReturning(JoinPoint jp,String result){System.out.println("===========执行后置通知============");}/*** 最终通知:目标方法调用之后执行的通知(无论目标方法是否出现异常均执行)* @param jp*/@After(value=EDP)public void doAfter(JoinPoint jp){System.out.println("===========执行最终通知============");}/*** 环绕通知:目标方法调用前后执行的通知,可以在方法调用前后完成自定义的行为。* @param pjp* @return* @throws Throwable*/@Around(EDP)public Object doAround(ProceedingJoinPoint pjp) throws Throwable{System.out.println("======执行环绕通知开始=========");// 调用方法的参数Object[] args = pjp.getArgs();// 调用的方法名String method = pjp.getSignature().getName();// 获取目标对象Object target = pjp.getTarget();// 执行完方法的返回值// 调用proceed()方法,就会触发切入点方法执行Object result=pjp.proceed();System.out.println("输出,方法名:" + method + ";目标对象:" + target + ";返回值:" + result);System.out.println("======执行环绕通知结束=========");return result;}/*** 在目标方法非正常执行完成, 抛出异常的时候会走此方法* @param jp* @param ex*/@AfterThrowing(value=EDP,throwing="ex")public void doAfterThrowing(JoinPoint jp,Exception ex) {System.out.println("===========执行异常通知============");}
}
spring的配置:
<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- 声明spring对@AspectJ的支持 --> <aop:aspectj-autoproxy/> <!-- 声明一个业务类 --> <bean id="userManager" class="com.spring.service.impl.UserManagerServiceImpl"> <property name="name" value="lixiaoxi"></property> </bean> <!-- 声明通知类 --> <bean id="aspectBean" class="com.spring.aop.AopAspectJ" /> </beans> 测试类: package com.spring.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.spring.service.IUserManagerService; public class TestAop1 { public static void main(String[] args) throws Exception{ ApplicationContext act = new ClassPathXmlApplicationContext("applicationContext4.xml"); IUserManagerService userManager = (IUserManagerService)act.getBean("userManager"); userManager.findUser(); System.out.println("\n"); userManager.addUser(); } }

转载于:https://www.cnblogs.com/xyhero/p/60829d413d54b280ff2f143be19029b4.html

Spring系列之AOP实现的两种方式相关推荐

  1. Spring加载properties文件的两种方式

    2019独角兽企业重金招聘Python工程师标准>>> 在项目中如果有些参数经常需要修改,或者后期可能需要修改,那我们最好把这些参数放到properties文件中,源代码中读取pro ...

  2. Vue系列vue-router的参数传递的两种方式(五)

    Vue系列vue-router的参数传递的两种方式 前言 一.编写Profile页面 二.编写App.vue入口 三.路由配置,index.js 四.对比route.params方式传参 五.项目中 ...

  3. Spring中进行事务管理的两种方式

    1.Spring中事务管理的API 事务是指逻辑上要么全部成功.要么全部失败的一组操作.例如用户A给用户B转账,则用户A账户余额减少.用户B账户增加这两个操作就是一组事务,必须全部成功或失败撤回操作, ...

  4. Spring Boot自定义 Servlet Filter 的两种方式

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 码农小胖哥 来源 | 公众号「码农小胖哥」 针 ...

  5. RabbitMQ(六)——Spring boot中消费消息的两种方式

    前言 上一篇博客中,我们只是简单总结了Spring boot中整合RabbitMQ的操作,针对消息消费的两种方式只是简单给了一个实例,这篇博客,我们进一步总结关于Spring boot消息消费的相关功 ...

  6. spring aop日志(两种方式)

    第一种方式(全注解): <!-- 定义事务管理器(声明式的事务) -->       <bean id="transactionManager"         ...

  7. Spring系列之依赖注入的三种方式

    目录 一.依赖注入方式 1.使用属性的setXXX方法注入 2.构造函数注入 (1)按类型匹配入参type (2)按索引匹配入参index (3)联合使用类型和索引匹配入参[type和index一起使 ...

  8. spring boot controller 初始化_使用 Spring 快速创建 web 应用的两种方式

    介绍 本篇文章主要介绍,如何使用 Spring 开发一个 Web 应用. 我们将研究用 Spring Boot 开发一个 web 应用,并研究用非 Spring Boot 的方法. 我们将主要使用 J ...

  9. 基于aspectj实现AOP操作的两种方式——xml配置

    1. 要导入的 jar 包: 常用的aspectj表达式: 权限修饰符可以省略,以下表示:返回值类型为任意,com.chy.service包以及其子包下的.任意类的.参数任意的.任意方法 execut ...

最新文章

  1. 【点云论文速读】点云分层聚类算法
  2. Pytorch使用TensorboardX进行网络可视化
  3. python自动化运维平台能用php开发吗_关于数据库自动化运维平台的设计及开发实现思路...
  4. iOS 开发中需要注意的小地方
  5. 对比SQL,学习Pandas操作:group_concat如何实现?
  6. 什么原因成就了一位优秀的程序员?
  7. 金融工程与计算机联系紧密吗,美国留学金融工程专业院校有什么推荐的呢?
  8. C#入门详解(12)
  9. Ubuntu16.04开机引导缺失Win10
  10. 变量案例弹出用户名(JS)
  11. FTPClient删除linux文件夹,使用ftp读取文件夹中的多个文件,并删除
  12. 单链表尾指针要置为空?为什么单链表建立完以后,比如p指向尾节点,要加上一句p-next=NULL;?
  13. 小程序源码:全新实用工具证件照制作-多玩法安装简单
  14. [极致用户体验] 微信设置大字号后,iOS加载网页时闪动怎么办?
  15. 公开数据集分享(一)-MMWHS
  16. 某程序员因准点下班没加班,被劝退!网友:还有没有天理?
  17. 10分钟认识低代码平台
  18. 汇编获取CPU的id
  19. Android 开发之Loading
  20. 5分钟带你了解python中超级好用的库 you-get

热门文章

  1. Python 空字符串转化问题:ValueError: invalid literal for int() with base 10: ' ',原因及解决方法。
  2. 操作系统中的零拷贝与java中的使用
  3. CTFshow 命令执行 web56
  4. cumprod--累积连乘
  5. 自动调节图像的对比度 和改变图像的大小
  6. DFS求连通块数目(深搜)
  7. matlab数组操作
  8. 1.2.2 Logistic回归和梯度下降计算的数学流程
  9. windows使用Win32DiskImager安装树莓派系统
  10. C语言学习笔记 (005) - 二维数组作为函数参数传递剖析