源码学习之AOP(三)-Spring AOP使用

 

 

 

接上文增强的内容,本文我们将介绍下Spring AOP的使用,以及增强执行的顺序。

 

         在spring中使用aop有很多种选择,即可以选择拥有完整aop解决方案的AspectJ,还可以选择Spring AOP,它侧重于提供和ioc容器整合的AOP实现。我们接下来就介绍下他的两种使用方式:@AspectJSchema-based AOP.

 

@AspectJ support

         在上文中,我们只是介绍了增强的使用,通过增强我们定义了横切逻辑,也就是我们希望代理要做的事,但是并没有指定要增强的地方,结果就是所有的方法都被我们进行了增强。

在aop中,我们可以通过切点(pointcut)来决定增强(advice)发生的具体地方,增强(advice)和切点(pointcut)的组合也就是我们的切面(aspect).

         @AspectJ这种方式来使用aop,其实也就是通过aspectj中的注解来定义切面,确定切点和增强。

主要注解

         @Aspect:定义切面

         @Pointcut:定义切点

         @Before:定义前置增强

         @After:定义后置增强

         @Around:定义环绕增强

         @AfterReturning:定义后置通知

@AfterThrowing:定义异常抛出增强

@DeclareParents:用于定义引介增强

使用实例

         我们还是使用之前的喇叭场景来进行演示:

AbstractTrumpet:

public interface AbstractTrumpet {
    /**
     * 广播时间
     */
    void broadcast();

    /**
     * 模拟抛出异常
     */
    void broken();
}

 

Trumpet:

public class Trumpet implements AbstractTrumpet{

    @Override
    public void broadcast() {
        System.out.println("目标方法");
    }

    @Override
    public void broken() {
        throw new RuntimeException();
    }
}

 

TrumpetAdvice:

@Aspect
public class TrumpetAdvice {

    @Pointcut("execution(* Trumpet.broadcast(..))")
    public void pointcut(){}


    @Before("pointcut()")
    public void before(){
        System.out.println("Before");
    }

    @After("pointcut()")
    public void after(){
        System.out.println("After");
    }

    @AfterReturning("pointcut()")
    public void afterReturning(){
        System.out.println("AfterReturning");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Around-before");
        Object obj = joinPoint.proceed();
        System.out.println("Around-after");
        return obj;
    }

}

xml:

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

<bean id="trumpet" class="com.ljw.aop.aspectj.Trumpet"></bean>

<bean id="advice" class="com.ljw.aop.aspectj.TrumpetAdvice"></bean>

 

 

test:

public class TestTrumpet {
    public static void main(String[] args) {
        ApplicationContext context = new FileSystemXmlApplicationContext("//Users/liujiawei/code view/spring/SpringMVCWithOutMaven/web/WEB-INF/Trumpet4Aspectj.xml");
        AbstractTrumpet trumpet = (AbstractTrumpet) context.getBean("trumpet");
        trumpet.broadcast();
    }
}

 

运行结果:

源码学习之AOP(三)-Spring AOP使用

 

通过上面的结果,我们可以看到,我们通过@Aspect注解标明这个类是一个切面,通过@Pointcut结合里面的表达式确定切点的执行地方时Trumpetbroadcast方法,在xml中,需要通过<aop:aspectj-autoproxy/>来开启对这类注解的支持。

         除了上面的这些注解外,还有一个引介增强,我们知道引介增强是一个特殊的增强,它可以给目标类通过实现额外的接口扩展属性和方法,我们看下怎样通过注解来使用引介增强。

额外的接口类:

public interface ExtraInterface {
    void intf();
}

 

额外的实现类:

public class ExtraImplement implements ExtraInterface {
    @Override
    public void intf() {
        System.out.println("extraImplement");
    }
}

 

在切面中的定义:

@DeclareParents(value = "com.ljw.aop.aspectj.Trumpet", defaultImpl = ExtraImplement.class)
ExtraInterface extraInterface = new ExtraImplement();

 

*通过@DeclareParents表示这个是引介增强,他有两个属性,value表示希望扩展的目标类,defaultImpl表示选择的实现类。

 

test

public class TestTrumpet {
    public static void main(String[] args) {
        ApplicationContext context = new FileSystemXmlApplicationContext("//Users/liujiawei/code view/spring/SpringMVCWithOutMaven/web/WEB-INF/Trumpet4Aspectj.xml");
        AbstractTrumpet trumpet = (AbstractTrumpet) context.getBean("trumpet");
        trumpet.broadcast();
        ExtraInterface extraInterface = (ExtraInterface) trumpet;
        extraInterface.intf();
    }
}

 

运行结果:

源码学习之AOP(三)-Spring AOP使用

可以看到,通过上面的配置,我们的目标类成功扩展了我们添加的接口。

 

 

通过上面的这些注解使用,我们基本上已经可以正常使用我们的spring aop了,现在我们来看下增强执行的顺序:

         通过上面的运行结果,我们可以得到这样一个顺序:

         @Around-before > @Before > @Around-after > @After > @AfterReturning

对于上面的这个顺序,我们没有必要去死记硬背,既然是横切逻辑,那么肯定要结合切面来分析这个执行顺序,我给大家画一幅图,通过这幅图我们对执行顺序就可以一目了然:

增强顺序

 

单个切面的增强顺序

        

源码学习之AOP(三)-Spring AOP使用

 

         这样子再来看我们的增强执行顺序是不是很清楚了。

 

