AspectJ开发

AspectJ是一个基于Java语言的AOP框架,它提供了强大的AOP功能。

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

  • 基于XML的声明式AspectJ
  • 基于注解的声明式AspectJ

基于XML的声明式AspectJ

基于XML的声明式AspectJ是指通过XML文件来定义切面切入点通知,所有的切面、切入点和通知都必须定义在<aop:config>元素内。

Spring配置文件中的<beans>元素中可以包含多个<aop:config>元素,一个<aop:config> 元素中又可以包含属性和子元素,其子元素包括<aop:aspect>元素下,同样包含了属性和多个子元素,通过使用<aop:aspect>元素及其子元素就可以在XML文件中配置切面、切入点和通知。

配置切面

在Spring的配置文件中,配置切面使用的是 <aop:aspect> 元素,该元素会将一个已定义好的Spring Bean转换成切面Bean,所以要在配置文件中先定义一个普通的Spring Bean,最后通过元素的ref属性即可引用该Bean。

属性名称描述
id用于定义该切面的唯一标识
ref用于引用普通的Spring Bean

配置切入点

在Spring的配置文件中,切入点是通过<aop:pointcut>元素来定义的。

<aop:pointcut>元素作为<aop:config>元素的子元素时,表示该切入点是全局切入点,它可被多个切面所共享;

<aop:pointcut>元素作为<aop:aspect>元素的子元素时, 表示该切入点只对当前切面有效。

<aop:pointcut>元素写到<aop:aspect>的外部时,就变成了所有的切面都可使用,但是要想写在外面必须写在<aop:aspect>元素的上方

属性名称描述
id用于指定切入点的唯一标识名称
expression用于指定切入点关联的切入点表达式
        <!--全局切入点-->
       <aop:pointcut id="pt1" expression="execution(* cn.itcast.service.Impl.*.*(..))"/>
            
     </aop:aspect>
</aop:config>

该切入点表达式的含义是:匹配 cn.itcast.service.Impl 包中任意类的任意方法的执行

execution():是方法的主体

第一个 * :返回的类型,使用 * 表示代表所有类型

后面的包名表示需要拦截的包名

第二个 * :表示类名,使用 * 表示所有的类

第三个 * :表示方法,使用 * 表示所有的方法

. . :表示方法的参数,这里表示任意参数

需要注意的是:第一个 * 与 包名之间有一个空格

配置通知

在配置代码中,分别使用 <aop:aspect>的子元素配置了5种常用通知

通知的常用属性及其描述
属性名称描述
pointcut用于指定一个切入点表达式
pointcut-ref指定一个已经存在的切入点名称,即pointcut中的id的值
method最终通知,无论切入点方法是否正常执行它都会在其后面执行
throwing异常通知,在切入点方法执行产生异常之后执行。注:它和后置通知永远只能执行一个
returning后置通知,在切入点方法执行之后执行。注:它和异常通知永远只能执行一个

部分代码如下:

//切面  在此类中编写通知

/**
 * 通知中使用了 JoinPoint接口 及其子接口   ProceedingJoinPoint
 * 作为参数来获得目标对象的类名、目标方法名和目标参数等
 *
 * 需要注意的是:
 * 环绕通知必须接受 ProceedingJoinPoint类型参数
 * 返回值必须也是Object类型
 * 且必须抛出异常
 *
 */
public class MyAspect {

    //前置通知
    public void myBefore(JoinPoint joinPoint) {
        System.out.print(" 前置通知:模拟执行权限检查. . . , ");
        System.out.print(" 目标类是: " + joinPoint.getTarget());
        System.out.println("被植入增强处理的目标方法为: " + joinPoint.getSignature().getName());
    }

    //后置通知
    public void myAfterReturning(JoinPoint joinPoint) {
        System.out.print(" 后置通知:模拟记录日志. . . ,");
        System.out.println(" 被植入增强处理的目标方法为: " + joinPoint.getSignature().getName());
    }

    /**
     * 环绕通知
     * ProceedingJoinPoint JoinPoint 子接口,表示可以执行目标方法
     * 1.必须是 Object 类型的返回值
     * 2.必须接收一个参数,类型为 ProceedingJoinPoint
     * 3.必须 throws Throwable
     **/
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
            throws Throwable {
        //开始
        System.out.println("环绕开始:执行目标方法之前,模拟开启事务. . .");
        //执行当前门标
        Object obj = proceedingJoinPoint.proceed();
        //结束
        System.out.println("环绕结束 执行门标方法之后 模拟关闭事务.. ");
        return obj;
    }

