两种方式开发AspectJ

Posted nuist__NJUPT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了两种方式开发AspectJ相关的知识,希望对你有一定的参考价值。

两种方式开发AspectJ

AspectJ一个基于Java语言的AOP框架,建议开发者使用AspectJ实现AOP

  • 使用AspectJ实现AOP有两种方式

  • 1-基于XML配置开发AspectJ

  • 2-基于注解开发AspectJ

  • 基于XML配置开发AspectJ是指通过XML配置文件定义切面,切入点及通知

  • 所有这些定义都必须在aop:config元素内,

  • aop:config:开发AspectJ顶层配置元素,配置文件beans下可以包含多个该元素

  • <aop:aspect>:配置一个切面,属性ref指定切面的定义

  • aop:pointcut:配置切入点,属性expression指定通知增强哪些方法

  • aop:before:配置前通知,属性method指定前置通知的方法,属性pointcut-ref指定关联的切入点

  • aop:after-returning:配置后置返回通知,属性method指定后置返回通知方法,属性pointcut-ref指定关联的切入点

  • aop:around:配置环绕通知,method指定环绕通知方法,属性pointcut-ref指定关联的切入点

  • aop:after-throwing:配置异常通知,属性method指定异常通知的方法,属性pointcut-ref指定关联的切入点,没有异常发生的时候将不再执行

  • aop:after:配置后置通知,属性method指定后置通知方法,属性pointcut-ref指定关联的切入点

下面通过一个实例演示基于XML配置开发AspectJ的过程
1-创建web应用ch17,并导入相关jar包如下:
在这里插入图片描述
2-在ch17中的src目录下创建aspectj.xml包,并在该包中创建切面类MyAspect,在该类中编写各种类型的通知。

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

//通过一个实例演示基于XML配置开发AspectJ的过程
public class MyAspect { //切面类,在此类中编写各种类型的通知
    //前置通知,使用JoinPoint接口作为参数信息获取目标对象信息
    public void before(JoinPoint joinPoint){
        System.out.print("前置通知,模拟权限管理")  ;
        System.out.println(",目标对象:" + joinPoint.getTarget() + ",被增强处理的方法:" + joinPoint.getSignature().getName()) ;
    }

    //后置返回通知
    public void afterReturning(JoinPoint joinPoint){
        System.out.print("后置返回通知:" + "模拟删除临时文件") ;
        System.out.println(",被增强处理的方法:" + joinPoint.getSignature().getName()) ;
    }

    //环绕通知
    public Object around (ProceedingJoinPoint pjp) throws Throwable{
        //开始
        System.out.println("环绕开始:执行目标方法,模拟开启事务") ;
        //执行当前目标方法
        Object object = pjp.proceed() ;
        //结束
        System.out.println("环绕结束:执行目标方法后,模拟关闭事务");
        return object ;
    }

    //异常通知
    public void except(Throwable e){
        System.out.println("异常通知:" + "程序执行异常" + e.getMessage()) ;
    }

    //后置通知,即最终通知
    public void after(){
        System.out.println("最终通知:模拟释放资源") ;
    }
}

3-创建配置文件,并编写相关配置,在配置文件spring-config.xml中配置,为aop:config元素及其子元素编写相关配置,在配置文件中expression="execution(* dynamic.jdk..(…))"是定义切入点表达式,该切入点表达式的意思是匹配dynamic.jdk包中任意类任意方法的执行,其中第一个号代表所有类型,dynamic.jdk表示需要匹配的包名,第二个号表示类名,代表匹配包中所有的类,第三个代表方法名,使用表示的所有方法,后面的(…)表示方法的参数是任意参数
注意:第一个*号与包名dynamic.jdk之间有一个空格

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--定义目标对象-->
    <bean id = "testDao" class = "dynamic.jdk.TestDaoImpl"/>
    <!--定义切面-->
    <bean id = "MyAspect" class = "aspectj.xml.MyAspect"/>
    <!--AOP配置-->
    <aop:config>
        <!--配置切面-->
        <aop:aspect ref = "MyAspect">
            <!--配置切入点,通知增强哪些方法-->
            <aop:pointcut expression="execution(* dynamic.jdk.*.*(..))" id = "myPointCut"/>
            <!--将通知与切入点关联-->
            <!--关联前置通知-->
            <aop:before method = "before" pointcut-ref = "myPointCut"/>
            <!--关联后置返回通知-->
            <aop:after-returning method="afterReturning" pointcut-ref="myPointCut"/>
            <!--关联环绕通知-->
            <aop:around method = "around" pointcut-ref = "myPointCut"/>
            <!--关联异常通知,没有发生异常不会执行增强,throwing属性设置通知的第一个参数名称-->
            <aop:after-throwing method="except" pointcut-ref="myPointCut" throwing  = "e" />
            <!--管理后置通知,不管目标方法师傅成功都要执行-->
            <aop:after method = "after" pointcut-ref = "myPointCut"/>
        </aop:aspect>
    </aop:config>
</beans>

4-创建测试类,在aspectj.xml包中创建测试类XMLAspectJTest,在主方法中,使用Spring容器获取增强后的目标对象,并执行目标方法。

import dynamic.jdk.TestDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class XMLAspectJTest {
    public static void main(String[] args){
        //初始化Spring容器,加载配置文件
        ApplicationContext appCon = new ClassPathXmlApplicationContext("spring-config.xml") ;
        //从容器中获取增强后的目标对象
        TestDao testDaoAdvice = (TestDao) appCon.getBean("testDao");
        //执行方法
        testDaoAdvice.save() ;
        System.out.println("===============") ;
        testDaoAdvice.modify() ;
        System.out.println("===============") ;
        testDaoAdvice.delete() ;

    }
}

