spring aop的另外一种实现

来源:互联网 发布:travel域名 编辑:程序博客网 时间:2024/05/18 03:52

在了解了基本的理论之后,我们通过一个实例来强化对AOP的认识。我们希望通过一个自定义的Aspect,来为业务方法添加上基本的debug信息和调用时间的记录。其主要思路是:通过定义业务接口,在业务接口的方法上(Joinpoint)添加该Aspect。在实现Aspect上,我们使用最常用的Interception。有了这个思路之后,我们先进行准备工作,先定义业务接口,并实现业务方法:

public interface SearchBookBean {
 public void search();
}


public class SearchBookBeanImpl implements SearchBookBean{

 public void search()
 {
  System.out.println("Business iethod s Called");
 }
}
  
然后是最重要的事情,定义一个拦截器,拦截器需要符合AOP Alliance的规范:

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class MyInterceptor implements MethodInterceptor
{
  private final Log logger = LogFactory.getLog(getClass());

  public Object invoke(MethodInvocation methodInvocation) throws Throwable
  {
 logger.info("Beginning method: " + methodInvocation.getMethod().getDeclaringClass() + "::" + methodInvocation.getMethod().getName());
 long startTime = System.currentTimeMillis();
 try
 {
   Object retVal = methodInvocation.proceed();
   return retVal;
 }
 finally
 {
   logger.info("Ending method: "  +
    methodInvocation.getMethod().getDeclaringClass() + "::" +
  methodInvocation.getMethod().getName());
   logger.info("Method invocation time: " + (System.currentTimeMillis() - startTime) + " msecs.");
 }
  }

}
  
主要的工作都已经做完了,上面的代码中,最重要的两句代码已经加粗表示了,它表示当前的拦截器处理完成后,将控制权交给下一个拦截器。从拦截器的结构来看,我们前面所说拦截器能够实现所有的Aspect功能的话是对的。

然后,我们需要通过配置文件把这些bean组合起来:

<beans>

 <bean id="SearchBookBeanTarget" class="SearchBookBeanImpl" />
 
 <bean id="myInterceptor" class="MyInterceptor" />
 
 <bean id="SearchBookBean" class="org.springframework.aop.framework.ProxyFactoryBean">
   <property name="proxyInterfaces"><value>SearchBookBean</value></property>
    <property name="interceptorNames">
      <list>
        <value>myInterceptor</value>
        <value>SearchBookBeanTarget</value>
      </list>
    </property>
  </bean>
 
</beans>
  
首先,定义一个具体的目标bean-SearchBookBeanTarget,然后,声明一个拦截器-myInterceptor。接下来,就需要将拦截器和目标bean关联起来,这里使用到了ProxyFactoryBean,我们先不管它的具体实现,只要知道它起到一个连接器的作用,所以,它的参数中应该包括到拦截器和目标bean的引用。

我们先不管配置文件的具体含义。先来看看最终的调用代码:

 public static void main(String[] args) {
  InputStream is = Spike.class.getClassLoader().getResourceAsStream("aop.xml");
  SearchBookBean sbb=(SearchBookBean)new XmlBeanFactory(is).getBean("SearchBookBean");
  sbb.search();
 }

  
然后是输出结果:

Debug interceptor: count=1 invocation=[Invocation:
method=[public abstract void SearchBookBean.search()]
args=null] target is of class SearchBookBeanImpl]

Search Service is Called

Debug interceptor: next returned

  
额外的功能已经添加到接口的方法上了,而接口和实现本身都没有任何的相关代码。看起来十分神奇,那么我们现在就分析一下它的原理。

LOG
通过分析spring的log,我们可以了解上面的例子到底做了些什么:

14:04:30,338 DEBUG ProxyFactoryBean:265 - Added new aspect interface: interface SearchBookBean

14:04:30,338 DEBUG ProxyFactoryBean:126 - Set BeanFactory. Will configure interceptor beans...

14:04:30,338 DEBUG ProxyFactoryBean:160 - Configuring interceptor 'myInterceptor'
14:04:30,338  INFO XmlBeanFactory:187 - Creating shared instance of singleton bean 'myInterceptor'
14:04:30,348 DEBUG ProxyFactoryBean:255 - Adding advisor or TargetSource [MyInterceptor@2f1921]
with name [myInterceptor]
14:04:30,368 DEBUG ProxyFactoryBean:261 - Adding advisor with name [myInterceptor]

14:04:30,368 DEBUG ProxyFactoryBean:160 - Configuring interceptor 'SearchBookBeanTarget'
14:04:30,368  INFO XmlBeanFactory:187 - Creating shared instance of singleton bean 'SearchBookBeanTarget'
14:04:30,378 DEBUG ProxyFactoryBean:255 - Adding advisor or TargetSource [SearchBookBeanImpl@10bc49d]
with name [SearchBookBeanTarget]
14:04:30,378 DEBUG ProxyFactoryBean:271 - Adding TargetSource [Singleton target source (not dynamic):
target=[SearchBookBeanImpl@10bc49d]] with name [SearchBookBeanTarget]

14:04:30,378  INFO ProxyFactoryBean:128 - ProxyFactoryBean config:

14:04:30,388 DEBUG JdkDynamicAopProxy:177 - Creating JDK dynamic proxy

  
以上的log做了大量的简化,但已经足够说明问题了。最核心的对象是ProxyFactoryBean,也就是配置文件中定义的,以及我们最终使用的那个对象。它的本质是一个基于类的适配器。它将AOP的具体实现转换为一个标准的,符合spring配置规范的bean。这样,除了使用AOP的功能之外,还可以使用配置来管理该对象。所以,该对象是我们研究的重点。

在范例的配置文件中,ProxyFactoryBean有两组的参数,一个是proxyInterfaces,它代表需要被代理的接口,也就是我们希望在哪个接口上添加功能。另一个是interceptorNames。它是一个列表,代表有几种的意思,一是需要添加到目标接口的Advice,如debugInterceptor,二是真正的实现对象,如SearchBookBeanTarget,称为TargetSource。前者可以有多个的,但后者只能有一个。

所以上述log文件除最后一行外,都是在对ProxyFactoryBean的各个属性进行配置。其中,最核心的处理是ProxyFactoryBean实现BeanFactoryAware接口的setBeanFactory回调方法。在这个方法中,ProxyFactoryBean将文本的配置信息转换为真正的对象,并进行配置。ProxyFactoryBean同时还是一个FactoryBean。因此,它可以不返回自身,而通过getObject方法返回一个自身的代理。这样,对接口本身的调用都转换为对代理对象的调用。而代理对象会在调用真正的方法的前后调用一些额外的方法。

在spring中,代理对象的实现采用了两种形式,一种是基于JDK1.3提供的proxy机制,一种是基于开源软件CGLIB提供的代理机制。

原创粉丝点击