论坛首页 Java企业应用论坛

我的酒窝

浏览 43496 次
锁定老帖子 主题:我的酒窝
该帖已经被评为精华帖
作者 正文
   发表时间:2006-12-11  
springside与合作项目是合作关系,而不是子项目关系阿,所以springside叫啥名字其实没什么所谓呀,就像springside放在满江红,但界面是绿色的一样:)

不过ajoo不是有俩CodeHaus项目么,直接作为子项目得了。
0 请登录后投票
   发表时间:2006-12-11  
支持一下
0 请登录后投票
   发表时间:2006-12-11  
支持!

可否共享代码,大家学习一下,多谢
0 请登录后投票
   发表时间:2006-12-11  
就是重构起来麻烦点
0 请登录后投票
   发表时间:2006-12-11  
nihongye 写道
就是重构起来麻烦点

这的确是一个问题。而且似乎没有什么好的解决办法做到重构安全。我能够看到的一个加强的地方是,验证new Object中每个方法是不是实现了要stub的接口或者具体类的方法。这样在改名了之后,可以fail fast,不会出莫名其妙的问题不知所以。
另外,不妨把酒窝当一个stub框架来做。
0 请登录后投票
   发表时间:2006-12-11  
nihongye 写道
就是重构起来麻烦点

对亚。我也没有想到好的解决方法。

幸好,如果仅仅是用它来stub Connection, SqlMapSession这些第三方接口的话,就没有重构的问题了。

自己的接口当然还是直接"implements"的好。
0 请登录后投票
   发表时间:2006-12-11  
liuxg 写道
支持!

可否共享代码,大家学习一下,多谢

正在申请codehaus项目中。然后就可以下载了。
0 请登录后投票
   发表时间:2006-12-12  
package org.codehaus.dimple;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * This class is used to create implementation of interface(s) dynamically.
 * <p>
 * This class is ideal for creating stub or interceptor for interfaces where only a few methods are of interest while
 * most other methods are either ignored or delegated.
 * </p>
 * <p>
 * For example:
 * <pre> 
 * Connection realConn = ...;
 * Connection nonCloseableConnection = (Connection)Implementor.proxy(Connection.class, new Object(){
 *   public void close() {
 *     //we intercept close() call and do nothing.
 *   }
 * }, realConn);
 * nonCloseableConnection.close();// no-op. realConn is not closed.
 * </pre>
 * </p>
 * @author Ben Yu
 * Dec 9, 2006 11:27:44 PM
 */
public class Implementor implements Serializable {
  /**
   * Equivalent as new Implementor(with.getClass()).implementWithDefaultHandler(itf, with, defaultHandler)
   * <p>
   * This method is a convenience shortcut.
   * As the constructor of Implementor may be expensive, it is recommended to create an Implementor object once
   * and then use it repeatedly.
   */
  public static Object proxyWithDefaultHandler(Class asType, Object with, InvocationHandler defaultHandler) {
    return new Implementor(with.getClass()).implementWithDefaultHandler(asType, with, defaultHandler);
  }
  /**
   * Equivalent as new Implementor(with.getClass()).implement(asType, with, defaultDelegate)
   * <p>
   * This method is a convenience shortcut.
   * As the constructor of Implementor may be expensive, it is recommended to create an Implementor object once
   * and then use it repeatedly.
   */
  public static Object proxy(Class asType, Object with, Object defaultDelegate){
    if(!asType.isInstance(defaultDelegate)){
      throw new IllegalArgumentException("default delegate of type "+asType.getName() + " expected, "
          + ((defaultDelegate==null)?null:defaultDelegate.getClass().getName())+" encountered");
    }
    return new Implementor(with.getClass()).implement(asType, with, defaultDelegate);
  }
  
  /**
   * Equivalent as new Implementor(with.getClass()).implement(asType)
   * <p>
   * This method is a convenience shortcut.
   * As the constructor of Implementor may be expensive, it is recommended to create an Implementor object once
   * and then use it repeatedly. 
   */
  public static Object proxy(Class asType, Object with) {
    return new Implementor(with.getClass()).implement(asType, with);
  }

