Spring AOP AspectJ

本文讲述使用AspectJ框架实现Spring AOP。

再重复一下Spring AOP中的三个概念,

  1. Advice:向程序内部注入的代码。
  2. Pointcut:注入Advice的位置,切入点,一般为某方法。
  3. Advisor:Advice和Pointcut的结合单元,以便将Advice和Pointcut分开实现灵活配置。

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

AspectJ支持的注释类型如下:

  1. @Before
  2. @After
  3. @AfterReturning
  4. @AfterThrowing
  5. @Around

首先定义一个简单的bean,CustomerBo实现了接口ICustomerBo

ICustomerBo.java如下:

复制代码
package com.lei.demo.aop.aspectj;

public interface ICustomerBo {
    void addCustomer();
    void deleteCustomer();
    String AddCustomerReturnValue();
    void addCustomerThrowException() throws Exception;
    void addCustomerAround(String name);

}
复制代码

 CustomerBo.java如下:

复制代码
package com.lei.demo.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结合在一起

首先没有引入Pointcut之前,Advice和Pointcut是混在一起的

步骤,只需要两步,如下:

  1. 创建一个Aspect类
  2. 配置Spring配置文件

第一步,创建Aspect类

LoggingAspect.java如下:

复制代码
package com.lei.demo.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.lei.demo.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.lei.demo.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.  必须使用@AspectLoggingAspect声明之前注释,以便被框架扫描到

2.  此例AdvicePointcut结合在一起,类中的具体方法logBeforelogAfter即为Advice,是要注入的代码,Advice方法上的表达式为Pointcut表达式,即定义了切入点,上例中@Before注释的表达式代表执行CustomerBo.addCustomer方法时注入logBefore代码。

3.  LoggingAspect方法上加入@Before或者@After等注释

4.  "execution(public * com.lei.demo.aop.aspectj.CustomerBo.addCustomer(..))"Aspect的切入点表达式,其中,*代表返回类型,后边的就要定义要拦截的方法名,这里写的的是com.lei.demo.aop.aspectj.CustomerBo.addCustomer表示拦截CustomerBo中的addCustomer方法,(..)代表参数匹配,此处表示匹配任意数量的参数,可以是0个也可以是多个,如果你确定这个方法不需要使用参数可以直接用(),还可以使用(*)来匹配一个任意类型的参数,还可以使用 (* , String),这样代表匹配两个参数,第二个参数必须是String 类型的参数

5.  AspectJ表达式,可以对整个包定义,例如,execution(* com.lei.service..*.*(..))表示切入点是com.lei.sevice包中的任意一个类的任意方法,具体的表达式请自行百度。

第二步,配置Spring配置文件,

配置Spring-AOP-AspectJ.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-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
    <aop:aspectj-autoproxy/>
    
    <bean id="customerBo" class="com.lei.demo.aop.aspectj.CustomerBo"/>
    
    <bean id="logAspect" class="com.lei.demo.aop.aspectj.LoggingAspect" />
    
</beans>
复制代码

解释:

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

测试:

执行App.java如下:

复制代码
package com.lei.demo.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[] { "Spring-AOP-AspectJ.xml" });
        ICustomerBo customer=(ICustomerBo)appContext.getBean("customerBo");
        
customer.addCustomer();
        
        System.out.println("-------------------------------------------");
        
customer.deleteCustomer();
        
    }
}
复制代码

结果:

logBefore() is running ...

hijacked:addCustomer

**********

addCustomer() is running ...

-------------------------------------------

deleteCustomer() is running ...

logAfter() is running ...

hijacked:deleteCustomer

**********

二、      将Advice和Pointcut分开

需要三步,

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

第一步,PointcutsDefinition.java定义了Pointcut,如下:

复制代码
package com.lei.demo.aop.aspectj;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class PointcutsDefinition {

    @Pointcut("execution(* com.lei.demo.aop.aspectj.CustomerBo.*(..))")
    public void customerLog() {
    }
}
复制代码

解释:

1. 类声明前加入@Aspect注释,以便被框架扫描到。

2. @Pointcut是切入点声明,指定需要注入的代码的位置,如上例中指定切入点为CustomerBo类中的所有方法,在实际业务中往往是指定切入点到一个逻辑层,例如 execution (* com.lei.business.service.*.*(..)),表示aop切入点为service包中所有类的所有方法,具体的表达式后边会有介绍。

3. 方法customerLog是一个签名,在Advice中可以用此签名代替切入点表达式,所以不需要在方法体内编写实际代码,只起到助记功能,例如此处代表操作CustomerBo类时需要的切入点。

第二步,创建Advice类

LoggingAspect.java如下:

复制代码
package com.lei.demo.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.lei.demo.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.lei.demo.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表达式找到相应的切入点,即通过签名找到PointcutsDefinitioncustomerLog签名上的Pointcut表达式,表达式指定切入点为CustomerBo类中的所有方法。所以此例中AdviceLoggingAdvice,为CustomerBo中的所有方法都加入了@Before@After两种类型的两种操作。

2.       对于PointcutsDefinition来说,主要职责是定义Pointcut,可以在其中第一多个切入点,并且可以用便于记忆的方法签名进行定义。

3.       单独定义Pointcut的好处是,一是通过使用有意义的方法名,而不是难读的Pointcut表达式,使代码更加直观;二是Pointcut可以实现共享,被多个Advice直接调用。若有多个Advice调用某个Pointcut,而这个Pointcut的表达式在将来有改变时,只需修改一个地方,维护更加方便。

第三步,配置Spring配置文件,配置文件并没有改变

配置Spring-AOP-AspectJ.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-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
    <aop:aspectj-autoproxy/>
    
    <bean id="customerBo" class="com.lei.demo.aop.aspectj.CustomerBo"/>
    
    <bean id="logAspect" class="com.lei.demo.aop.aspectj.LoggingAspect" />
    
</beans>
复制代码

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

输出结果:

logBefore() is running ...

hijacked:addCustomer

**********

addCustomer() is running ...

logAfter() is running ...

hijacked:addCustomer

**********

-------------------------------------------

logBefore() is running ...

hijacked:deleteCustomer

**********

deleteCustomer() is running ...

logAfter() is running ...

hijacked:deleteCustomer

**********

原文地址:https://www.cnblogs.com/chinajava/p/5731326.html