当前位置: 代码迷 >> 综合 >> 【SSM -Spring篇05】使用AspectJ实现Spring AOP - (连接点(JoinPoint) - 通知(Advice))
  详细解决方案

【SSM -Spring篇05】使用AspectJ实现Spring AOP - (连接点(JoinPoint) - 通知(Advice))

热度:35   发布时间:2024-02-20 15:47:06.0

文章目录

    • AspectJ
    • 通知类型(通知(Advice):在方法执行前或执行后要做的动作)
    • 使用AspectJ实现Spring AOP的方式有两种:
      • 1. 基于XML配置开发AspectJ
        • 案例实现
      • 2. 基于注解开发AspectJ【***】
        • AspectJ通知注解【***】
        • 连接点(JoinPoint)【***】
        • 案例

AspectJ

??AspectJ是一个基于Java语言的AOP框架。从Spring 2.0以后引入了AspectJ的支持。目前的Spring框架,建议使用AspectJ实现Spring AOP。
??AOP面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低提高程序的可重用性,同时提高了开发的效率

通知类型(通知(Advice):在方法执行前或执行后要做的动作)

  1. 环绕通知
    环绕通知(org.aopalliance.intercept.MethodInterceptor)是在目标方法执行前和执行后实施增强,可以应用于日志记录、事务处理等功能。
  2. 前置通知
    前置通知(org.springframework.aop.MethodBeforeAdvice)是在目标方法执行前实施增强,可应用于权限管理等功能。
  3. 后置返回通知
    后置返回通知(org.springframework.aop.AfterReturningAdvice)是在目标方法成功执行后实施增强,可应用于关闭流、删除临时文件等功能。
  4. 后置(最终)通知
    后置通知(org.springframework.aop.AfterAdvice)是在目标方法执行后实施增强与后置返回通知不同的是,不管是否发生异常都要执行该通知,可应用于释放资源
  5. 异常通知
    异常通知(org.springframework.aop.ThrowsAdvice)是在方法抛出异常后实施增强,可以应用于处理异常、记录日志等功能。
  6. 引入通知
    引入通知(org.springframework.aop.IntroductionInterceptor)是在目标类中添加一些新的方法和属性,可以应用于修改目标类(增强类)。

使用AspectJ实现Spring AOP的方式有两种:

  1. 基于XML配置开发AspectJ
  2. 基于注解开发AspectJ。

1. 基于XML配置开发AspectJ

??注意:基于XML配置开发AspectJ是指通过XML配置文件定义切面、切入点及通知,所有这些定义都必须在applicationContext.xml的<aop:config>元素内。

案例实现

  1. pom.xml加入AspectJ的依赖
<!--AspectJ 实现Spring AOP 方式: 1、aspectJ基于XML开发 切面编程AOP 事物回滚使用--><!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.5</version></dependency>
  1. 创建被代理类(目标类)
public interface UserDao {
    public void save();public void delete();
}
public class UserDaoImpl implements UserDao{
    @Overridepublic void save() {
    
// int i = 100/0;//测试异常抛出 虚拟机终止程序运行System.out.println("save user .....");}@Overridepublic void delete() {
    System.out.println("delete user .....");}
}
  1. 创建切面类
/*切面类中定义的一个个方法叫通知advice 通知 --- 是增强的方法,应用于被代理类方法的额外功能*//*AspectJ中的切入点匹配的执行点称作连接的(JoinPoint), 在通知方法中可以声明一个JoinPoint类型的参数。通过JoinPoint可以访问连接点的细节1.java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;2.Signature getSignature() :获取连接点的方法签名对象;3.java.lang.Object getTarget() :获取连接点所在的目标对象;4.java.lang.Object getThis() :获取代理对象本身;ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法:5.java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法;6.java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的参数替换原来的参数。 * */public class MyAspect {
    
// 前置通知 advicepublic void before(JoinPoint joinPoint){
    
// 被代理对象joinPoint.getTarget();
// 目标对象被增强的方法//获取方法签名对象 public void save() 【除了{}方法体以外都是方法签名】joinPoint.getSignature().getName();System.out.println("前置通知,用于权限控制。。"+ joinPoint.getTarget()+" ----methodName---"+joinPoint.getSignature().getName());}//后置通知public void afterReturning(JoinPoint joinPoint){
    System.out.println("后置通知,清除操作过的文件"+ joinPoint.getTarget()+" ----methodName---"+joinPoint.getSignature().getName());}
// 环绕通知 ProceedingJoinPoint是joinPoint的子类public void around(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("环绕通知,方法执行之前,开启事务");joinPoint.proceed();  //让方法继续执行System.out.println("环绕通知,方法执行之后,关闭事务");}public void afterThrowing(Throwable e) throws Throwable {
    System.out.println("异常通知:方法出现异常调用,处理异常"+e.getMessage());}public void after(JoinPoint joinPoint) {
    System.out.println("最终通知 管理数据库连接等清理工作");}
}
  1. 创建配置文件applicationContext.xml,编写相关配置
