• Spring使用AspectJ开发AOP:基于Annotation


    基于 Annotation 的声明式

    在 Spring 中,尽管使用 XML 配置文件可以实现 AOP 开发,但是如果所有的相关的配置都集中在配置文件中,势必会导致 XML 配置文件过于臃肿,从而给维护和升级带来一定的困难。

    为此,AspectJ 框架为 AOP 开发提供了另一种开发方式——基于 Annotation 的声明式。AspectJ 允许使用注解定义切面、切入点和增强处理,而 Spring 框架则可以识别并根据这些注解生成 AOP 代理。

    关于 Annotation 注解的介绍如表 1 所示。

    表 1 Annotation 注解介绍
    名称说明
    @Aspect 用于定义一个切面。
    @Before 用于定义前置通知,相当于 BeforeAdvice。
    @AfterReturning 用于定义后置通知,相当于 AfterReturningAdvice。
    @Around 用于定义环绕通知,相当于MethodInterceptor。
    @AfterThrowing 用于定义抛出通知,相当于ThrowAdvice。
    @After 用于定义最终final通知,不管是否异常,该通知都会执行。
    @DeclareParents 用于定义引介通知,相当于IntroductionInterceptor(不要求掌握)。


    下面使用注解的方式重新实现《基于XML的声明式》部分的功能。

    1. 创建切面类 MyAspect

    在 src 目录下创建一个名为 com.mengma.aspectj.annotation 的包,在该包下创建一个切面类 MyAspect,如下所示。

    package com.mengma.aspectj.annotation;
    
    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;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    //切面类
    @Aspect
    @Component
    public class MyAspect {
    // 用于取代:<aop:pointcut
    // expression="execution(*com.mengma.dao..*.*(..))" id="myPointCut"/>
    // 要求:方法必须是private,没有值,名称自定义,没有参数
    @Pointcut("execution(*com.mengma.dao..*.*(..))")
    private 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("后置通知,方法名称:" + joinPoint.getSignature().getName());
    }
    
    // 环绕通知
    @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("最终通知");
    }
    }

    上述代码中,第 13 行 @Aspect 注解用于声明这是一个切面类,该类作为组件使用,所以要添加 @Component 注解才能生效。第 19 行中 @Poincut 注解用于配置切入点,取代 XML 文件中配置切入点的代码。

    在每个通知相应的方法上都添加了注解声明,并且将切入点方法名“myPointCut”作为参数传递给要执行的方法,如需其他参数(如异常通知的异常参数),可以根据代码提示传递相应的属性值。

    2. 为目标类添加注解

    在 com.mengma.dao.CustomerDaoImpl 目标类中添加注解 @Repository("customerDao")。

    import org.springframework.stereotype.Repository;
    @Repository("customerDao")
    public class CustomerDao {
        public void doSome(){
           // int i=1/0;
            System.out.println("正式业务");
        }
    }

    3. 创建Spring配置文件

    在 com.mengma.aspectj.annotation 包下创建 applicationContext.xml 配置文件,如下所示。

    <?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-2.5.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">
    <!--扫描含com.mengma包下的所有注解-->
    <context:component-scan base-package="com.mengma"/>
    <!-- 使切面开启自动代理 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>

    上述代码中,首先导入了 AOP 命名空间及其配套的约束,使切面类中的 @AspectJ 注解能够正常工作;第 13 行代码添加了扫描包,使注解生效。需要注意的是,这里还包括目标类 com.mengma.dao.CustomerDaoImpl 的注解,所以 base-package 的值为 com.mengma;第 15 行代码的作用是切面开启自动代理。

    4. 创建测试类

    在 com.mengma.aspectj.annotation 包下创建一个名为 AnnotationTest 的测试类,如下所示。

    package com.mengma.aspectj.annotation;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.mengma.dao.CustomerDao;
    
    public class AnnotationTest {
    @Test
    public void test() {
    String xmlPath = "com/mengma/aspectj/xml/applicationContext.xml";
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
    xmlPath);
    // 从spring容器获取实例
    CustomerDao customerDao = (CustomerDao) applicationContext
    .getBean("customerDao");
    // 执行方法
    customerDao.add();
    }
    }

    5. 运行项目并查看结果

    使用 JUnit 测试运行 test() 方法,运行成功后,控制台的输出结果如图 3 所示。

    运行结果
    图 3  运行结果


    删除 add() 方法中的“int i=1/0;”,重新运行 test() 方法,此时控制台的输出结果如图 4 所示。

    运行结果
    图 4  运行结果


    从图 3 和图 4 的输出结果中可以看出,已成功使用 Annotation 的方式实现了 AOP 开发。与其他方式相比,基于 Annotation 方式实现 AOP 的效果是最方便的方式,所以实际开发中推荐使用注解的方式。

  • 相关阅读:
    关于jQuery动态多次绑定的问题及解决
    零基础英文盲打的建议
    29道Java简答题
    Redis分布式锁的实现(纯文字)
    小程序三级联动(动态获取数据)
    sql中按in中的ID进行排序输出
    layui中对表格操作按钮集的判断
    Linq改进
    C#前台直接调用后台数据时,<p>或双引号出现转义的问题
    sql 中sum函数返回null的解决方案
  • 原文地址:https://www.cnblogs.com/lowerma/p/11762100.html
Copyright © 2020-2023  润新知