5-测试结果如下:
在这里插入图片描述
6-若在dynamic.jdk包的TestImpl类的save方法总添加异常代码,例如:int n = 100 / 0 ;
然后重新运行测试类,测试结果如下:
在这里插入图片描述

基于注解开发AspectJ要比基于XML配置开发AspectJ要便捷许多,

  • 所以实际开发中往往使用注解开发AspectJ
  • AspectJ注解如下:
  • 1-@Aspect:用于定义一个切面,注解在切面类上
  • 2-@Pointcut:用于定义切入点表达式,在使用时候需要定义一个切入点方法,该方法是一个返回值void且方法体为空的普通方法
  • 3-@Before:用于定义前置通知,在使用时候通常需要指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式
  • 4-@AfterReturning:用于定义后置返回通知,在使用时通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义的切入点表达式
  • 5-@Around:用于定义环绕通知,在使用时通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式
  • 6-@AfterThrowing:用于定义异常通知,在使用时通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式
  • 另外,还有一个throwing属性 用于访问目标方法抛出的异常,该属性值与异常通知方法中同名的形参一致。
  • 7-@After:用于定义后置通知,在使用时通常为其指定value属性值,该值可以是已有的切入点,也可以直接定义切入点表达式

1-在src目录下创建aspectj.annotation包,并在该包中创建切面类MyAspect, 在该类中首先使用@Aspect注解定义一个切面类,由于该类在Spring中作为组件使用的,所以还需要使用@Component注解,然后使用@Pointcut注解切入点表达式,并通过定义该方法来表示切入点名称,最后在每个通知方法上添加相应的注解,并将切入点名称作为参数传递给需要执行增强通知的方法。


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

import org.springframework.stereotype.Component;

@Aspect //对应<aop:aspect ref = "myAspect>
@Component  //对应<bean id = "myAspect" class = "aspectj.xml.MyAspect"/>
public class MyAspect { //切面类,在该类中编写各种类型的通知
    //定义切入点
    @Pointcut("execution(* dynamic.jdk.*.*(..))")
    private void myPointCut(){

    }

    //前置通知,使用JoinPoint接口作为参数获取目标对象信息
    @Before("myPointCut()")
    public void before(JoinPoint joinPoint){
        System.out.print("前置通知:模拟权限控制") ;
        System.out.println(",目标类对象:" + joinPoint.getTarget() + ",被增强处理的方法:" + joinPoint.getSignature().getName()) ;
    }

    //后置返回通知
    @AfterReturning("myPointCut()")
    public void afterReturning(JoinPoint joinPoint){
        System.out.print("后置返回通知:模拟删除临时文件") ;
        System.out.println(",被增强处理的方法:" + joinPoint.getSignature().getName()) ;
    }

    //环绕通知
    @Around("myPointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        //开始
        System.out.println("环绕开始:执行目标方法前,模拟开启事务") ;
        //执行当前目标方法
        Object object = pjp.proceed() ;
        System.out.println("环绕结束:执行目标方法后,模拟关闭事务") ;
        return object ;
    }

    //异常通知
    @AfterThrowing(value = "myPointCut()" , throwing = "e")
    public void except(Throwable e){
        System.out.println("异常通知:" + "程序执行异常" + e.getMessage()) ;
    }

    //后置(最终)通知
    @After("myPointCut()")
    public void After(){
        System.out.println("最终通知:模拟释放资源") ;
    }
}

2-注解目标类,使用 注解@Repository将目标类dynamic.jdk.TestDaoImpl注解为目标对象,注解代码如下

@Repository("testDao")

3-创建配置文件,在src目录下创建配置文件applicationContext.xml,并在配置文件中指定需要扫描的包,使注解生效,同时需要启动注解的AspectJ支持

<?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
      https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--指定需要扫描的包-->
    <context:component-scan base-package = "aspectj.annotation"/>
    <context:component-scan base-package = "dynamic.jdk"/>
    <!--启动基于注解的AspectJ支持-->
    <aop:aspectj-autoproxy/>
</beans>

4-创建测试类,在aspectj.annotaion包中创建测试类AnnotationAspectJTest,并在该类中加载增强后的目标对象,执行 相应方法。

import dynamic.jdk.TestDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AnnotationAspectJTest {
    public static void main(String[] args){
        //初始化Spring容器,加载配置文件
        ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml") ;
        //获取增强后的目标对象
        TestDao testDaoAdvice = (TestDao) appCon.getBean("testDao");
        testDaoAdvice.save() ;
        System.out.println("=================") ;
        testDaoAdvice.modify() ;
        System.out.println("=================") ;
        testDaoAdvice.delete() ;
    }
}

5-测试结果如下:
在这里插入图片描述
6-若在dynamic.jdk包的TestImpl类的save方法总添加异常代码,例如:int n = 100 / 0 ;
然后重新运行测试类,测试结果如下:
在这里插入图片描述

以上是关于两种方式开发AspectJ的主要内容,如果未能解决你的问题,请参考以下文章

AspectJ开发

AspectJ——基于注解的开发方式

AspectJ——基于注解的开发方式

[AOP] 2. AOP的两种实现-Spring AOP以及AspectJ

Spring的aop操作

Spring系列之AOP实现的两种方式