前言
上文讲述ioc框架的实现,本文开始讲述aop。在spring中aop也有3种配置方式,注解形式的我们先不讨论。我们先看看xml形式的配置方式。
<aop:config> <aop:aspect ref="testInterceptor"> <aop:pointcut expression="execution(public * com.freud.test..*.*(..))" id="testPointCut" /> <aop:after method="after" pointcut-ref="testPointCut" /> <aop:before method="before" pointcut-ref="testPointCut" /> </aop:aspect> </aop:config> <bean id="testInterceptor" class="com.freud.TestInterceptor" />
public class TestInterceptor { public void after() { System.out.println("After"); } public void before() { System.out.println("before"); } }
上述配置是纯xml形式的配置,这样的配置结构清晰明了,就是看起来有点臃肿。
spring中提供了四种Advice用来支持对方法调用时施加的不同行为,通过这四种Advice可以帮助我们减少xml的配置
BeforeAdvice:具体接口:MethodBeforeAdvice 在目标方法调用之前调用的Advice
AfterAdvice:具体接口:AfterReturningAdvice 在目标方法调用并返回之后调用的Advice
AroundAdvice:具休接口:MethodInterceptor 在目标方法的整个执行前后有效,并且有能力控制目标方法的执行
ThrowsAdvice:具体接口:ThrowsAdvice 在目标方法抛出异常时调用的Advice
举个例子
<bean id="testInterceptor" class="com.freud.Interceptor.TestInterceptor"></bean> <aop:config > <!--切入点--> <aop:pointcut id="testPointCut" expression="execution(public * com.freud.test.*.*(..))"/> <!-- 在该切入点使用自定义拦截器 --> <aop:advisor pointcut-ref="testPointCut" advice-ref="testInterceptor"/> </aop:config>
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class TestInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { //Do something before Object ret = invocation.proceed(); //Do something After return ret; } }
这里的TestInterceptor我们称之为Advice,就是往目标对象(Target)需要增强的内容。
这里MethodInvocation称之为切点(point cut),提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配 程序中的方法(join point), 给满足规则的 程序中的方法 添加 Advice.
那么现在我们可以吧aop转变为两个问题
1.如何通过joinpoint匹配到特定的joinpoint
2.如何编写advice代码织入增强
接下来我们看下我们该如何实现上述问题
1.Aop简单实现
前面已经说到spring中提供了四种Advice,我们这里可以使用MethodInterceptor帮助我们实现aop
invoke的参数MethodInvocation参数暴露了被调用的方法; 目标连接点;AOP代理以及传递给方法的参数。
现在我们创建以下几个对象
1.TargetSource 被代理的对象 2.MethodInterceptor实现类 3.AdvisedSupport 封装TargetSource和MethodInterceptor
4.ReflectiveMethodInvocation 继承MethodInvocation
那现在一次调用就是这样
现在还有个问题就是要获取被代理的方法和参数。通过反射的方式可以取到,但是这样写起来不够灵活。这里采用动态代理
那么现在,实现aop的操作就很清晰了
2.Aop与Spring的集成
第一段我们将aop的织入过程进行分析,其中有个很重要的东西被我们忽略了。那就是Pointcut表达式。Spring借鉴了AspectJ表达式,封装成AspectJExpressionPointcut。这里不做过多介绍
本文最开始的时候介绍了xml的配置方式实现aop,第一段我们已代码的形式,实现了aop的织入。接下来 我们看下aop与spring是如何结合的。
spring提供了BeanPostProcessor对象,只要你的Bean实现了BeanPostProcessor
接口,那么Spring在初始化时,会优先找到它们,并且在Bean的初始化过程中,调用这个接口,从而实现对BeanFactory核心无侵入的扩展。
优先找到BeanPostProcessor代码如下
在实例化的时候,调用beanPostProcessor的方法增强bean。
我们举个例子
配置文件:
<bean id="autoProxyCreator" class="us.codecraft.tinyioc.aop.AspectJAwareAdvisorAutoProxyCreator"></bean> <bean id="timeInterceptor" class="us.codecraft.tinyioc.aop.TimerInterceptor"></bean> <bean id="aspectjAspect" class="us.codecraft.tinyioc.aop.AspectJExpressionPointcutAdvisor"> <property name="advice" ref="timeInterceptor"></property> <property name="expression" value="execution(* us.codecraft.tinyioc.*.*(..))"></property> </bean>
这里可能会有人奇怪这个配置为什么和最开头的配置不一样 ,最开头的配置都是以 <aop:config> 开头,而上面的配置文件只是几个bean而已。
其实spring在解析<aop:config>时会默认的提供包含 AspectJAwareAdvisorAutoProxyCreator 的 BeanDefinition,有兴趣的可以看看(org.springframework.aop.config.AopNamespaceUtils#registerAspectJAutoProxyCreatorIfNecessary)
在解析<aop:before>和<aop:after>时,spring会向候选Advisor链的开头添加一个org.springframework.aop.support.DefaultPointcutAdvisor。所以上述两种配置理论上是一样的。只是现在我们还没有spring解析的那一套代码,所以就直接已bean的形式写成配置文件。
测试代码
初始化的时候优先找到BeanPostProcessor上面已经提过了。getBean的前半段代码也已经提过,我们主要看下beanPostProcessor是如何增强的。
到这里,一个aop框架已经描述完毕了。