  /**
   * create a dynamic proxy that implements <i>asType</i> by calling <i>with</i>
   * if a method is implemented by the impl class.
   * Otherwise delegate call to defaultDelegate 
   * or throw UnsupportedOperationException if defaultDelegate is null.
   * @param asType the interface to implement or super class to override (cglib is required in this case). 
   * @param with the instance of the impl class.
   * @param defaultDelegate the default delegate.
   * @return the dynamic proxy that implements <i>asType</i>.
   */
  public Object implement(Class asType, Object with, Object defaultDelegate) {
    return newProxyInstance(with.getClass().getClassLoader(), asType, 
        createInvocationHandler(with, defaultDelegate));
  }
  /**
   * create a dynamic proxy that implements <i>asType</i> by calling <i>with</i>
   * if a method is implemented by the impl class.
   * Otherwise call the invoke() method of defaultHandler, 
   * or throw UnsupportedOperationException if defaultHandler is null.
   * @param asType the interface to implement or super class to override (cglib is required in this case). 
   * @param with the instance of the impl class.
   * @param defaultHandler the default InvocationHandler.
   * @return the dynamic proxy that implements <i>asType</i>.
   */
  public Object implementWithDefaultHandler(Class asType, Object with, InvocationHandler defaultHandler) {
    return newProxyInstance(with.getClass().getClassLoader(), asType, 
        createInvocationHandlerWithDefaultHandler(with, defaultHandler));
  }
  /**
   * create a dynamic proxy that implements <i>asType</i> by calling <i>with</i>
   * if a method is implemented by the impl class.
   * Otherwise call the invoke() method if <i>with</i> implements InvocationHandler,
   * or throw UnsupportedOperationException otherwise.
   * @param asType the interface to implement or super class to override (cglib is required in this case). 
   * @param with the instance of the impl class.
   * @return the dynamic proxy that implements <i>asType</i>.
   */
  public Object implement(Class asType, Object with){
    return newProxyInstance(with.getClass().getClassLoader(), asType, 
        createInvocationHandler(with));
  }
  /**
   * create an InvocationHandler object by calling <i>instance</i>
   * if a method is implemented by the impl class.
   * Otherwise call the invoke() method if <i>instance</i> implements InvocationHandler,
   * or throw UnsupportedOperationException otherwise. 
   * @param instance the instance of the impl class.
   * @return the InvocationHandler object.
   */
  public InvocationHandler createInvocationHandler(final Object instance){
    return createInvocationHandlerWithDefaultHandler(instance, 
        (instance instanceof InvocationHandler)?(InvocationHandler)instance:null);
  }
  /**
   * create an InvocationHandler object by calling <i>instance</i>
   * if a method is implemented by the impl class.
   * Otherwise call the provided default InvocationHandler object. 
   * @param instance the instance of the impl class.
   * @param defaultHandler the InvocationHandler object to provide default behavior.
   * If null, UnsupportedOperationException is thrown.
   * @return the InvocationHandler object.
   */
  public InvocationHandler createInvocationHandlerWithDefaultHandler(final Object instance, final InvocationHandler defaultHandler){
    return createInvocationHandlerWithDefaults(instance, null, defaultHandler);
  }
  /**
   * create an InvocationHandler object by calling <i>instance</i>
   * if a method is implemented by the impl class.
   * Otherwise forward the call to defaultDelegate. 
   * @param instance the instance of the impl class.
   * @param defaultDelegate the default delegate. If null, UnsupportedOperationException is thrown.
   * @return the InvocationHandler object.
   */
  public InvocationHandler createInvocationHandler(final Object instance, Object defaultDelegate){
    return createInvocationHandlerWithDefaults(instance, defaultDelegate, null);
  }
  InvocationHandler createInvocationHandlerWithDefaults(Object instance, Object defaultDelegate, InvocationHandler defaultHandler){
    checkInstanceType(instance);
    return new ImplInvocationHandler(instance, defaultDelegate, defaultHandler);
  }
  private void checkInstanceType(final Object instance) {
    if(!cls.isInstance(instance)){
      throw new IllegalArgumentException("instance of type "+cls.getName() + " expected, "
          + ((instance==null)?null:instance.getClass().getName())+" encountered");
    }
  }
  /**
   * To create an Implementor class.
   * @param implClass the class used to implement.
   */
  public Implementor(Class implClass){
    this.cls = implClass;
    addClass(implClass);
    sort();
  }
  public boolean equals(Object obj){
    if(obj instanceof Implementor){
      Implementor other = (Implementor)obj;
      return cls.equals(other.cls) && methods.equals(other.methods);
    }
    else return false;
  }
  public int hashCode(){
    return cls.hashCode(); 
  }
  public String toString(){
    return cls.toString();
  }
  /**
   * Get the impl class, which is the class whose public methods are used to implement target interface. 
   */
  public Class getImplClass(){
    return cls;
  }
  void addClass(Class cls) {
    final boolean force = !Modifier.isPublic(cls.getModifiers());
    final Method[] mtds = cls.getMethods();
    for (int i = 0; i < mtds.length; i++) {
      final Method mtd = mtds[i];
      if(force) setAccessible(mtd);
      addMethod(mtd);
    }
  }
  private static void setAccessible(Method mtd){
    try{
      mtd.setAccessible(true);
    }
    catch(SecurityException e){}
  }
  private final Class cls;
  private final Map methods = new HashMap();
  private final class ImplInvocationHandler implements InvocationHandler, Serializable {
    private static final long serialVersionUID = 7723292911817172565L;
    
