使用@AspectJ定义一个切面:

package com.ivy.annotation.aspectj;

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

@Aspect
public class PreGreetingAspect {

    @Before("execution(*greetTo(..))")
    public void beforeGreeting() {
        System.out.println("How are you");
    }
}

第三方处理程序通过类中是否拥有@Aspect注解判断其是否为一个切面。@Before注解表示该增强是前置增强,成员值是一个切点表达式,意思是在目标类的greetTo()方法上织入增强,greetTo()方法可以带任意的入参和任意的返回值。而beforeGreeting()方法就是增强所使用的横切逻辑,该横切逻辑在目标方法前调用。

  1. 编程方式织入

ProxyFactory还有另一个兄弟,AspectJProxyFactory,通过AspectJProxyFactory可以实现Aspect定义到目标对象的织入。例如:为NaiveWaiter生成织入PreGreetingAspect切面的代理:

package com.ivy.annotation.aspectj;

import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;

import com.ivy.aop.advice.NaiveWaiter;
import com.ivy.aop.advice.Waiter;

public class AspectJProxyTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Waiter target = new NaiveWaiter();
        AspectJProxyFactory factory = new AspectJProxyFactory();
        factory.setTarget(target);
        factory.addAspect(PreGreetingAspect.class);
        Waiter proxy = factory.getProxy();
        proxy.greetTo("John");
        proxy.serveTo("John");
    }

}

首先AspectProxyFactory设置了目标对象,之后添加一个切面类,该类必须是带@Aspect注解的类,然后就可以获取织入了切面的代理对象了。

运行结果:

log4j:WARN No appenders could be found for logger (org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory).
log4j:WARN Please initialize the log4j system properly.
How are you
greet to John...
serve to John...

 

  2. 自动代理织入

  通过配置使用@AspectJ切面

  一般情况都是通过Spring的配置完成切面织入的工作:

<?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-3.0.xsd">
  <aop:aspectj-autoproxy proxy-target-class="true">
 </aop:aspectj-autoproxy>
<bean id="waiter" class="com.ivy.aop.advice.NaiveWaiter"/> <bean class="com.ivy.annotation.aspectj.PreGreetingAspect"/> </beans>

  

  • @AspectJ形式的Pointcut

  @AspectJ形式的Pointcut声明,依附在@Aspect所标注的Aspect定义类之内,通过Pointcut注解,指定AspectJ形式的Pointcut表达式之后,将这个指定了相应表达式的注解标注到Aspect定义类的某个方法上即可。

Pointcut表述语言中有以下几种可用标志符:

  execution

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?), 其中方法的返回类型/方法名以及参数部分的匹配模式是必须指定的,其他部分的匹配模式可以省略。

execution中使用两种通配符,即*和..。例如 execution(public void Foo.doSomething(String))

  1. * 可以用于任何部分的匹配模式中,可以匹配相邻的多个字符,即一个Word。所以可以简化为execution(* *(String))
  2. ..可以在两个位置使用,一个在declaring-type-pattern规定的位置,一个在方法参数匹配模式的位置。例如

      execution(void cn.spring21.*.doSomething(*)) // 只能指定到cn.spring21这一层下的所有类

      execution(void cn.spring21..*.doSomething(*)) // 可以匹配cn.spring21包下的所有类,以及cn.spring21下层包下声明的所有类型

    如果..用于方法参数列表匹配位置,则表示该方法可以有0到多个参数,参数类型不限。如下:

      execution(void *.doSomething(..))

  within

within标志符只接受类型声明,它将会匹配指定类型下的所有Joinpoint。不过,因为SpringAoP只支持方法级别的Joinpoint,所以在我们为within指定某个类后,它将匹配指定类所声明的所有方法执行。

  

  切点表达式函数

  切点表达式由关键字和操作参数组成,Spring支持9个切点表达式函数,5个类型:

  1.  方法切点函数

    execution(): 方法匹配模式串,execution(* greetTo(..))表示所有目标类的greetTo()方法

    @annotation(): 方法注解类名,@annotation(com.ivy.anno.NeedTest)表示任何标注了@NeedTest注解的目标类

  2.    方法入参切点函数

    args(类名): args(com.ivy.advice.Waiter)表示所有有且仅有一个按类型匹配于Waiter入参的方法

    @args(类型注解类名): @args(com.ivy.Monitorable)表示任何这样的一个目标方法:它有一个入参且入参对象的类标注@Monitorable注解

  3.   目标类切点函数

    within(类名匹配串): within(com.ivy.service.*)表示com.ivy.service包中的所有连接点,即包中所有类的所有方法。而within(com.ivy.service.*Service)表示com.ivy.service包中所有以Service结尾的类的所有连接点。

    target(类名): ,target(com.ivy.Waiter)定义的切点,Waiter以及Waiter实现类NaiveWaiter中所有连接点都匹配该切点

    @within(类型注解类名): @within(com.ivy.Monitorable)定义的切点,假如Waiter类标注了@Monitorable注解,则Waiter以及NaiveWaiter类的所有连接点都匹配

  4.   目标类切点函数

    @target(类型注解类名): @target(com.ivy.Monitorable),假如NaiveWaiter标注了@Monitorable,则NaiveWaiter所有连接点匹配切点。

  5.   代理类切点函数

    this(类名): 代理类按类型匹配于指定类,则被代理的目标类所有连接点匹配切点。

 注意:

  所有以@开头的标志符,都只能指定注解类型参数,而且,注解只有Java 5及之后才有效。

 

  不同增强类型

  @Before 前置增强

  @AfterReturning 后置增强

  @Around 环绕增强

  @AfterThrowing 抛出增强

  @After Final增强,不管是抛出异常或是争抢退出,该增恰国内都会得到执行,该增强没有对应的增强接口,一般用于释放资源。

  @DeclareParents 引介增强

  示例:

@Aspect
public class MockAspect {
    @Pointcut("execution(* destroy(..))")
    public void destroy(){}

    @Before("execution(public void *.methodName(String))")
    public void setUpResourceFolder() {
        ...
    }

    @After("destroy()")
    public void cleanUpResourceIfNeccessary() {
        ...
    }
}

 

  切点复合运算

package com.ivy.annotation.aspectj;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Before;

public class TestAspect {

    // 匹配com.ivy包中所有greetTo()方法的切点
    @After("within(com.ivy.*)&&execution(* greetTo(..))")
    public void greetToFun() {
        System.out.println("--greetToFun() executed!");
    }
    
    // 匹配所有serveTo()方法并且该方法不位于NaiveWaiter目标类的切点
    @Before("!target(com.ivy.NaiveWaiter)&&execution(* serveTo(..))")
    public void notServeInNaiveWaiter() {
        System.out.println("--notServeInNaiveWaiter() executed!");
    }
    
    // 匹配Waiter和Seller接口实现类所有连接点的切点
    @AfterReturning("target(com.ivy.Waiter)||target(com.ivy.Seller)")
    public void waiterOrSeller() {
        System.out.println("--waiterOrSeller() executed!");
    }
}

  

  命名切点

  切点直接声明在增强方法处,这种切点声明方式称为匿名切点,匿名切点只能在声明处使用。如果希望在其他地方重用一个切点,可以通过@Pointcut注解以及切面类方法对切点进行命名。

package com.ivy.annotation.aspectj;

import org.aspectj.lang.annotation.Pointcut;

public class TestNamePointcut {

    // 通过注解方法inPackage(), 该方法名就作为切点的名称,
    // 方法修饰符为private,表明该命名切点只能在本切面类中使用
    @Pointcut("within(com.ivy)")
    private void inPackage() {}
    
    // 通过注解方法inPackage(), 该方法名就作为切点的名称,
    // 方法修饰符为protected,表明该命名切点可以在当前包中的切面类,子切面类中使用
    @Pointcut("execution(* greetTo(..))")
    protected void greetTo() {}
    
    // 通过注解方法inPackage(), 该方法名就作为切点的名称,
    // 方法修饰符为public,表明该命名切点可以在任意类中使用
    @Pointcut("inPackage() and greetTo()")
    public void inPkgGreetTo() {}
}

  inPkgGreetTo可以引用同类的greetTo()和inPackage(),而inPkgGreetTo()可以被任何类使用。

package com.ivy.annotation.aspectj;

import org.aspectj.lang.annotation.Before;

public class TestNamePoint {

    @Before("NamePointcut.inPkgGreetTo()")
    public void pkgGreetTo() {
        System.out.println("--pgkGreetTo() executed!--");
    }
    
    @Before("!target(com.ivy.NaiveWaiter)&&NamePointcut.inPkgGreetTo()")
    public void pkgGreetToNotNaiveWaiter() {
        System.out.println("--pgkGreetTo() executed!--");
    }
}

 

  增强织入顺序

  1.  如果增强在同一个切面类中声明,则依照增强在切面类中定义的顺序进行织入。
  2.  如果增强位于不同的切面类中,且这些切面类都实现了Ordered接口,则由接口方法的顺序号决定。
  3.  如果增强位于不同的切面类中,且这些切面没有实现Ordered接口,则顺序不定。

 

posted on 2017-03-04 09:29  coder为  阅读(309)  评论(0编辑  收藏  举报