Hello,大家好,在 Spring核心系列之AOP(一) 中给大家讲了Spring AOP的最强用的注解方式实现AOP(没看的小伙伴最好先看一看,本文后面的例子大多都使用注解开发),这一篇就给大家分享一下Spring如何基于XML来做AOP,文章结构:
这里直接以一个案例的形式对xml的开发形式进行简要分析,定义一个切面类MyAspectXML:
public class MyAspectXML { public void before(){ System.out.println("MyAspectXML====前置通知"); } public void afterReturn(Object returnVal){ System.out.println("后置通知-->返回值:"+returnVal); } public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("MyAspectXML=====环绕通知前"); Object object= joinPoint.proceed(); System.out.println("MyAspectXML=====环绕通知后"); return object; } public void afterThrowing(Throwable throwable){ System.out.println("MyAspectXML======异常通知:"+ throwable.getMessage()); } public void after(){ System.out.println("MyAspectXML=====最终通知..来了"); } }
注意这个类没有加任何注解. 然后看下我们的XML文件:
<!-- 把切面引入到Spring容器中 --> <bean name="myAspectXML" class="com.zdy.MyAspectXML" /> <!-- 配置AOP 切面 --> <aop:config> <!-- 定义切点 --> <aop:pointcut id="pointcut" expression="execution(...)" /> <!-- 定义其他切点函数 --> <aop:pointcut id="otherPointcut" expression="execution(...)" /> <!-- 定义通知 order 定义优先级,值越小优先级越大--> <aop:aspect ref="myAspectXML" order="0"> <!-- 定义通知 method 指定通知方法名,必须与MyAspectXML中的相同 pointcut 指定切点函数 --> <aop:before method="before" pointcut-ref="pointcut" /> <!-- 后置通知 returning="returnVal" 定义返回值 必须与类中声明的名称一样--> <aop:after-returning method="afterReturn" pointcut-ref="pointcut" returning="returnVal" /> <!-- 环绕通知 --> <aop:around method="around" pointcut-ref="pointcut" /> <!--异常通知 throwing="throwable" 指定异常通知错误信息变量,必须与类中声明的名称一样--> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="throwable"/> <!-- method : 通知的方法(最终通知) pointcut-ref : 通知应用到的切点方法 --> <aop:after method="after" pointcut-ref="otherPointcut"/> </aop:aspect> </aop:config>
大家可以看到,<aop:aspect>这个标签其实对应的就是MyAspectXML类,然后在类中组装各种增强(建言)。其实如果在上一篇里对注解的那种形式比较了解的话,可以看到XML注解无非就是把各种元素放到了XML里去组装。效果是一样一样的。xml配置在工作中用的比较少,一般都是注解。所以大家看了了解下就OK了 。也是比较简单的。然后给XMl配置的传送门,参考了看下吧:
首先说一下, 设计到优先级问题的前提是,增强的Pointcut有交集。 Aspect优先级分两种,一种是一个Aspect中定义多个增强。另一种是不同的Aspect中的多个增强.我先说下结论,然后针对不同情况搞俩例子。
@Aspect public class AspectOne { /** * Pointcut定义切点函数 */ @Pointcut("execution(...)") private void myPointcut(){} @Before("myPointcut()") public void beforeOne(){ System.out.println("前置通知....执行顺序1"); } @Before("myPointcut()") public void beforeTwo(){ System.out.println("前置通知....执行顺序2"); } @AfterReturning(value = "myPointcut()") public void AfterReturningThree(){ System.out.println("后置通知....执行顺序3"); } @AfterReturning(value = "myPointcut()") public void AfterReturningFour(){ System.out.println("后置通知....执行顺序4"); } }
打印结果:
前置通知....执行顺序1 前置通知....执行顺序2 后置通知....执行顺序4 后置通知....执行顺序3
@Aspect public class AspectOne implements Ordered { /** * Pointcut定义切点函数 */ @Pointcut("execution(...)") private void myPointcut(){} @Before("myPointcut()") public void beforeOne(){ System.out.println("前置通知..AspectOne..执行顺序1"); } @Before("myPointcut()") public void beforeTwo(){ System.out.println("前置通知..AspectOne..执行顺序2"); } @AfterReturning(value = "myPointcut()") public void AfterReturningThree(){ System.out.println("后置通知..AspectOne..执行顺序3"); } @AfterReturning(value = "myPointcut()") public void AfterReturningFour(){ System.out.println("后置通知..AspectOne..执行顺序4"); } /** * 定义优先级,值越低,优先级越高 * @return */ @Override public int getOrder() { return 0; } } //切面类 AspectTwo.java @Aspect public class AspectTwo implements Ordered { /** * Pointcut定义切点函数 */ @Pointcut("execution(...)") private void myPointcut(){} @Before("myPointcut()") public void beforeOne(){ System.out.println("前置通知....执行顺序1--AspectTwo"); } @Before("myPointcut()") public void beforeTwo(){ System.out.println("前置通知....执行顺序2--AspectTwo"); } @AfterReturning(value = "myPointcut()") public void AfterReturningThree(){ System.out.println("后置通知....执行顺序3--AspectTwo"); } @AfterReturning(value = "myPointcut()") public void AfterReturningFour(){ System.out.println("后置通知....执行顺序4--AspectTwo"); } /** * 定义优先级,值越低,优先级越高 * @return */ @Override public int getOrder() { return 1; } }
输出结果:
前置通知..AspectOne..执行顺序1 前置通知..AspectOne..执行顺序2 前置通知....执行顺序1--AspectTwo 前置通知....执行顺序2--AspectTwo 后置通知....执行顺序4--AspectTwo 后置通知....执行顺序3--AspectTwo 后置通知..AspectOne..执行顺序4 后置通知..AspectOne..执行顺序3
好了,关于AspectJ的优先级分享完了,大家参照着前面总结的结论,再看下两个例子,应该是可以搞懂的。不过说实在的,用的比较少。(^_^)
Spring AOP的实际运用场景其实还是比较多的,别的不说,Spring自己的事务管理其实就运用了AOP,这里我来一个相对而言靠近开发者的案例, 性能监控 。其实说是性能监控,没那么高大上,无非就是计算一下一个Web接口调用的时间。然后打日志或者写入到其他监控平台(granafa等).废话少说,直接撸代码:
先定义一个实体类,用于存监控的一些信息:
public class MonitorData { //类名 private String className; //方法名 private String methodName; //消耗时间 private String consumeTime; //记录时间 private Date logTime; //gettter setter toString什么的省略 .... }
@Aspect @Component public class MonitorAspectJ { /** * 定义切点函数,过滤controller包下的名称以Controller结尾的类所有方法 */ @Pointcut("execution(* com..*Controller.*(..))") void timer() { } @Around("timer()") public Object logTimer(ProceedingJoinPoint thisJoinPoint) throws Throwable { MonitorData monitorData=new MonitorData(); //获取目标类名称 String clazzName = thisJoinPoint.getTarget().getClass().getName(); //获取目标类方法名称 String methodName = thisJoinPoint.getSignature().getName(); //记录类名称 monitorData.setClassName(clazzName); //记录对应方法名称 monitorData.setMethodName(methodName); //记录时间 monitorData.setLogTime(new Date()); // 计时并调用目标函数 long start = System.currentTimeMillis(); Object result = thisJoinPoint.proceed(); Long time = System.currentTimeMillis() - start; //设置消耗时间 monitorData.setConsumeTime(time.toString()); //把monitorTime记录的信息上传给监控系统,并没有实现,需要自行实现即可 //MonitoruUtils.report(monitorTime) System.out.println(monitorData.toString()); return result; } }
其实还是比较好理解的,无非就是在把所有以Controller结尾的类的所有方法上加上环绕,记录时间。然后存储下来(我这是打印了一下).
AOP的应用远不止这两种,诸如缓存,权限验证、内容处理、事务控制等都可以使用AOP实现,其中事务控制Spring中提供了专门的处理方式,限于篇幅就先聊到这。
Spring AOP的底层实现有两种可选,一种是JDK动态代理,一种是CGLib动态代理。先说下结论,如果要代理的target有接口,则默认采用JDK动态代理。如果没有,则采用CGLib动态代理。当然也可以强制指定使用CGLib动态代理。方法:
如果要代理的对象没有实现任何接口,则必须使用CGLIb代理,如果有,则可以使用JDK代理和CGLib代理,至于为什么,其实有点一言难尽,这里不准备展开。后期有机会专门写动态代理了再展开说。对于我们在Web项目中常用的单例类,尽量使用CGLib动态代理来实现Spring AOP.
CGlib特点如下: 字节码技术,启动慢,为每一个方法做索引,效率高。 而JDK动态代理特点则是: 启动快,每个方法通过反射调用,效率低,方法没有索引。
好了,Spring AOP和大家分享完了,有点遗憾的是关于它底层的JDK动态代理和CGLib动态代理没有展开来讲。不过没关系,后期有时间一定专门出一篇动态代理的文章,通过Spring AOP 的两篇文章,希望大家至少能够在使用层面掌握好AOP这种思想,并能运用到工作当中去。Spring,以及Spring boot其实很多地方都运用到了Spring AOP,后面的文章如果涉及到会给大家点出来。 Over ,Have a good day .