    private final Object instance;
    private final Object defaultDelegate;
    private final InvocationHandler defaultHandler;
    ImplInvocationHandler(Object instance, Object defaultDelegate, InvocationHandler fwd) {
      this.instance = instance;
      this.defaultDelegate = defaultDelegate;
      this.defaultHandler = fwd;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      final Method impl = lookupImplementingMethod(method);
      if(impl == null) {
        if(defaultDelegate != null) {
          return forwardCall(defaultDelegate, method, args);
        }
        if(defaultHandler != null) {
          return defaultHandler.invoke(proxy, method, args);
        }
        else{
          throw new UnsupportedOperationException();
        }
      }
      else {
        return forwardCall(instance, impl, args);
      }
    }
    private Object forwardCall(Object obj, final Method mtd, Object[] args) throws IllegalAccessException, Throwable {
      try {
        return mtd.invoke(obj, args);
      }
      catch(InvocationTargetException e){
        throw e.getTargetException();
      }
    }
  }

  private static final class MyMethod implements Serializable {
    private static final long serialVersionUID = 8758558523031285785L;
    private transient Method method;
    private transient Class[] parameterTypes;
    private transient Class returnType;
    private final long depth;
    public boolean equals(Object obj){
      if(obj instanceof MyMethod){
        final MyMethod other = (MyMethod)obj;
        return depth==other.depth && returnType.equals(other.returnType)
          && method.equals(other.method)
          && Arrays.equals(parameterTypes, other.parameterTypes);
      }
      else return false;
    }
    MyMethod(Method mtd) {
      this.method = mtd;
      this.parameterTypes = mtd.getParameterTypes();
      this.returnType = mtd.getReturnType();
      this.depth = getHierarchyDepthSum(parameterTypes);
    }
    public Method getMethod() {
      return method;
    }
    public Class[] getParameterTypes() {
      return parameterTypes;
    }
    public Class getReturnType() {
      return returnType;
    }
    public String toString(){
      return method.toString();
    }
    public long getDepth(){
      return depth;
    }