    //异常通知
    public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
        System.out.println("异常通知:" + "出错了" + e.getMessage());
    }

    //最终通知
    public void myAfter() {
        System.out.println("最终通知 模拟方法结束后的释放资源 . . ");
    }

}

    <!--1.目标对象-->
    <bean id="userDao" class="com.dfbz.aspectj.dao.Impl.UserDaoImpl"></bean>

    <!--2.切面-->
    <bean id="myAspect" class="com.dfbz.aspectj.xml.MyAspect"></bean>

    <!--3.AOP-->
    <!--
          注意:
               后置通知只有在目标方法成功执行后才会执行后置通知
               最终通知不管目标方法是否有异常都会执行最终通知
     -->
    <aop:config>
        <!--配置切面-->
        <aop:aspect id="" ref="myAspect">
            <!--配置切入点-->
            <aop:pointcut id="pt" expression="execution(* com.dfbz.aspectj.dao.Impl.*.*(..))"></aop:pointcut>
            <!--前置通知-->
            <aop:before method="myBefore" pointcut-ref="pt"></aop:before>

            <!--后置通知-->
            <aop:after-returning method="myAfterReturning" pointcut-ref="pt"
                                 returning="joinPoint"></aop:after-returning>

            <!--环绕通知-->
            <aop:around method="myAround" pointcut-ref="pt"></aop:around>

            <!--异常通知 如果没有异常,将不会执行增强-->
            <aop:after-throwing method="myAfterThrowing" pointcut-ref="pt" throwing="e"></aop:after-throwing>

            <!--最终通知-->
            <aop:after method="myAfter" pointcut-ref="pt"></aop:after>
            
        </aop:aspect>
    </aop:config>
 前置通知:模拟执行权限检查. . . ,  目标类是: com.dfbz.aspectj.dao.Impl.UserDaoImpl@d706f19被植入增强处理的目标方法为: addUser
环绕开始:执行目标方法之前,模拟开启事务. . .
添加用户
最终通知 模拟方法结束后的释放资源 . . 
环绕结束 执行门标方法之后 模拟关闭事务.. 
 后置通知:模拟记录日志. . . , 被植入增强处理的目标方法为: addUser
 前置通知:模拟执行权限检查. . . ,  目标类是: com.dfbz.aspectj.dao.Impl.UserDaoImpl@4b7dc788被植入增强处理的目标方法为: addUser
环绕开始:执行目标方法之前,模拟开启事务. . .
最终通知 模拟方法结束后的释放资源 . . 
异常通知:出错了/ by zero

基于注解的声明式AspectJ

基于XML的声明式AspectJ要便捷,但是也有一些缺点,就是在Spring配置文件中需要配置大量的代码信息。

AspectJ框架为AOP的实现提供了一套注解。

AspectJ的注解及其描述
注解名称描述
@Aspect用来定义一个切面
@Pointcut用来定义切入点表达式
@Before用来定义前置通知
@AfterReturning用来定义后置通知
@Around用来定义环绕通知
@AfterThrowing用来定义异常通知
@After用来定义最终通知

部分代码如下:

//切面  在此类中编写通知
@Aspect
@Component
public class MyAspect {

    //切入点表达式
    @Pointcut("execution(* com.dfbz.dao.Impl.*.*(..))")
    //切入点
    public void myPointCut(){}




    //前置通知
    @Before("myPointCut()")
    public void myBefore(JoinPoint joinPoint) {
        System.out.print(" 前置通知:模拟执行权限检查. . . , ");
        System.out.print(" 目标类是: " + joinPoint.getTarget());
        System.out.println("被植入增强处理的目标方法为: " + joinPoint.getSignature().getName());
    }

    //后置通知
    @AfterReturning(value = "myPointCut()")
    public void myAfterReturning(JoinPoint joinPoint) {
        System.out.print(" 后置通知:模拟记录日志. . . ,");
        System.out.println(" 被植入增强处理的目标方法为: " + joinPoint.getSignature().getName());
    }

    /**
     * 环绕通知
     * ProceedingJoinPoint JoinPoint 子接口,表示可以执行目标方法
     * 1.必须是 Object 类型的返回值
     * 2.必须接收一个参数,类型为 ProceedingJoinPoint
     * 3.必须 throws Throwable
     **/
    @Around("myPointCut()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
            throws Throwable {
        //开始
        System.out.println("环绕开始:执行目标方法之前,模拟开启事务. . .");
        //执行当前门标
        Object obj = proceedingJoinPoint.proceed();
        //结束
        System.out.println("环绕结束 执行门标方法之后 模拟关闭事务.. ");
        return obj;
    }

    //异常通知
    @AfterThrowing(value = "myPointCut()",throwing = "e")
    public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
        System.out.println("异常通知:" + "出错了" + e.getMessage());
    }

    //最终通知
    @After("myPointCut()")
    public void myAfter() {
        System.out.println("最终通知 模拟方法结束后的释放资源 . . ");
    }
}

目标对象类需要添加注解 :

@Repository("userDao")

配置文件如下:

    <!--指定需要扫描的包,使注解生效-->
    <context:component-scan base-package="com.dfbz"/>

    <!--启动基于注解的声明式AspectJ-->
    <aop:aspectj-autoproxy/>

运行结果如下:

环绕开始:执行目标方法之前,模拟开启事务. . .
 前置通知:模拟执行权限检查. . . ,  目标类是: com.dfbz.dao.Impl.UserDaoImpl@765d7657被植入增强处理的目标方法为: addUser
添加用户
环绕结束 执行门标方法之后 模拟关闭事务.. 
最终通知 模拟方法结束后的释放资源 . . 
 后置通知:模拟记录日志. . . , 被植入增强处理的目标方法为: addUser

注意:

如果在同一个连接点有多个通知需要执行,那么在同一切面中,目标方法之前的前置通知和环绕通知的执行顺序是未知的;目标方法之后的后置通知和环绕通知的执行顺序也是未知的。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值