AOP 应用:
1. 监控系统重要API的调用事件,用来监控系统的性能。
2.Authentication 权限
3. Caching 缓存
4. Context passing 内容传递
5. Error handling 错误处理
6. Lazy loading 懒加载
7. Debugging 调试
8. logging, tracing, profiling and monitoring 记录跟踪 优化 校准
9. Performance optimization 性能优化
10. Persistence 持久化
11. Resource pooling 资源池
12. Synchronization 同步
13. Transactions 事务
......html
相信只要使用过Spring框架的,你们对于AOP都不陌生,尤为提起它就能马上随口说出,通常用在日志处理、异常处理、权限验证等方面。但刚开始接触不免会有各类各样的疑惑,今天抽时间,按照以前的理解整理了一份关于Spring AOP的简单教程,但愿可以帮助你们尽快的了解它的实现过程及原理。首先来明确几个概念:java
JointPoint正则表达式
系统在运行以前,AOP的功能模块须要织入到OOP的功能模块中。要进行这种织入过程,须要知道在系统的哪些功能点上进行织入操做,这些将要在其上进行织入操做的系统功能点就称为JointPoint。如某方法调用的时候或者处理异常的时候,在Spring AOP中,一个链接点老是表示一个方法的执行。常见的几种类型的JoinPoint:spring
Ø 方法调用:当某个方法被调用的时候所处的程序执行点;缓存
Ø 方法执行:该类型表示的是某个方法内部执行开始时的点,应该与方法调用相区分;性能优化
Ø 构造方法调用:程序执行过程当中对某个对象调用其构造方法进行初始化时的点;app
Ø 构造方法执行:它与构造方法调用关系如同方法调用与方法执行间的关系;框架
Ø 字段设置:对象的某个属性经过setter方法被设置或直接被设置的执行点;ide
Ø 字段获取:某个对象相应属性被访问的执行点;模块化
Ø 异常处理执行:某些类型异常抛出后,对应的异常处理逻辑执行点;
Ø 类初始化:类中某些静态类型或静态块的初始化时的执行点。
Pointcut
Pointcut表明的是JoinPoint的表述方式。在将横切逻辑织入当前系统的过程当中,虽然知道须要在哪些功能点上织入AOP的功能模块,但须要一种表达方法。Pointcut和一个切入点表达式关联,并在知足这个切入点的Joinpoint上运行。目前一般使用的Pointcut方式有如下几种:
Ø 直接指定Joinpoint所在的方法名称;
Ø 正则表达式,Spring的AOP支持该种方式;
Ø 使用特定的Pointcut表述语言,Spring 2.0后支持该方式。
Advice
Advice是单一横切关注点逻辑的载体,它表明将会织入到JoinPoint的横切逻辑。在切面的某个特定的链接点上执行的逻辑。根据它在Joinpoint位置执行时机的差别或完成功能的不一样,可分为如下几种形式:
Ø Before Advice:在Joinpoint指定位置以前执行的Advice类型,能够采用它来作一些系统的初始化工做,如设置系统初始值,获取必要系统资源等。
Ø After Advice:在相应链接点以后执行的Advice类型,它还能够细分为如下三种:
² After Returning Advice:只有当前Joinpoint处执行流程正常完成后,它才会执行;
² After throwing Advice:在当前Joinpoint执行过程当中抛出异常的状况下会执行;
² After Advice:该类型的Advice无论JoinPoint处执行流程是正常仍是抛出异常都会执行。
Ø Around Advice:对附加其上的Joinpoint进行包裹,能够在joinpoint以前和以后都指定相应的逻辑,甚至中断或忽略joinpoint处原来程序流程的执行。
Aspect
它是对系统中横切关注点逻辑进行模块化封装的AOP概念实体,它能够包含多个Pointcut以及相关的Advice定义。
织入器
通过织入过程后,以Aspect模块化的横切关注点才会集成到oop的现存系统中,而完成织入过程实体称为织入器。Spring中使用一组类来完成最终的织入操做,ProxyFactory类是Spring AOP最通用的织入器。
目标对象
符合Pointcut所指定的条件,将在织入过程当中被织入横切逻辑的对象,称之为目标对象。
单看上述的概念,可能会以为有点眼花缭乱,其实经过一个简单的AOP的实例便可以帮助咱们很快的了解其内部的机制。其实对于方法拦截有不一样的实现方式,经常使用的即有直接采用Spring提供的各类Advice进行拦截,另外一种则是采用MethodInterceptor方式进行拦截。
Spring提供的Advice拦截方式
定义一个逻辑接口IBusinessLogic:
package com.wow.asc.aop; public interface IBusinessLogic { public void foo(); public void bar() throws BusinessLogicException; public long time(); }
其中有一个BusinessLogicException异常,它用于后面对于ThrowsAdvice进行检验的实例,在此定义为:
package com.wow.asc.aop; public class BusinessLogicException extends Exception { }
对于该业务逻辑的实现BusinessLogic,以下所示:
package com.wow.asc.aop; public class BusinessLogic implements IBusinessLogic { @Override public void foo() { System.out.println("Inside BusinessLogic.foo()"); } @Override public void bar() throws BusinessLogicException { System.out.println("Inside BusinessLogic.bar()"); throw new BusinessLogicException(); } /* * 返回该方法执行的时间 */ @Override public long time() { System.out.println("Inside BusinessLogic.time()"); long startTime = System.currentTimeMillis(); for(int i = 0; i < 100000000; i++); long endTime = System.currentTimeMillis(); return (endTime - startTime); } }
在完成上述业务逻辑编码后,接下来将进行更多的横切插入点的设计,如在方法执行前或返回时、抛出异常时进行各类处理。对于Advice的写法以下所示:
package com.wow.asc.aop; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; /* * 表示一个在方法执行前进行拦截的一个Advice */ public class TracingBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("execute before (by " + method.getDeclaringClass().getName() + "." + method.getName() + ")"); } } package com.wow.asc.aop; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; /* * 表示一个在方法返回时进行拦截的Advice */ public class TracingAfterAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println(method.getDeclaringClass().getName() + "." + method.getName() + "spend time: " + returnValue); System.out.println("execute after (by " + method.getDeclaringClass().getName() + "." + method.getName() + ")"); } } package com.wow.asc.aop; import java.lang.reflect.Method; import org.springframework.aop.ThrowsAdvice; /* * 表示一个异常抛出时进行拦截的Advice */ public class TracingThrowsAdvice implements ThrowsAdvice { public void afterThrowing(Method method, Object[] args, Object target, Throwable subclass) { System.out.println( "Logging that a " + subclass + "Exception was thrown."); } }
在设计完上述的代码及逻辑后,便可以经过applicationContext.xml将上述类进行组合,在装配过程当中便可明确哪一个类的哪些方法须要被拦截,及拦截前、后会作哪些事情。具体的配置示例:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/context http://localhost:8080/schema/www.springframework.org/schema/context/spring-context-2.5.xsd"> <bean id="businessLogic" class="com.wow.asc.aop.BusinessLogic" /> <bean id="businessLogicBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.wow.asc.aop.IBusinessLogic</value> </property> <property name="target"> <ref local="businessLogic"/> </property> <property name="interceptorNames"> <list> <value>theTracingBeforeAdvisor</value> <value>theTracingAfterAdvisor</value> <value>theTracingThrowsAdvisor</value> </list> </property> </bean> <bean id="theTracingBeforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="theTracingBeforeAdvice"/> </property> <property name="pattern"> <value>.*</value> </property> </bean> <bean id="theTracingAfterAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="theTracingAfterAdvice"/> </property> <property name="pattern"> <value>.*time.*</value> </property> </bean> <bean id="theTracingThrowsAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="theTracingThrowsAdvice"/> </property> <property name="pattern"> <value>.*bar.*</value> </property> </bean> <bean id="theTracingBeforeAdvice" class="com.wow.asc.aop.TracingBeforeAdvice"/> <bean id="theTracingAfterAdvice" class="com.wow.asc.aop.TracingAfterAdvice"/> <bean id="theTracingThrowsAdvice" class="com.wow.asc.aop.TracingThrowsAdvice"/> </beans>
经过上述的配置,咱们能够看出咱们将IBusinessLogic作为代理接口,同时它的真正的目标类是BusinesssLogic。同时会对全部进入方法以前采用TracingBeforeAdvice进行拦截,进行方法前的预处理;对time方法采用TracingAfterAdvice进行拦截,进行方法返回后的处理;对于bar则采用TracingThrowsAdvice进行拦截,当方法返回BusinessLogicException时进行相应的处理。
在配置完上述类的依赖关系及须要拦截的方法后,便可以编写客户端程序来调用,查看它的运行机制。客户端调用代码:
package com.wow.asc.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.wow.asc.aop.BusinessLogicException; import com.wow.asc.aop.IBusinessLogic; public class AOPTest { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); IBusinessLogic ibl = (IBusinessLogic)ac.getBean("businessLogicBean"); ibl.foo(); try { ibl.bar(); } catch (BusinessLogicException e) { System.out.println("Caught BusinessLogicException"); } ibl.time(); } }
经过运行结果来详细的了解下,看是否真正的如上所述,会在方法前、后及异常抛出时可以拦截并进行相应处理。结果以下:
一、execute before (by com.wow.asc.aop.IBusinessLogic.foo) 二、Inside BusinessLogic.foo() 三、execute before (by com.wow.asc.aop.IBusinessLogic.bar) 四、Inside BusinessLogic.bar() 五、Logging that a com.wow.asc.aop.BusinessLogicExceptionException was thrown. 六、Caught BusinessLogicException 七、execute before (by com.wow.asc.aop.IBusinessLogic.time) 八、Inside BusinessLogic.time() 九、com.wow.asc.aop.IBusinessLogic.timespend time: 46 十、execute after (by com.wow.asc.aop.IBusinessLogic.time)
其实经过一、三、7行能够很是清晰的了解到,每一个方法在执行前都被TracingBeforeAdvice拦截到,并执行了预处理。五、6行表示当调用的是bar方法时,会被TracingThrowsAdvice拦截,当有异常抛出时,会执行相应的处理;八、九、10行则表示当调用的是time方法,返回时会被TracingAfterAdvice拦截,对其返回值进行处理。
MethodInterceptor拦截方式
采用该种方式进行拦截,须要实现一个继承自MethodInterceptor的类,并将该类注册至spring Context中,具体以下:
package com.wow.asc.aop; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) { Object result = null; StringBuffer info = new StringBuffer(); info.append("intercept the method: "); info.append(invocation.getMethod().getDeclaringClass(). getName()); info.append("."); info.append(invocation.getMethod().getName()); System.out.println("start " + info.toString()); try { result = invocation.proceed(); } catch (Throwable e) { e.printStackTrace(); } finally { System.out.println("end " + info.toString()); } return result; } }
对于类的装配,其实和上面的很是相似,示例:
<bean id="testBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.wow.asc.aop.IBusinessLogic</value> </property> <property name="target"> <ref local="businessLogic"/> </property> <property name="interceptorNames"> <list> <value>myInterceptor</value> </list> </property> </bean> <bean id="myInterceptor" class="com.wow.asc.aop.MyInterceptor"/>
再经过客户端进行调用,可获得运行结果,从结果来分析能够看出它在方法执行的前、后均添加了相应的日志。
start intercept the method: com.wow.asc.aop.IBusinessLogic.foo Inside BusinessLogic.foo() end intercept the method: com.wow.asc.aop.IBusinessLogic.foo start intercept the method: com.wow.asc.aop.IBusinessLogic.time Inside BusinessLogic.time() end intercept the method: com.wow.asc.aop.IBusinessLogic.time
至此,采用两种不一样方式实现的AOP就结束了,但愿你们可以体会到其中的奥妙。
参考AOP资料:
http://www.zabada.com/technology/aop-example.html
http://onjava.com/pub/a/onjava/2004/07/14/springaop.html?page=1