源码学习之AOPSpring AOP使用
Posted 刘老c
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码学习之AOPSpring AOP使用相关的知识,希望对你有一定的参考价值。
接上文增强的内容,本文我们将介绍下Spring AOP的使用,以及增强执行的顺序。
在spring中使用aop有很多种选择,即可以选择拥有完整aop解决方案的AspectJ,还可以选择Spring AOP,它侧重于提供和ioc容器整合的AOP实现。我们接下来就介绍下他的两种使用方式:@AspectJ和Schema-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();
}
}
运行结果:
通过上面的结果,我们可以看到,我们通过@Aspect注解标明这个类是一个切面,通过@Pointcut结合里面的表达式确定切点的执行地方时Trumpet的broadcast方法,在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();
}
}
运行结果:
可以看到,通过上面的配置,我们的目标类成功扩展了我们添加的接口。
通过上面的这些注解使用,我们基本上已经可以正常使用我们的spring aop了,现在我们来看下增强执行的顺序:
通过上面的运行结果,我们可以得到这样一个顺序:
@Around-before > @Before > @Around-after > @After > @AfterReturning
对于上面的这个顺序,我们没有必要去死记硬背,既然是横切逻辑,那么肯定要结合切面来分析这个执行顺序,我给大家画一幅图,通过这幅图我们对执行顺序就可以一目了然:
增强顺序
单个切面的增强顺序
这样子再来看我们的增强执行顺序是不是很清楚了。
多个切面的增强顺序
上面只是单个切面的情况,我们知道,通过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();
}
}
运行结果:
上面的结果和你想的一样么?如果不一样也没有关系,我又画了一幅图来帮助大家理解:
这样子是不是又一目了然了。
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使用的主要内容,如果未能解决你的问题,请参考以下文章
Springday03 AOPSpring声明式事务Spring编程式事务