Hello,你们好,在Spring核心系列之AOP(一)中给你们讲了Spring AOP的最强用的注解方式实现AOP(没看的小伙伴最好先看一看,本文后面的例子大多都使用注解开发),这一篇就给你们分享一下Spring如何基于XML来作AOP,文章结构:html
这里直接以一个案例的形式对xml的开发形式进行简要分析,定义一个切面类MyAspectXML:java
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文件:express
<!-- 把切面引入到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中的多个加强.我先说下结论,而后针对不一样状况搞俩例子。bash
@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");
}
}
复制代码
打印结果:ide
前置通知....执行顺序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的优先级分享完了,你们参照着前面总结的结论,再看下两个例子,应该是能够搞懂的。不过说实在的,用的比较少。(^_^)post
Spring AOP的实际运用场景其实仍是比较多的,别的不说,Spring本身的事务管理其实就运用了AOP,这里我来一个相对而言靠近开发者的案例,性能监控。其实说是性能监控,没那么高大上,无非就是计算一下一个Web接口调用的时间。而后打日志或者写入到其余监控平台(granafa等).废话少说,直接撸代码:性能
先定义一个实体类,用于存监控的一些信息:this
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 .