Spring3系列12- Spring AOP AspectJ

Spring3系列12- Spring AOP AspectJ

 

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

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

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

AspectJ是基于注释(Annotation)的,因此须要JDK5.0以上的支持。express

AspectJ支持的注释类型以下:app

  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以下:this

 

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);

    }

}

 

1、      简单的AspectJ,Advice和Pointcut结合在一块儿

首先没有引入Pointcut以前,Advice和Pointcut是混在一块儿的spa

步骤,只须要两步,以下:code

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

 

第一步,建立Aspect类orm

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

**********

 

2、      将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

**********

 

3、     切入点表达式

Spring3.0.5帮助文档中的切入点表达式以下:

 

Some examples of common pointcut expressions are given below.

 

the execution of any public method:

execution(public * *(..))


the execution of any method with a name beginning with "set":

execution(* set*(..))


the execution of any method defined by the AccountService interface:

execution(* com.xyz.service.AccountService.*(..))


the execution of any method defined in the service package:

execution(* com.xyz.service.*.*(..))


the execution of any method defined in the service package or a sub-package:

execution(* com.xyz.service..*.*(..))


any join point (method execution only in Spring AOP) within the service package:

within(com.xyz.service.*)


any join point (method execution only in Spring AOP) within the service package or a sub-package:

within(com.xyz.service..*)


any join point (method execution only in Spring AOP) where the proxy implements the AccountService interface:

this(com.xyz.service.AccountService)

'this' is more commonly used in a binding form :- see the following section on advice for how to make the proxy object available in the advice body.

 

any join point (method execution only in Spring AOP) where the target object implements the AccountService interface:

target(com.xyz.service.AccountService)

'target' is more commonly used in a binding form :- see the following section on advice for how to make the target object available in the advice body.

 

any join point (method execution only in Spring AOP) which takes a single parameter, and where the argument passed at runtime is Serializable:

args(java.io.Serializable)
'args' is more commonly used in a binding form :- see the following section on advice for how to make the method arguments available in the advice body.

Note that the pointcut given in this example is different to execution(* *(java.io.Serializable)): the args version matches if the argument passed at runtime is Serializable, the execution version matches if the method signature declares a single parameter of type Serializable.

 

any join point (method execution only in Spring AOP) where the target object has an @Transactional annotation:

@target(org.springframework.transaction.annotation.Transactional)

'@target' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

 

any join point (method execution only in Spring AOP) where the declared type of the target object has an @Transactional annotation:

@within(org.springframework.transaction.annotation.Transactional)

'@within' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

 

any join point (method execution only in Spring AOP) where the executing method has an @Transactional annotation:

@annotation(org.springframework.transaction.annotation.Transactional)

'@annotation' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

 

any join point (method execution only in Spring AOP) which takes a single parameter, and where the runtime type of the argument passed has the@Classified annotation:

@args(com.xyz.security.Classified)

'@args' can also be used in a binding form :- see the following section on advice for how to make the annotation object(s) available in the advice body.

 

any join point (method execution only in Spring AOP) on a Spring bean named 'tradeService':

bean(tradeService)


any join point (method execution only in Spring AOP) on Spring beans having names that match the wildcard expression '*Service':

bean(*Service)

相关文章
相关标签/搜索