package net.dubboclub.dubbogenerator.reference; import com.alibaba.dubbo.common.utils.ConfigUtils; import com.alibaba.dubbo.common.utils.StringUtils; import com.alibaba.dubbo.config.ReferenceConfig; import net.dubboclub.dubbogenerator.InvokeTargetException; import net.dubboclub.dubbogenerator.JavassistClassGenerator; import net.dubboclub.dubbogenerator.handler.DefaultInvokeHandler; import net.dubboclub.dubbogenerator.handler.InvokeHandler; import java.lang.reflect.Method; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; /** * Created by bieber on 2015/7/31. * 动态加载封装Dubbo客户端调用 */ public class DubboClientWrapper { //缓存已经生成的包装对象 private static final ConcurrentHashMap<Class<?>,Object> WRAPPER_CACHE = new ConcurrentHashMap<Class<?>, Object>(); private static final ConcurrentHashMap<Class<?>,Object> HANDLER_CACHE = new ConcurrentHashMap<Class<?>, Object>(); private static final String DEFAULT_WRAPPER_HANDLER_KEY="dubbo.wrapper.default.handler"; private static final String INVOKE_HANDLER_KEY_SUFFIX=".handler"; private static final String DUBBO_WRAPPER_KEY_PREFIX="dubbo.wrapper."; //包装计数器 private static final AtomicLong WRAPPER_COUNTER = new AtomicLong(0); private static Class<? extends InvokeHandler> DEFAULT_HANDLER = DefaultInvokeHandler.class; private static volatile boolean isShutdown = false; private static volatile boolean hadSetDefaultHandler = false; //动态类的标记接口 public interface DCW{ }//mark dynamic facade wrapper static { Runtime.getRuntime().addShutdownHook(new Thread(){ @Override public void run() { isShutdown=true; WRAPPER_CACHE.clear(); } }); String handlerClassName = ConfigUtils.getProperty(DEFAULT_WRAPPER_HANDLER_KEY); if(!StringUtils.isEmpty(handlerClassName)){ DEFAULT_HANDLER = generateHandler(handlerClassName); } } public static<T extends Object> T getWrapper(Class<T> clientType,String id){ return getWrapper(clientType,id, null); } public static<T extends Object> T getWrapper(Class<T> clientType,Class<? extends InvokeHandler> handler){ return getWrapper(clientType,null,handler); } /** * 对外提供的接口,获取指定类型的dubbo客户端引用 * 如果之前创建过,则直接从缓存中获取,不必再次创建 * @param clientType * @param <T> * @return */ public static<T extends Object> T getWrapper(Class<T> clientType){ return getWrapper(clientType,generateClientId(clientType)); } public static<T extends Object> T getWrapper(Class<T> clientType,String id,Class<? extends InvokeHandler> handler){ if(isShutdown){ throw new IllegalStateException("JVM had shutdown,can not generate wrapper!"); } T clientInstance = null; if(StringUtils.isEmpty(id)){ id = generateClientId(clientType); } if(handler==null){ handler = getInvokeHandler(clientType,id); } if(WRAPPER_CACHE.containsKey(clientType)){ clientInstance = (T) WRAPPER_CACHE.get(clientType); }else{ clientInstance = (T) makeClientWrapper(clientType,id,handler); Object oldInstance = WRAPPER_CACHE.putIfAbsent(clientType,clientInstance); if(oldInstance!=null){ clientInstance= (T) oldInstance; } } return clientInstance; } public static synchronized void setDefaultHandler(Class<? extends InvokeHandler> handler){ if(hadSetDefaultHandler){ throw new IllegalStateException("had already set default InvokeHandler ["+DEFAULT_HANDLER.getName()+"]."); } hadSetDefaultHandler=true; DEFAULT_HANDLER=handler; } /** * 判断当前类是不是包装类 * @param type * @return */ public static boolean isWrapped(Class<?> type){ return DCW.class.isAssignableFrom(type); } private static String generateClientId(Class<?> clientType){ return "["+clientType.getName()+"]"; } private static Class<? extends InvokeHandler> getInvokeHandler(Class<?> clientType,String id){ //dubbo.wrapper.[clientfacadefullname].handler String handlerName = ConfigUtils.getProperty(DUBBO_WRAPPER_KEY_PREFIX + id + INVOKE_HANDLER_KEY_SUFFIX); if(StringUtils.isEmpty(handlerName)){ return DEFAULT_HANDLER; } return generateHandler(handlerName); } private static Class<? extends InvokeHandler> generateHandler(String handlerClassName){ try { Class<?> handlerClass = Class.forName(handlerClassName); if(!InvokeHandler.class.isAssignableFrom(handlerClass)){ throw new IllegalArgumentException("Class ["+handlerClassName+"] must implements InvokerHandler or extends sub class,please check property [default.dubbo.wrapper.handler]"); } return (Class<? extends InvokeHandler>) handlerClass; } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Class ["+handlerClassName+"] not found ,please check property [default.dubbo.wrapper.handler]",e); } } /** * 构造一个新的指定类型的Dubbo客户端引用 * @param clientType 该参数只能是接口类型,不支持其他类型 * @return */ private static Object makeClientWrapper(Class<?> clientType,String clientId,Class<? extends InvokeHandler> handlerClass){ String clientName = clientType.getSimpleName(); long id = WRAPPER_COUNTER.getAndIncrement(); if(clientType==DCW.class){ throw new IllegalArgumentException("not support generate wrapper for interface "+clientName); } if(!clientType.isInterface()){ throw new IllegalArgumentException("only support interface to generate wrapper,but type "+clientName+" is not interface"); } JavassistClassGenerator generator = JavassistClassGenerator.newInstance(clientType.getClassLoader()); generator.addInterface(clientType.getName()); generator.addInterface(DCW.class.getName()); StringBuilder className = new StringBuilder(); className.append(clientType.getSimpleName()).append("$DCW").append(id); generator.setClassName(className.toString()); Method[] methods = clientType.getDeclaredMethods(); StringBuilder methodCode = new StringBuilder(); for(Method method:methods){ methodCode.append("public "); Class<?> returnType = method.getReturnType(); Class<?>[] argsTypes = method.getParameterTypes(); if(returnType== Void.TYPE){ methodCode.append("void "); }else{ methodCode.append(returnType.getName()).append(" "); } methodCode.append(method.getName()).append("("); //append arguments if(argsTypes.length!=0){ for(int i=0;i<argsTypes.length;i++){ methodCode.append(argsTypes[i].getName()).append(" arg").append(i).append(","); } methodCode.setLength(methodCode.length()-1); } Class<?>[] exceptionTypes = method.getExceptionTypes(); methodCode.append(")"); if(exceptionTypes.length!=0){ methodCode.append("throws "); for(Class<?> exceptionType:exceptionTypes){ methodCode.append(exceptionType.getName()).append(","); } methodCode.setLength(methodCode.length()-1); } methodCode.append("{"); methodCode.append("try{"); //append method body //handle invoke before if(argsTypes.length==0){ methodCode.append("invokeHandler.beforeInvoke(clientType,\"" + method.getName() + "\",null);"); }else{ methodCode.append("invokeHandler.beforeInvoke(clientType,\"" + method.getName() + "\",$args);"); } //handle invoke complete if(returnType== Void.TYPE){ methodCode.append("(($w)clientRef).").append(method.getName()).append("($$);"); if(argsTypes.length==0){ methodCode.append("invokeHandler.completeInvoke(clientType,\"" + method.getName() + "\",null,null);"); }else{ methodCode.append("invokeHandler.completeInvoke(clientType,\"" + method.getName() + "\",null,$args);"); } }else{ methodCode.append("Object result=(($w)clientRef).").append(method.getName()).append("($$);"); if(argsTypes.length==0){ methodCode.append("invokeHandler.completeInvoke(clientType,\"" + method.getName() + "\",(Object)result,null);"); }else{ methodCode.append("invokeHandler.completeInvoke(clientType,\"" + method.getName() + "\",(Object)result,$args);"); } methodCode.append("return ($r)result;"); } methodCode.append("}catch(Throwable t){"); //handle invoke exception if(argsTypes.length==0){ methodCode.append("invokeHandler.caughtException(clientType,\"" + method.getName() + "\",t,null);"); }else{ methodCode.append("invokeHandler.caughtException(clientType,\"" + method.getName() + "\",t,$args);"); } methodCode.append("throw new ").append(InvokeTargetException.class.getName()).append("(clientType,\""+method.getName()+"\",t);"); methodCode.append("}"); methodCode.append("}"); generator.addMethod(methodCode.toString()); methodCode.setLength(0); } generator.addField("public static "+clientType.getName()+" clientRef;"); generator.addField("public static "+InvokeHandler.class.getName()+" invokeHandler;"); generator.addField("public static "+Class.class.getName()+" clientType;"); Class<?> clazz = generator.toClass(); try { ReferenceConfig config = new ReferenceConfig(); config.setId(clientId); config.setCheck(false); config.setInterface(clientType.getName()); clazz.getField("clientRef").set(null,config.get()); if(!HANDLER_CACHE.containsKey(handlerClass)){ HANDLER_CACHE.putIfAbsent(handlerClass,handlerClass.newInstance()); } clazz.getField("invokeHandler").set(null,HANDLER_CACHE.get(handlerClass)); clazz.getField("clientType").set(null,clientType); return clazz.newInstance(); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } } }