Spring Aop分析

前言


上文讲述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框架已经描述完毕了。

猜你喜欢

转载自www.cnblogs.com/xmzJava/p/9139741.html