《Spring》(十四) ---- Spring AOP 的织入

在Spring AOP中,使用类org.springframework.aop.framework.ProxyFactory作为织入器。Spring AOP是基于代理模式的AOP实现,织入过程完成后,会返回织入了横切逻辑的目标对象的代理对象。

ProxyFactory weaver = new ProxyFactory(yourTargetObject);
// or
// ProxyFactory weaver = new ProxyFactory();
// weaver.setTarget(yourTargetObject);
Advisor advisor = ...;
weaver.addAdvisor(advisor);
Object proxyObject = weaver.getProxy();
// now you can use proxyObject to do something

使用ProxyFactory只需要指定如下两个最基本的东西:

  1,要对其进行织入的目标对象。可以通过构造方法直接传入,也可以构造完后,通过setter方法设置。

  2,将要应用到目标对象的Advisor。

Spring AOP在使用代理模式实现AOP的过程中采用了动态代理和CGLIB两种机制,分别对实现了某些接口的目标类和没有实现任何接口的目标类进行代理,所以,在使用ProxyFactory对目标类进行代理的时候,会通过ProxyFactory的某些行为控制属性对这两种情况进行区分。如果满足以下列出的三种情况中的任何一种,ProxyFactory将对目标类进行基于类的代理。

  1,如果目标类没有实现任何接口,不管proxyTargetClass的值是什么,ProxyFactory会采用基于类的代理。

  2,如果ProxyFactory的proxyTargetClass属性值被设置为true,ProxyFactory会采用基于类的代理。

  3,如果ProxyFactory的optimize属性设置为true,ProxyFactory会采用基于类的代理。

如果ProxyFactory对目标类是基于类的代理,即使目标类实现了接口,代理对象就需要转化为具体目标类而不是接口,然后再调用。

 Spring的Introduction支持只能通过接口定义为当前对象添加新的行为,所以,在需要织入Introduction时,需要指定新织入的接口类型。

  ProxyFactory的本质

ProxyFactory的根是AopProxy: 

public interface AopProxy {
   Object getProxy();
   Object getProxy(ClassLoader classLoader);   
}

目前,Spring AOP框架内提供了针对JDK的动态代理和CGLIB两种机制的AopProxy实现。

不同AopProxy实现的实例化过程采用工厂模式进行封装,即通过AopProxyFactory进行。AopProxyFactory接口定义:

public interface AopProxyFactory {
   AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
}

AopProxyFactory的实现类就一个DefaultAopProxyFactory.

AopProxyFactory需要根据AdvisedSupport实例信息,来构建相应的AopProxy. 简单来说,AdvisedSupport其实就是一个生成代理对象所需要的信息载体。

AdvisedSupport记载两类信息:ProxyConfig(记载生成代理对象的控制信息)和Advised(记载生成代理对象的相关目标类/Advice/Advisor等)

ProxyConfig有五个控制属性,最常用的是:

  1. proxyTargetClass(是否使用CGLIB)
  2. optimize(是否采取优化措施)

AdvisedSupport继承了ProxyConfig和Advised,就可以设置代理对象生成的一些控制属性和代理对象相关的目标类等必要信息,这样,具体的AopProxy实现在生成代理对象时,可以从AdvisedSupport中取得所有必要信息。

  

ProxyFactory集AopProxy和AdvisedSupport于一身,所以可以通过ProxyFactory设置生成代理对象的相关信息,也可以通过ProxyFactory取得最终生成的代理对象。

ProxyFactory的兄弟之一 ---- ProxyFactoryBean

  

 

<bean id="pointcut" class="org.springframwork.aop.support.NameMatchMethodPointcut">
    <property name="mappedName" value="execute" />
</bean>

<bean id="performanceInterceptor" class="...advice.PerformanceMethodInterceptor">
</bean>

<bean id="performanceAdvisor" class="org.springframwork.aop.support.DefaultPointcutAdvisor">
    <property name="pointcut">
        <ref bean="pointcut" />
    </property>
    <property name="advice">
        <ref bean="performanceInterceptor" />
    </property>
</bean>

<bean id="task" class="...MockTask">
</bean>

<bean id="taskProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref bean="task"/>
    </property>
    <property name="proxyInterfaces">
        <list>
            <value>...ITask</value>
        </list>
    </property>
    <property name="interceptorNames">
        <list>
            <value>performanceAdvisor</value>
        </list>
    </property>
</bean>

如果确认目标对象所实现的接口就是要代理的接口,可以完全省略interfaces或者proxyInterfaces明确指定代理接口的配置,改为如下配置:

<bean id="taskProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref bean="task"/>
    </property>
    <property name="interceptorNames">
        <list>
            <value>performanceAdvisor</value>
        </list>
    </property>
</bean>

如果没有指定要代理的接口类型,且目标对象也没有实现任何借口,那么,ProxyFactoryBean会采用基于类的代理方式为目标类生成代理对象。即使目标对象实现了某些接口,也可以通过设置proxyTargetClass为true,强制采用基于类的代理方式来生成代理对象。

<bean id="taskProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref bean="task"/>
    </property>
    <property name="proxyTargetClass">
        <value>true</value>
    </property>
    <property name="interceptorNames">
        <list>
            <value>performanceAdvisor</value>
        </list>
    </property>
</bean>

不过这样的话,就不能将代理对象强制转型为ITask了,而应该强制转型为目标类的具体类型,即MockTask。如下所示:

ApplicationContext ctx = ...;
// wrong
// ITask task = (ITask)ctx.getBean("taskProxy");

// right
MockTask task = (MockTask)ctx.getBean("taskProxy");
task.execute(null);
...

提示:

有时,我们的应用可能需要依赖于第三方库,这些库中可能有些对象出于简单实用的目标,就没有进行面向接口的编程,自然也没有实现任何接口。而且,我们自己设计和实现的类,可能出于某种目的,也是没有实现接口的必要,这时,就需要通过将proxyTargetClasss设置为true来解决代理问题了。

加快织入的自动化进程

Spring AOP给出了自动代理机制,用以帮助我们解决使用ProxyFactoryBean配置工作量比较大的问题。

  • 自动代理机制原理

提供一个BeanPostProcessor,通过这个BeanPostProcessor,可以遍历容器中所有bean的基础上,对遍历到的bean进行一些操作。当对象实例化的时候,为其生成代理对象并返回,而不是返回实例化后的目标对象本身,这样就达到代理对象自动生成的目的。伪代码如下: 

  • 可用的AutoProxyCreator
  1. BeanNameAutoProxyCreator

使用BeanNameAutoProxyCreator,可以通过指定一组容器内的目标对象对应的beanName,将指定的一组拦截器应用到这些目标对象上。

  1. DefaultAdvisorAutoProxyCreator

  使用DefaultAdvisorAutoProxyCreator,只需要在ApplicationContext的配置文件中注册一下DefaultAdvisorAutoProxyCreator的bean定义即可,它就会自动搜寻容器内的所有Advisor,然后根据各个Advisor所提供的拦截信息,为符合条件的容器中的目标对象生成相应的代理对象。注意,DefaultAdvisorAutoProxyCreator只对Advisor有效,因为只有Advisor才既有Pointcut信息以捕捉符合条件的目标对象。

  使用DefaultAdvisorAutoProxyCreator对容器内所有bean定义对应的对象进行自动代理后,从容器中取得的对象实例,就都是代理后已经包含了织入的横切逻辑的代理对象了,除非该对象不符合Pointcut规定的拦截条件。

原文地址:https://www.cnblogs.com/IvySue/p/6552393.html