<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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 被代理类 目标类 --><bean id="userDao" class="com.xgf.aop.aspectj.xml.UserDaoImpl"/><!--切面类 干什么事情--><bean id="myAspect" class="com.xgf.aop.aspectj.xml.MyAspect"/><!-- 基于XML配置开发AspectJ是指通过XML配置文件定义切面、切入点及通知,所有这些定义都必须在<aop:config>元素内。【***】 --><aop:config><!--引入切面类 --><aop:aspect ref="myAspect"><!-- 执行的方法 在什么地方 pointcut切点execution(返回值类型 包名.类名.方法名(参数))(..)代表任意参数--><aop:pointcut id="mycut" expression="execution(* com.xgf.aop.aspectj.xml.*.*(..))"/><!-- 切入时机 --><!--前置通知 方法执行之前--><aop:before method="before" pointcut-ref="mycut"/><!--后置返回通知 方法执行之后执行,方法正常结束才调用,出现异常不会调用--><aop:after-returning method="afterReturning" pointcut-ref="mycut"/><!--环绕通知 --><aop:around method="around" pointcut-ref="mycut"/><!--异常通知--><aop:after-throwing method="afterThrowing" pointcut-ref="mycut" throwing="e"/><!-- 最终(后置)通知 不管方法是否出现异常都会执行,进行资源释放等清理工作 --><aop:after method="after" pointcut-ref="mycut"/></aop:aspect></aop:config>
</beans>
  1. 编写测试类
public class AspectJXMLTest {public static void main(String[] args) {ApplicationContext context =new ClassPathXmlApplicationContext("com/xgf/aop/aspectj/xml/applicationContext.xml");/* 代理类 */UserDao userDao = (UserDao) context.getBean("userDao");userDao.save();userDao.delete();}
}
  1. 运行结果
    在这里插入图片描述



2. 基于注解开发AspectJ【***】

??注意:一定要在applicationContext.xml中配置<aop:aspectj-autoproxy></aop:aspectj-autoproxy>才能让aop起作用。

AspectJ通知注解【***】

注解名称
@Aspect 用于定义一个切面,注解在切面类上
@Pointcut 用于定义切入点表达式。在使用时,需要定义一个切入点方法。该方法是一个返回值void,且方法体为空的普通方法
@Before 用于定义前置通知。在使用时,通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式
@AfterReturning 用于定义 后置返回通知。在使用时,通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式
@Around 用于定义 环绕通知。在使用时,通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式
@AfterThrowing 用于定义异常通知。在使用时,通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式。另外,还有一个throwing属性用于访问目标方法抛出的异常,该属性值与异常通知方法中同名的形参一致
@After 用于定义后置(最终)通知。在使用时,通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式

连接点(JoinPoint)【***】

程序运行中的一些时间点, 例如一个方法的执行, 或者是一个异常的处理(和方法有关的前前后后都是连接点)

JoinPoint对象方法 描述
Signature getSignature() 获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
Object getTarget() 获取被代理的对象
Object getThis() 获取代理对象
Object[] getArgs() 获取传入目标方法的参数对象(输入的参数列表)
joinpoint.proceed() 在环绕通知around中使用,用于启动目标方法执行的(环绕通知=前置+目标方法执行+后置通知)
joinPoint.getSignature().getName() 获取目标对象被增强的方法

案例

  1. pom.xml导入依赖
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.5</version></dependency>
  1. 创建被代理类
public interface UserDao {
    public void save();public void delete();
}
@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Overridepublic void save() {
    //int i = 100/0;//异常抛出 虚拟机终止程序运行System.out.println("save 保存 ......");}@Overridepublic void delete() {
    System.out.println("delete 删除 ......");}
}
  1. 创建切面类
// @Component配置 启动容器会实例化类 名字首字母小写myAspect
// @Aspect配置切面
@Aspect
@Component
public class MyAspect {
    /* 配置切点 - pointcut切点execution(返回值类型 包名.类名.方法名(参数))(..)代表任意参数*/@Pointcut("execution(* com.xgf.aop.aspectj.annotation.*.*(..))")public void mycut(){
    // 切点的id}// 通知 advice//@Before("mycut()") //当有多个值时用value 只有一个属性的时候直接写@Before(value="mycut()")public void before(JoinPoint joinPoint){
    //JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.// 获取被代理对象joinPoint.getTarget();
// 目标对象被增强的方法joinPoint.getSignature().getName();System.out.println("前置通知,用于权限控制 被代理对象: "+ joinPoint.getTarget()+" ----方法名methodName---"+joinPoint.getSignature().getName());}@AfterReturning("mycut()")public void afterReturning(JoinPoint joinPoint){
    System.out.println("后置返回通知,清除操作过的文件 被代理对象:"+ joinPoint.getTarget()+" ----方法名methodName---"+joinPoint.getSignature().getName());}@Around("mycut()")public void around(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("环绕通知,方法执行之前,开启事务");joinPoint.proceed();//启动目标方法执行System.out.println("环绕通知,方法执行之后,关闭事务");}@AfterThrowing(value = "mycut()",throwing = "e")public void afterThrowing(Throwable e) throws Throwable {
    System.out.println("异常通知:方法出现异常调用,处理异常"+e.getMessage());}@After("mycut()")public void after(JoinPoint joinPoint) {
    System.out.println("最终通知 管理数据库连接等清理工作");}
}
  1. 创建applicationContext.xml配置文件
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>一要有定,不然不起作用!
<?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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!--基于注解开发 扫描包--><!-- 容器启动的时候 加载配置文件xml 读取com.xgf.aop.aspectj.annotation包识别注解 --><context:component-scan base-package="com.xgf.aop.aspectj.annotation"/><!--使aop起作用--><aop:aspectj-autoproxy></aop:aspectj-autoproxy></beans>
  1. 编写测试类
public class AspectJAnnotationTest {
    public static void main(String[] args) {
    ApplicationContext context =new ClassPathXmlApplicationContext("com/xgf/aop/aspectj/annotation/applicationContext.xml");UserDao userDao = (UserDao) context.getBean("userDao");//调用方法userDao.save();userDao.delete();}
}
  1. 执行结果
    在这里插入图片描述
  相关解决方案