源码学习之AOPSpring AOP使用

Posted 刘老c

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码学习之AOPSpring 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: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的标签即可。



欢迎扫码一起交流学习



    


以上是关于源码学习之AOPSpring AOP使用的主要内容,如果未能解决你的问题,请参考以下文章

源码学习之AOP-代理对象的生成

Springday03 AOPSpring声明式事务Spring编程式事务

Concurrent包学习之 BlockingQueue源码学习

tensorflow源码学习之一 -- tf架构

自问自答系列-caffe源码学习之总体学习

源码学习之线程池