    private void writeObject(java.io.ObjectOutputStream out)
    throws java.io.IOException{
      out.defaultWriteObject();
      out.writeObject(method.getDeclaringClass());
      out.writeObject(method.getName());
      out.writeObject(parameterTypes);
    }
    private void readObject(java.io.ObjectInputStream in)
      throws java.io.IOException, ClassNotFoundException{
      in.defaultReadObject();
      try{
        final Class c = (Class)in.readObject();
        final String name = (String)in.readObject();
        this.parameterTypes = (Class[])in.readObject();
        this.method = c.getDeclaredMethod(name, parameterTypes);
        this.returnType = method.getReturnType();
      }
      catch(NoSuchMethodException e){
        throw new IllegalStateException(e.getMessage());
      }
    }

  }
  void addMethod(Method mtd){
    final String name = mtd.getName();
    List suite = (List) methods.get(name);
    if(suite==null){
      suite = new ArrayList();
      methods.put(name, suite);
    }
    suite.add(new MyMethod(mtd));
  }
  void sort(){
    for(Iterator it = methods.values().iterator(); it.hasNext();){
      final List suite = (List)it.next();
      sortMethods(suite);
    }
  }
  private static final Comparator SUB_PARAM_TYPES_FIRST = new Comparator(){
    public int compare(Object arg0, Object arg1) {
      return compareMyMethod((MyMethod)arg0, (MyMethod)arg1);
    }
  };
  private static int compareMyMethod(MyMethod m1, MyMethod m2){
    return compareParameterTypes(m1.getParameterTypes(), m1.getDepth(), 
        m2.getParameterTypes(), m2.getDepth());
  }
  static int compareParameterTypes(final Class[] params1, long depth1, 
      final Class[] params2, long depth2) {
    if(params1.length > params2.length) return -1;
    if(params1.length < params2.length) return 1;
    if(depth1 > depth2) return -1;
    if(depth1 < depth2) return 1;
    return 0;
  }
  private static void sortMethods(List suite){
    Collections.sort(suite, SUB_PARAM_TYPES_FIRST);
  }
  /**
   * To find a method in the impl class that can be used in place of the <i>implemented</i> method.
   * @param implemented the method to be implemented.
   * @return the method that can be used to implement, or null if not found.
   */
  public Method lookupImplementingMethod(Method implemented) {
    final String name = implemented.getName();
    final List suite = (List)methods.get(name);
    if(suite==null) return null;
    final int size = suite.size();
    final Class[] implementedParamTypes = implemented.getParameterTypes();
    final Class implementedReturnType = implemented.getReturnType();
    for(int i=0; i<size; i++){
      final MyMethod mm = (MyMethod)suite.get(i);
      final Class[] withParamTypes = mm.getParameterTypes();
      if(isParamsCompatible(withParamTypes, implementedParamTypes) && isReturnTypeCompatible(mm.getReturnType(), implementedReturnType)){
        return mm.getMethod();
      }
    }
    return null;
  }
  /**
   * To create a proxy instance for a given interface or superclass.
   * @param loader the class loader.
   * @param asType the interface or super class (cglib is required in this case).
   * @param handler the InvocationHandler to handle calls.
   * @return the proxy instance.
   */
  public static Object newProxyInstance(ClassLoader loader, Class asType, InvocationHandler handler){
    if(asType.isInterface()){
      return Proxy.newProxyInstance(loader, new Class[]{asType}, handler);
    }
    else {
      return CglibUtils.proxy(loader, asType, handler);
    }
  }
  static boolean isParamsCompatible(Class[] with, Class[] implemented){
    if(with.length!=implemented.length) return false;
    for (int i = 0; i < implemented.length; i++) {
      if(!with[i].isAssignableFrom(implemented[i])) return false;
    }
    return true;
  }
  static boolean isReturnTypeCompatible(Class with, Class implemented){
    if(void.class.equals(implemented)){
      return true;
    }
    return implemented.isAssignableFrom(with);
  }
  static long getHierarchyDepthSum(Class[] classes){
    long sum = 0;
    for (int i = 0; i < classes.length; i++) {
      sum += getHierarchyDepth(classes[i]);
    }
    return sum;
  }
  static int getHierarchyDepth(Class c){
    int depth = 0;
    if(c==null || Object.class.equals(c)){
      return depth;
    }
    int superDepth = 1+getHierarchyDepth(c.getSuperclass());
    if(superDepth>depth){
      depth = superDepth;
    }
    final Class[] itfs = c.getInterfaces();
    for(int i=0; i<itfs.length; i++){
      int itfDepth = 1+getHierarchyDepth(itfs[i]);
      if(itfDepth>depth){
        depth = itfDepth;
      }
    }
    return depth;
  }

  private static final long serialVersionUID = -5648266362433165290L;
}

codehaus总是这么慢。先把代码贴出来。大家给挑挑错。
cglib相关的类就不贴了。代码不能编译,大家包涵则个。
0 请登录后投票
   发表时间:2006-12-12  
taowen 写道
nihongye 写道
就是重构起来麻烦点

这的确是一个问题。而且似乎没有什么好的解决办法做到重构安全。我能够看到的一个加强的地方是,验证new Object中每个方法是不是实现了要stub的接口或者具体类的方法。这样在改名了之后,可以fail fast,不会出莫名其妙的问题不知所以。
另外,不妨把酒窝当一个stub框架来做。

这样是可以,就是有点降低重用能力。我不能用一个Object给不同的interface做proxy么?

考虑可以在annotation上做文章。如果有一个@Override之类的annotation,那么就检查。
0 请登录后投票
   发表时间:2006-12-12  
ajoo 写道
taowen 写道
nihongye 写道
就是重构起来麻烦点

这的确是一个问题。而且似乎没有什么好的解决办法做到重构安全。我能够看到的一个加强的地方是,验证new Object中每个方法是不是实现了要stub的接口或者具体类的方法。这样在改名了之后,可以fail fast,不会出莫名其妙的问题不知所以。
另外,不妨把酒窝当一个stub框架来做。

这样是可以,就是有点降低重用能力。我不能用一个Object给不同的interface做proxy么?

考虑可以在annotation上做文章。如果有一个@Override之类的annotation,那么就检查。

By default, every method is regarded as @Override, only the methods marked as @Aux will be escaped from the check. Otherwise, most of the methods have to be marked as @Override, which is not convinent.
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics