• 使用 AspectJ 框架实现 Spring AOP


    AspectJ 是基于注释(Annotation)的,所以需要 JDK5.0 以上的支持。

    AspectJ 支持的注解类型如下:

    • @Before
    • @After
    • @AfterReturning
    • @AfterThrowing
    • @Around

    准备工作

    首先定义一个简单的 bean,CustomerBo 实现了接口 ICustomerBo。ICustomerBo.java 如下:

    package com.shiyanlou.spring.aop.aspectj;
    
    public interface ICustomerBo {
        void addCustomer();
        void deleteCustomer();
        String AddCustomerReturnValue();
        void addCustomerThrowException() throws Exception;
        void addCustomerAround(String name);
    }
    

    CustomerBo.java 如下:

    package com.shiyanlou.spring.aop.aspectj;
    
    public class CustomerBo implements ICustomerBo {
    
        public void addCustomer() {
            System.out.println("addCustomer() is running ...");
        }
    
        public void deleteCustomer() {
            System.out.println("deleteCustomer() is running ...");
        }
    
        public String AddCustomerReturnValue() {
            System.out.println("AddCustomerReturnValue() is running ...");
            return "abc";
        }
    
        public void addCustomerThrowException() throws Exception {
            System.out.println("addCustomerThrowException() is running ...");
            throw new Exception("Generic Error");
        }
    
        public void addCustomerAround(String name) {
            System.out.println("addCustomerAround() is running ,args:"+name);
    
        }
    
    }
    

    简单的 AspectJ,Advice 和 Pointcut 结合在一起

    首先没有引入 Aspect 之前,Advice 和 Pointcut 是混在一起的,步骤如下:

    • 创建一个 Aspect 类
    • 配置 Spring 配置文件

    由于接下来要使用 aspectj 的 jar 包,首先添加 maven 依赖。需要在 pom.xml 添加:

            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.2</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjtools</artifactId>
                <version>1.9.2</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjrt</artifactId>
                <version>1.9.2</version>
            </dependency>
    

    注:我们在新建项目时就已经添加了这些依赖,这里写出来只是让同学们知道这些包的作用

    创建 AspectJ 类,LoggingAspect.java 如下:

    package com.shiyanlou.spring.aop.aspectj;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    @Aspect
    public class LoggingAspect {
    
        @Before("execution(public * com.shiyanlou.spring.aop.aspectj.CustomerBo.addCustomer(..))")
        public void logBefore(JoinPoint joinPoint){
            System.out.println("logBefore() is running ...");
            System.out.println("hijacked:"+joinPoint.getSignature().getName());
            System.out.println("**********");
        }
    
        @After("execution(public * com.shiyanlou.spring.aop.aspectj.CustomerBo.deleteCustomer(..))")
        public void logAfter(JoinPoint joinPoint){
            System.out.println("logAfter() is running ...");
            System.out.println("hijacked:"+joinPoint.getSignature().getName());
            System.out.println("**********");
        }
    }
    

    解释:

    1、必须使用 @Aspect 在 LoggingAspect 声明之前注释,以便被框架扫描到。
    2、此例 Advice 和 Pointcut 结合在一起,类中的具体方法 logBefore 和 logAfter 即为 Advice,是要注入的代码,Advice 方法上的表达式为 Pointcut 表达式,即定义了切入点,上例中 @Before 注释的表达式代表执行 CustomerBo.addCustomer 方法时注入 logBefore 代码。
    3、在 LoggingAspect 方法上加入 @Before 或者 @After 等注释。
    4、execution(public * com.shiyanlou.spring.aop.aspectj.CustomerBo.addCustomer(..)) 是 Aspect 的切入点表达式,其中,* 代表返回类型,后边的就要定义要拦截的方法名。这里写的的是 com.shiyanlou.spring.aop.aspectj.CustomerBo.addCustomer 表示拦截 CustomerBo 中的 addCustomer 方法,(..) 代表参数匹配,此处表示匹配任意数量的参数,可以是 0 个也可以是多个,如果你确定这个方法不需要使用参数可以直接用 (),还可以使用 () 来匹配一个任意类型的参数,还可以使用 ( , String ),这样代表匹配两个参数,第二个参数必须是 String 类型的参数。
    5、AspectJ 表达式,可以对整个包定义,例如 execution ( * com.shiyanlou.spring.aop.aspectj..(..)) 表示切入点是 com.shiyanlou.spring.aop.aspectj 包中的任意一个类的任意方法,具体的表达式请自行搜索。

    配置 SpringAopAspectj.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"
        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">
    
        <aop:aspectj-autoproxy/>
    
        <bean id = "customerBo" class = "com.shiyanlou.spring.aop.aspectj.CustomerBo"/>
    
        <bean id = "logAspect" class = "com.shiyanlou.spring.aop.aspectj.LoggingAspect" />
    
    </beans>
    

    <aop:aspectj-autoproxy/> 启动 AspectJ 支持,这样 Spring 会自动寻找用 @Aspect 注释过的类,其他的配置与 Spring 普通 bean 配置一样。

    执行 App.java 如下:

    package com.shiyanlou.spring.aop.aspectj;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    
    public class App {
        public static void main(String[] args) {
    
            ApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] { "SpringAOPAspectj.xml" });
            ICustomerBo customer = (ICustomerBo)appContext.getBean("customerBo");
    
            customer.addCustomer();
    
            System.out.println("-------------------------------------------");
    
            customer.deleteCustomer();
    
        }
    }
    

    控制台输入命令:

    mvn clean compile
    mvn exec:java -Dexec.mainClass="com.shiyanlou.spring.aop.aspectj.App"
    

    实验结果如下:

    将 Advice 和 Pointcut 分开

    需要三步:

    1、创建 Pointcut
    2、创建 Advice
    3、配置 Spring 的配置文件

    定义 Pointcut,创建 PointcutsDefinition.java,如下:

    package com.shiyanlou.spring.aop.aspectj;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    
    @Aspect
    public class PointcutsDefinition {
    
        @Pointcut("execution(* com.shiyanlou.spring.aop.aspectj.CustomerBo.*(..))")
        public void customerLog() {
        }
    }
    

    解释:

    1、类声明前加入 @Aspect 注释,以便被框架扫描到。
    2、@Pointcut 是切入点声明,指定需要注入的代码的位置,如上例中指定切入点为 CustomerBo 类中的所有方法,在实际业务中往往是指定切入点到一个逻辑层,例如 execution (* com.shiyanlou.spring.aop.aspectj..(..)),表示 aop 切入点为 aspectj 包中所有类的所有方法,具体的表达式后边会有介绍。
    3、方法 customerLog 是一个签名,在 Advice 中可以用此签名代替切入点表达式,所以不需要在方法体内编写实际代码,只起到助记功能,例如此处代表操作 CustomerBo 类时需要的切入点。
    创建 LoggingAspect.java:

    package com.shiyanlou.spring.aop.aspectj;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    @Aspect
    public class LoggingAspect {
    
        @Before("com.shiyanlou.spring.aop.aspectj.PointcutsDefinition.customerLog()")
        public void logBefore(JoinPoint joinPoint){
            System.out.println("logBefore() is running ...");
            System.out.println("hijacked:"+joinPoint.getSignature().getName());
            System.out.println("**********");
        }
    
        @After("com.shiyanlou.spring.aop.aspectj.PointcutsDefinition.customerLog()")
        public void logAfter(JoinPoint joinPoint){
            System.out.println("logAfter() is running ...");
            System.out.println("hijacked:"+joinPoint.getSignature().getName());
            System.out.println("**********");
        }
    }
    

    解释:

    1、@Before 和 @After 使用 PointcutsDefinition 中的方法签名代替 Pointcut 表达式找到相应的切入点,即通过签名找到 PointcutsDefinition 中 customerLog 签名上的 Pointcut 表达式,表达式指定切入点为 CustomerBo 类中的所有方法。所以此例中 Advice 类 LoggingAspect,为 CustomerBo 中的所有方法都加入了 @Before 和 @After 两种类型的两种操作。
    2、对于 PointcutsDefinition 来说,主要职责是定义 Pointcut,可以在其中定义多个切入点,并且可以用便于记忆的方法签名进行定义。
    3、单独定义 Pointcut 的好处是,一是通过使用有意义的方法名,而不是难读的 Pointcut 表达式,使代码更加直观;二是 Pointcut 可以实现共享,被多个 Advice 直接调用。若有多个 Advice 调用某个 Pointcut,而这个 Pointcut 的表达式在将来有改变时,只需修改一个地方,维护更加方便。

    配置 Spring 配置文件。

    配置 SpringAOPAspectJ.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">
    
        <aop:aspectj-autoproxy/>
    
        <bean id = "customerBo" class = "com.shiyanlou.spring.aop.aspectj.CustomerBo"/>
    
        <bean id = "logAspect" class = "com.shiyanlou.spring.aop.aspectj.LoggingAspect" />
    
    </beans>
    

    App.java 不变,运行测试代码 App.java:

    mvn clean compile
    mvn exec:java -Dexec.mainClass="com.shiyanlou.spring.aop.aspectj.App"
    

    结果如下:

  • 相关阅读:
    线程池
    自定义死锁
    不安全线程取钱
    JUC Lock实现类ReentrantLock使用说明
    同步方法跟同步方法块 synchronized
    线程的管程法跟信号灯法_生产者消费模式
    CopyOnWriteArrayList JUC当中安全容器
    inserttextatcursorinacontenteditablediv
    Android开发——NDK开发入门
    Linux下线程同步对象(1)——互斥量
  • 原文地址:https://www.cnblogs.com/sakura579/p/13973974.html
Copyright © 2020-2023  润新知