`
maosheng
  • 浏览: 550709 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

基于Java Dynamic Proxy(面向Interface)的Spring AOP实现原理

阅读更多
Dynamic Proxy是面向接口的动态代理实现,其代理对象必须是某个接口的实现。Dynamic Proxy通过在运行期构建一个此接口的动态实现类完成对目标对象的代理(相当于在运行期动态构造一个UserDAOProxy,完成对UserDAOImp的代理任务)。而如果目标代理对象并未实现任何接口,那么Dynamic Proxy就失去了创建动态代理类的基础依据。

我们先来看传统方式下一个Proxy的实现实例。
假设我们有一个UserDAO接口及其实现类UserDAOImp:
UserDAO.java:
public interface UserDAO {
    public void saveUser(User user);
}
UserDAOImp.java:
public class UserDAOImp implements UserDAO{
    public void saveUser(User user) {
    ……
    }
}

UserDAOImp.saveUser方法中实现了针对User对象的数据库持久逻辑。
如果我们希望在UserDAOImp.saveUser方法执行前后追加一些处理过程,如启动/
提交事务,而不影响外部代码的调用逻辑,那么,增加一个Proxy类是个不错的选择:
UserDAOProxy.java
public class UserDAOProxy implements UserDAO {
  private UserDAO userDAO;
  public UserDAOProxy(UserDAO userDAO) {
    this.userDAO = userDAO;
  }
  public void saveUser(User user) {
    UserTransaction tx = null;
    try {
         tx = (UserTransaction) (
            new InitialContext().lookup("java/tx")
         );
         userDAO.saveUser(user);
         tx.commit();
     } catch (Exception ex) {
     if (null!=tx){
       try {
          tx.rollback();
       }catch(Exception e) {
       }
     }
   }
}
}

UserDAOProxy同样是UserDAO接口的实现,对于调用者而言,saveUser方法的使用完全相同,不同的是内部实现机制已经发生了一些变化――我们在UserDAOProxy中为UserDAO.saveUser方法套上了一个JTA事务管理的外壳。
上面是静态Proxy模式的一个典型实现。

现在假设系统中有20个类似的接口,针对每个接口实现一个Proxy,实在是个繁琐无味的苦力工程。
Dynamic Proxy的出现,为这个问题提供了一个更加聪明的解决方案。
我们来看看怎样通过Dynamic Proxy解决上面的问题:

public class TxHandler implements InvocationHandler {
    private Object originalObject;
    public Object bind(Object obj) {
        this.originalObject = obj;
        return Proxy.newProxyInstance(
            obj.getClass().getClassLoader(),
            obj.getClass().getInterfaces(),
            this
         );
    }
    public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
       Object result = null;
       if (!method.getName().startsWith("save")) {
        UserTransaction tx = null;
        try {
                tx = (UserTransaction) (
                     new InitialContext().lookup("java/tx")
                    );
                result = method.invoke(originalObject, args);
            
                tx.commit();
            } catch (Exception ex) {
          if (null != tx) {
              try {
                  tx.rollback();
              } catch (Exception e) {
              }
           }
         }
      } else {
           result = method.invoke(originalObject, args);
      }
      return result;
   }
}

首先注意到,上面这段代码中,并没有出现与具体应用层相关的接口或者类引用。也就是说,这个代理类适用于所有接口的实现。
其中的关键在两个部分:
1.
  return Proxy.newProxyInstance(
     obj.getClass().getClassLoader(),
     obj.getClass().getInterfaces(),
     this);

java.lang.reflect.Proxy.newProxyInstance方法根据传入的接口类型(obj.getClass().getInterfaces())动态构造一个代理类实例返回,这个代理类是JVM在内存中动态构造的动态类,它实现了传入的接口列表中所包含的所有接口。
这里也可以看出,Dynamic Proxy要求所代理的类必须是某个接口的实现(obj.getClass().getInterfaces()不可为空),否则无法为其构造响应的动态类。这也就是为什么Spring对接口实现类通过Dynamic Proxy实现AOP。

2.
   public Object invoke(Object proxy, Method method, Object[] args)
   throws Throwable {
      ……

       result = method.invoke(originalObject, args);
      ……
       return result;
   }

InvocationHandler.invoke方法将在被代理类的方法被调用之前触发。通过这个方法中,我们可以在被代理类方法调用的前后进行一些处理,如代码中所示,InvocationHandler.invoke方法的参数中传递了当前被调用的方法(Method),以及被
调用方法的参数。同时,我们可以通过Method.invoke方法调用被代理类的原始方法实现。这样,我们就可以在被代理类的方法调用前后大做文章。
在示例代码中,我们为所有名称以“save”开头的方法追加了JTA事务管理。

谈到这里,可以回忆一下Spring事务配置中的内容:
<property name="transactionAttributes">
   <props>
      <prop key="save*">PROPAGATION_REQUIRED</prop>
      <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
   </props>
</property>

想必大家已经猜测到Spring事务管理机制的实现原理。
是的,只需通过一个Dynamic Proxy对所有需要事务管理的Bean进行加载,并根据配置,在invoke方法中对当前调用的方法名进行判定,并为其加上合适的事务管理代码,那么就实现了Spring式的事务管理。当然,Spring中的AOP实现更为复杂和灵活,不过基本原理一致。

Spring中Dynamic Proxy AOP实现类为:
org.springframework.aop.framework.JdkDynamicAopProxy
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics