今天谈谈 Dubbo 的 org.apache.dubbo.common.bytecode.Wrapper 类
Dubbo 依赖该工具在服务提供者端接收处理请求的时候、直接调用提供服务的 service 相对于传统的反射、性能其实有很大的提升
private static Wrapper makeWrapper(Class<?> c) {
String name = c.getName();
ClassLoader cl = ClassUtils.getClassLoader(c);
StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ ");
StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ ");
StringBuilder c3 = new StringBuilder("public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws " + InvocationTargetException.class.getName() + "{ ");
c1.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
c2.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
c3.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
Map<String, Class<?>> pts = new HashMap<>(); // <property name, property types>
Map<String, Method> ms = new LinkedHashMap<>(); // <method desc, Method instance>
List<String> mns = new ArrayList<>(); // method names.
List<String> dmns = new ArrayList<>(); // declaring method names.
// get all public field.
for (Field f : c.getFields()) {
String fn = f.getName();
Class<?> ft = f.getType();
if (Modifier.isStatic(f.getModifiers()) || Modifier.isTransient(f.getModifiers())) {
continue;
}
c1.append(" if( $2.equals(\"").append(fn).append("\") ){ w.").append(fn).append("=").append(arg(ft, "$3")).append("; return; }");
c2.append(" if( $2.equals(\"").append(fn).append("\") ){ return ($w)w.").append(fn).append("; }");
pts.put(fn, ft);
}
Method[] methods = c.getMethods();
// get all public method.
boolean hasMethod = hasMethods(methods);
if (hasMethod) {
c3.append(" try{");
for (Method m : methods) {
//ignore Object's method.
if (m.getDeclaringClass() == Object.class) {
continue;
}
String mn = m.getName();
c3.append(" if( \"").append(mn).append("\".equals( $2 ) ");
int len = m.getParameterTypes().length;
c3.append(" && ").append(" $3.length == ").append(len);
boolean override = false;
for (Method m2 : methods) {
if (m != m2 && m.getName().equals(m2.getName())) {
override = true;
break;
}
}
if (override) {
if (len > 0) {
for (int l = 0; l < len; l++) {
c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"")
.append(m.getParameterTypes()[l].getName()).append("\")");
}
}
}
c3.append(" ) { ");
if (m.getReturnType() == Void.TYPE) {
c3.append(" w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");").append(" return null;");
} else {
c3.append(" return ($w)w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");");
}
c3.append(" }");
mns.add(mn);
if (m.getDeclaringClass() == c) {
dmns.add(mn);
}
ms.put(ReflectUtils.getDesc(m), m);
}
c3.append(" } catch(Throwable e) { ");
c3.append(" throw new java.lang.reflect.InvocationTargetException(e); ");
c3.append(" }");
}
c3.append(" throw new " + NoSuchMethodException.class.getName() + "(\"Not found method \\\"\"+$2+\"\\\" in class " + c.getName() + ".\"); }");
// deal with get/set method.
Matcher matcher;
for (Map.Entry<String, Method> entry : ms.entrySet()) {
String md = entry.getKey();
Method method = entry.getValue();
if ((matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
String pn = propertyName(matcher.group(1));
c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
pts.put(pn, method.getReturnType());
} else if ((matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(md)).matches()) {
String pn = propertyName(matcher.group(1));
c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
pts.put(pn, method.getReturnType());
} else if ((matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
Class<?> pt = method.getParameterTypes()[0];
String pn = propertyName(matcher.group(1));
c1.append(" if( $2.equals(\"").append(pn).append("\") ){ w.").append(method.getName()).append("(").append(arg(pt, "$3")).append("); return; }");
pts.put(pn, pt);
}
}
c1.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" field or setter method in class " + c.getName() + ".\"); }");
c2.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" field or setter method in class " + c.getName() + ".\"); }");
// make class
long id = WRAPPER_CLASS_COUNTER.getAndIncrement();
ClassGenerator cc = ClassGenerator.newInstance(cl);
cc.setClassName((Modifier.isPublic(c.getModifiers()) ? Wrapper.class.getName() : c.getName() + "$sw") + id);
cc.setSuperClass(Wrapper.class);
cc.addDefaultConstructor();
cc.addField("public static String[] pns;"); // property name array.
cc.addField("public static " + Map.class.getName() + " pts;"); // property type map.
cc.addField("public static String[] mns;"); // all method name array.
cc.addField("public static String[] dmns;"); // declared method name array.
for (int i = 0, len = ms.size(); i < len; i++) {
cc.addField("public static Class[] mts" + i + ";");
}
cc.addMethod("public String[] getPropertyNames(){ return pns; }");
cc.addMethod("public boolean hasProperty(String n){ return pts.containsKey($1); }");
cc.addMethod("public Class getPropertyType(String n){ return (Class)pts.get($1); }");
cc.addMethod("public String[] getMethodNames(){ return mns; }");
cc.addMethod("public String[] getDeclaredMethodNames(){ return dmns; }");
cc.addMethod(c1.toString());
cc.addMethod(c2.toString());
cc.addMethod(c3.toString());
try {
Class<?> wc = cc.toClass();
// setup static field.
wc.getField("pts").set(null, pts);
wc.getField("pns").set(null, pts.keySet().toArray(new String[0]));
wc.getField("mns").set(null, mns.toArray(new String[0]));
wc.getField("dmns").set(null, dmns.toArray(new String[0]));
int ix = 0;
for (Method m : ms.values()) {
wc.getField("mts" + ix++).set(null, m.getParameterTypes());
}
return (Wrapper) wc.newInstance();
} catch (RuntimeException e) {
throw e;
} catch (Throwable e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
cc.release();
ms.clear();
mns.clear();
dmns.clear();
}
}
其实这段代码就是动态生成一个包装类、生成的类继承 org.apache.dubbo.common.bytecode.Wrapper
主要涉及有三个方法
- getPropertyValue
- invokeMethod
- setPropertyValue
生成的类其实是
public class TestWrapper {
private String name;
private Integer age;
public String address;
public String sayHi(){
return "hi";
}
public String sayGoodbye() {
return "bye";
}
}
看下生成的 Wrapper 对象
package org.apache.dubbo.common.bytecode;
public class Wrapper0 extends Wrapper {
/**
* property name array.
*/
public static String[] pns;
/**
* property type map.
*/
public static Map pts;
/**
* all method name array.
*/
public static String[] mns;
/**
* declared method name array.
*/
public static String[] dmns;
public static Class[] mts0; //存放方法的参数类型
public static Class[] mts1; // 存放方法的参数类型
public String[] getPropertyNames(){ return pns; }
public boolean hasProperty(String n){ return pts.containsKey(n); }
public Class getPropertyType(String n){ return (Class)pts.get(n); }
public String[] getMethodNames(){ return mns; }
public String[] getDeclaredMethodNames(){ return dmns; }
public Object getPropertyValue(Object o, String n) {
TestWrapper w;
try {
w = ((TestWrapper) $1);
} catch (Throwable e) {
throw new IllegalArgumentException(e);
}
if ($2.equals("address")) {
return ($w) w.address;
}
throw new org.apache.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + $2 + "\" field or setter method in class TestWrapper.");
}
public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
TestWrapper w;
try {
w = ((TestWrapper) $1);
} catch (Throwable e) {
throw new IllegalArgumentException(e);
}
try {
if ("sayHi".equals($2) && $3.length == 0) {
return ($w) w.sayHi();
}
if ("sayGoodbye".equals($2) && $3.length == 0) {
return ($w) w.sayGoodbye();
}
} catch (Throwable e) {
throw new java.lang.reflect.InvocationTargetException(e);
}
throw new org.apache.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + $2 + "\" in class TestWrapper.");
}
public void setPropertyValue(Object o, String n, Object v) {
TestWrapper w;
try {
w = ((TestWrapper) $1);
} catch (Throwable e) {
throw new IllegalArgumentException(e);
}
if ($2.equals("address")) {
w.address = (java.lang.String) $3;
return;
}
throw new org.apache.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + $2 + "\" field or setter method in class TestWrapper.");
}
}
- getPropertyValue 能获取到的都是申明为 public 的属性
- invokeMethod 能获取到的都是申明为 public 的方法
- setPropertyValue 能获取到的都是申明为 public 的属性
原因简单啊、因为不在同一个包下、生成的这个 Wrapper 类是跟 org.apache.dubbo.common.bytecode.Wrapper 在同一个包中
至于他生成的几个静态变量
其实都是为了以后方便获取被包装类的相关信息、比如在 ServiceConfig 的 doExportUrlsFor1Protocol 方法的时候
就通过生成一个 Wrapper 获取服务接口暴露出去多少个方法、获取起方法名、放到 map 里面、为后续生成注册的 url 做准备