多个切面的增强顺序

         上面只是单个切面的情况,我们知道,通过Ordered接口,我们可以改变切面的优先级,返回的order越小,优先级也就越高,如果是两个切面,那么它们的执行顺序会是什么样子呢?

Target:

public class Target {
    public void test(){
        System.out.println("target-method");
    }
}

AspectOne:

@Aspect
public class AspectOne implements Ordered {

    @Pointcut("execution(* Target.test(..))")
    public void pointcut() {

    }

    @Before("pointcut()")
    public void before() {
        System.out.println("AspectOne-before");
    }

    @After("pointcut()")
    public void after() {
        System.out.println("AspectOne-after");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("AspectOne-Around-before");
        Object obj = joinPoint.proceed();
        System.out.println("AspectOne-Around-after");
        return obj;
    }

    @AfterReturning("pointcut()")
    public void afterReturning() {
        System.out.println("AspectOne-afterReturning");
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

 

AspectTwo:

@Aspect
public class AspectTwo implements Ordered {
    @Pointcut("execution(* *.*(..))")
    public void pointcut() {

    }

    @Before("pointcut()")
    public void before() {
        System.out.println("AspectTwo-before");
    }

    @After("pointcut()")
    public void after() {
        System.out.println("AspectTwo-after");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("AspectTwo-Around-before");
        Object obj = joinPoint.proceed();
        System.out.println("AspectTwo-Around-after");
        return obj;
    }

    @AfterReturning("pointcut()")
    public void afterReturning() {
        System.out.println("AspectTwo-afterReturning");
    }

    @Override
    public int getOrder() {
        return 2;
    }
}

 

XML:

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

<bean id="target" class="com.ljw.aop.aspectj.Target"></bean>

<bean id="aspectOne" class="com.ljw.aop.aspectj.AspectOne"></bean>

<bean id="aspectTwo" class="com.ljw.aop.aspectj.AspectTwo"></bean>

 

test:

public class TestOrderedAspect {
    public static void main(String[] args) {
        ApplicationContext context = new FileSystemXmlApplicationContext("//Users/liujiawei/code view/spring/SpringMVCWithOutMaven/web/WEB-INF/OrderedAspect.xml");
        Target target = (Target) context.getBean("target");
        target.test();
    }
}

 

运行结果:

源码学习之AOP(三)-Spring AOP使用

 

       上面的结果和你想的一样么?如果不一样也没有关系,我又画了一幅图来帮助大家理解:

源码学习之AOP(三)-Spring AOP使用

 

        

这样子是不是又一目了然了。

 

Schema-based AOP support

         除了上面通过注解的方式来进行aop的配置以外,我们还可以直接通过配置文件来完成aop的使用。

         与上面的使用相比,我们主要的区别在于配置文件,增强类的aop注解也可以全部去掉,把它就当作一个普通的java类即可。

Advice:

@Pointcut("execution(* Trumpet.broadcast(..))")
public void pointcut() {
}


@Before("pointcut()")
public void before() {
    System.out.println("Before");
}

@After("pointcut()")
public void after() {
    System.out.println("After");
}

@AfterReturning("pointcut()")
public void afterReturning() {
    System.out.println("AfterReturning");
}

@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("Around-before");
    Object obj = joinPoint.proceed();
    System.out.println("Around-after");
    return obj;
}

xml:

<bean id="trumpet" class="com.ljw.aop.aspectj.Trumpet"></bean>

<bean id="advice" class="com.ljw.aop.aspectj.TrumpetAdvice2"></bean>

<aop:config>
    <aop:pointcut id="pc" expression="execution(* *.*(..))"></aop:pointcut>
    <aop:aspect ref="advice">
        <aop:before method="before" pointcut-ref="pc"></aop:before>
        <aop:after method="after" pointcut-ref="pc"></aop:after>
        <aop:around method="around" pointcut-ref="pc"></aop:around>
        <aop:after-returning method="afterReturning" pointcut-ref="pc"></aop:after-returning>
    </aop:aspect>
</aop:config>

test:

public class TestTrumpet {
    public static void main(String[] args) {
        ApplicationContext context = new FileSystemXmlApplicationContext("//Users/liujiawei/code view/spring/SpringMVCWithOutMaven/web/WEB-INF/SchemaTrumpet.xml");
        AbstractTrumpet trumpet = (AbstractTrumpet) context.getBean("trumpet");
        trumpet.broadcast();
    }
}

 

运行结果:

源码学习之AOP(三)-Spring AOP使用

 

我们再看下引介增强怎么表示:

<aop:config>
    <aop:pointcut id="pc" expression="execution(* *.*(..))"></aop:pointcut>
    <aop:aspect ref="advice">
        <aop:declare-parents types-matching="com.ljw.aop.aspectj.Trumpet"
                             implement-interface="com.ljw.aop.aspectj.ExtraInterface"
                             default-impl="com.ljw.aop.aspectj.ExtraImplement"></aop:declare-parents>
    </aop:aspect>
</aop:config>

        

         通过<aop:declare-parents>来声明一个引介增强,

通过types-matching声明需要扩展的对象

通过implement-interface声明扩展的接口

通过default-impl声明扩展接口的实现类

 

总结

         以上就是spring aop的两种常见的使用方法,第一种需要jar包的支持,第二种直接使用spring的标签即可。