dubbo的源码中,还有两个关于javassist技术和jdk proxy的扩展,
javassist:动态编程,通过jdk中的JavaCompiler接口,能够直接对一个已经存在java class文件进行编译,也可以在内存中动态生成java代码,动态编译执行
jdk proxy:jdk动态代理,通过Proxy类动态的根据指定的接口生成一个class byte,该class会继承自Proxy类,并且实现你的所有指定的接口,然后再利用指定的classloader将class byte加载进jvm,最后生成一个类的对象,并初始化该类的一些值,以及所有的接口和方法,这样客户端就能拿到你所定义的接口的一个Proxy对象
Javassist是一个强调源代码层面进行操作增加的字节码操作工具
利用Javassist实现字节码增强时,可以无须关注字节码刻板的结构,其优点就在于编程简单,直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构或者动态。
其中最重要的是ClassPool、CtClass、CtMethod、CtField这四个类:
以一个简单的小示例来说明一下javassist相关API的使用:
这里,我们以增强Base类的process()方法,在方法的执行之前打印start,在方法的执行之后打印end
/**
* @author: zhoucg
* @date: 2019-10-16
*/
public class Base {
public void process(){
System.out.println("process");
}
}
增强类的定义JavassistTest
/**
*
* 利用Javassist实现字节码增强时,可以无须关注字节码刻板的结构,其优点就在于编程简单
* 其中最重要的是ClassPool、CtClass、CtMethod、CtField这四个类:
*
* CtClass(compile-time class):编译时类信息,它是一个class文件在代码中的抽象表现形式,
* 可以通过一个类的全限定名来获取一个CtClass对象,用来表示这个类文件。
* ClassPool:从开发视角来看,ClassPool是一张保存CtClass信息的HashTable,key为类名,value为类名对应的CtClass对象。当我们需要对某个类进行修改时,
* 就是通过pool.getCtClass(“className”)方法从pool中获取到相应的CtClass。
*
* CtMethod、CtField:这两个比较好理解,对应的是类中的方法和属性。
*
* @author: zhoucg
* @date: 2019-10-16
*/
public class JavassistTest {
public static void main(String[] args) throws NotFoundException, CannotCompileException, IllegalAccessException, InstantiationException {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("com.common.jdk.javassist.Base");
CtMethod m = cc.getDeclaredMethod("process");
m.insertBefore("{ System.out.println(\"start\"); }");
m.insertAfter("{ System.out.println(\"end\"); }");
Class c = cc.toClass();
Base h = (Base)c.newInstance();
h.process();
}
}
dubbo中的Proxy类对于指定接口的动态类的class byte的生成是使用了javassist技术,而jdk 原生的JDK Proxy对于指定接口的动态类的class byte的生成是使用ProxyGenerator进行生成的,对于动态类的的对象的创建是由内部的ProxyClassFactory创建的
JDK原生的Proxy类中,很明显,公共的方法就红框中的四个,受保护的构造函数一个,
1,protected Proxy(InvocationHandler h),传参是一个InvocationHandler对象,这个对象的作用,它是Proxy代理实例调用处理程序实现的一个接口,每一个proxy代理实例都有一个调用关联的处理程序,在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。(这个看一个代理类之后就能明白)
2,public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException 静态的方法,获取对应的代理类的InvocationHandler对象
public static InvocationHandler getInvocationHandler(Object proxy)
throws IllegalArgumentException
{
/*
* Verify that the object is actually a proxy instance.
*/
//1,首先是判断对应的proxy是否为代理类
if (!isProxyClass(proxy.getClass())) {
throw new IllegalArgumentException("not a proxy instance");
}
//为对应的代理类时,直接获取代理类中的h变量
final Proxy p = (Proxy) proxy;
final InvocationHandler ih = p.h;
if (System.getSecurityManager() != null) {
Class> ihClass = ih.getClass();
Class> caller = Reflection.getCallerClass();
if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(),
ihClass.getClassLoader()))
{
ReflectUtil.checkPackageAccess(ihClass);
}
}
return ih;
}
3,public static Class> getProxyClass(ClassLoader loader,
Class>… interfaces) throws IllegalArgumentException 根据指定的ClassLoader和被代理的Class对象数组,获取对应的代理类Class对象
4,public static boolean isProxyClass(Class> cl) 判读当前的Class字节码类是否为对应的代理
5,public static Object newProxyInstance(ClassLoader loader,Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException 根据对应的ClassLoader,被代理类的接口Class数组,InvocationHandler对象获取对应的代理对象
/**
* @author: zhoucg
* @date: 2019-05-17
*/
public interface ProxyBean {
void say();
}
例如我们对应ProxyBean去创建它的代理类
//获取到代理对象的Class对象
Class proxyClass = Proxy.getProxyClass(ProxyBean.class.getClassLoader(),ProxyBean.class);
//根据Proxy.newProxyInstance(ClassLoader,Class>[] interfaces,InvocationHandler)获取代理对象
ProxyBean proxyClassBean = (ProxyBean) Proxy.newProxyInstance(proxyClass.getClassLoader(),proxyClass.getInterfaces(),myInvocationHandler);
proxyClassBean.say();
//根据获取到的代理的Class对象实例化
Constructor constructors = proxyClass.getConstructor(InvocationHandler.class);
System.out.println("aa"+constructors);
ProxyBean constructorProxyBean = (ProxyBean) constructors.newInstance(myInvocationHandler);
constructorProxyBean.say();
MyInvocationhHandler
/**
* @author: zhoucg
* @date: 2019-05-17
*/
public class MyInvocationHandler implements InvocationHandler {
private final TestAddBean testAddBean;
public MyInvocationHandler(TestAddBean testAddBean) {
this.testAddBean = testAddBean;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
testAddBean.test(methodName);
return null;
}
}
生成的$Proxy
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import com.common.util.proxy.jdk.ProxyBean;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements ProxyBean {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void say() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.common.util.proxy.jdk.ProxyBean").getMethod("say", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
在ProxyBean接口的代理类中,首先是继承了JDK 的Proxy类,然后实现类ProxyBean接口,并实现类ProxyBean接口中的say()方法,
public final void say() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
say方法中,将对应的调用委托给了之前传递进入的MyInvocationHandler对象的invoke(Object obj,Method method,Object[] args)方法进行指定,可以发现,第一个参数代表的就是当前接口ProxyBean的代理类,第二个参数method表示的是指定的mehod对象,第三个参数表示方法的参数信息,
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//加上这个
System.out.println(proxy);
String methodName = method.getName();
testAddBean.test(methodName);
return null;
}
很明显,这个是会报错的,System.out.println(proxy);方法实际上就是指定System.out.println(proxy.toString()); 方法,所以会进入到$Proxy方法的toString()方法,但是,toString还是会将方法的指定委托给handler中的invoke方法,这个就形成了一个死循环了
所以:不要视图在invoke方法中去对代理类进行toString(),hash(),equals()方法
Exception in thread "main" java.lang.StackOverflowError
at com.sun.proxy.$Proxy0.toString(Unknown Source)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
at com.sun.proxy.$Proxy0.toString(Unknown Source)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
at com.sun.proxy.$Proxy0.toString(Unknown Source)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
at com.sun.proxy.$Proxy0.toString(Unknown Source)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
at com.sun.proxy.$Proxy0.toString(Unknown Source)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
at com.sun.proxy.$Proxy0.toString(Unknown Source)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
at com.sun.proxy.$Proxy0.toString(Unknown Source)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
at com.sun.proxy.$Proxy0.toString(Unknown Source)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at com.common.util.proxy.jdk.MyInvocationHandler.invoke(MyInvocationHandler.java:22)
at com.sun.proxy.$Proxy0.toString(Unknown Source)
at java.lang.String.valueOf(String.java:2994)
。。。。。。。。。。。。。
dubbo对于proxy的使用,实际上相对于jdk 的 Proxy简单的多,对于动态类的class byte的生成使用了javassist技术,【dubbo的ClassGenerator源码】
/*
* Copyright 1999-2011 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.dubbo.common.bytecode;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicLong;
import com.alibaba.dubbo.common.utils.ClassHelper;
import com.alibaba.dubbo.common.utils.ReflectUtils;
/**
* Proxy.
*
* @author qian.lei
*/
public abstract class Proxy
{
private static final AtomicLong PROXY_CLASS_COUNTER = new AtomicLong(0);
private static final String PACKAGE_NAME = Proxy.class.getPackage().getName();
public static final InvocationHandler RETURN_NULL_INVOKER = new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args){ return null; }
};
public static final InvocationHandler THROW_UNSUPPORTED_INVOKER = new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args){ throw new UnsupportedOperationException("Method [" + ReflectUtils.getName(method) + "] unimplemented."); }
};
private static final Map> ProxyCacheMap = new WeakHashMap>();
private static final Object PendingGenerationMarker = new Object();
/**
* Get proxy.
*
* @param ics interface class array.
* @return Proxy instance.
*/
public static Proxy getProxy(Class>... ics)
{
return getProxy(ClassHelper.getCallerClassLoader(Proxy.class), ics);
}
/**
* Get proxy.
* @param cl class loader.
* @param ics interface class array.
*
* @return Proxy instance.
*/
public static Proxy getProxy(ClassLoader cl, Class>... ics)
{
if( ics.length > 65535 )
throw new IllegalArgumentException("interface limit exceeded");
StringBuilder sb = new StringBuilder();
for(int i=0;i tmp = null;
try
{
tmp = Class.forName(itf, false, cl);
}
catch(ClassNotFoundException e)
{}
if( tmp != ics[i] )
throw new IllegalArgumentException(ics[i] + " is not visible from class loader");
sb.append(itf).append(';');
}
// use interface class name list as key.
String key = sb.toString();
// get cache by class loader.
Map cache;
synchronized( ProxyCacheMap )
{
cache = ProxyCacheMap.get(cl);
if( cache == null )
{
cache = new HashMap();
ProxyCacheMap.put(cl, cache);
}
}
Proxy proxy = null;
synchronized( cache )
{
do
{
Object value = cache.get(key);
if( value instanceof Reference> )
{
proxy = (Proxy)((Reference>)value).get();
if( proxy != null )
return proxy;
}
if( value == PendingGenerationMarker )
{
try{ cache.wait(); }catch(InterruptedException e){}
}
else
{
cache.put(key, PendingGenerationMarker);
break;
}
}
while( true );
}
long id = PROXY_CLASS_COUNTER.getAndIncrement();
String pkg = null;
ClassGenerator ccp = null, ccm = null;
try
{
ccp = ClassGenerator.newInstance(cl);
Set worked = new HashSet();
List methods = new ArrayList();
for(int i=0;i rt = method.getReturnType();
Class>[] pts = method.getParameterTypes();
StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
for(int j=0;j[]{ InvocationHandler.class }, new Class>[0], "handler=$1;");
ccp.addDefaultConstructor();
Class> clazz = ccp.toClass();
clazz.getField("methods").set(null, methods.toArray(new Method[0]));
// create Proxy class.
String fcn = Proxy.class.getName() + id;
ccm = ClassGenerator.newInstance(cl);
ccm.setClassName(fcn);
ccm.addDefaultConstructor();
ccm.setSuperClass(Proxy.class);
ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");
Class> pc = ccm.toClass();
proxy = (Proxy)pc.newInstance();
}
catch(RuntimeException e)
{
throw e;
}
catch(Exception e)
{
throw new RuntimeException(e.getMessage(), e);
}
finally
{
// release ClassGenerator
if( ccp != null )
ccp.release();
if( ccm != null )
ccm.release();
synchronized( cache )
{
if( proxy == null )
cache.remove(key);
else
cache.put(key, new WeakReference(proxy));
cache.notifyAll();
}
}
return proxy;
}
/**
* get instance with default handler.
*
* @return instance.
*/
public Object newInstance()
{
return newInstance(THROW_UNSUPPORTED_INVOKER);
}
/**
* get instance with special handler.
*
* @return instance.
*/
abstract public Object newInstance(InvocationHandler handler);
protected Proxy(){}
private static String asArgument(Class> cl, String name)
{
if( cl.isPrimitive() )
{
if( Boolean.TYPE == cl )
return name + "==null?false:((Boolean)" + name + ").booleanValue()";
if( Byte.TYPE == cl )
return name + "==null?(byte)0:((Byte)" + name + ").byteValue()";
if( Character.TYPE == cl )
return name + "==null?(char)0:((Character)" + name + ").charValue()";
if( Double.TYPE == cl )
return name + "==null?(double)0:((Double)" + name + ").doubleValue()";
if( Float.TYPE == cl )
return name + "==null?(float)0:((Float)" + name + ").floatValue()";
if( Integer.TYPE == cl )
return name + "==null?(int)0:((Integer)" + name + ").intValue()";
if( Long.TYPE == cl )
return name + "==null?(long)0:((Long)" + name + ").longValue()";
if( Short.TYPE == cl )
return name + "==null?(short)0:((Short)" + name + ").shortValue()";
throw new RuntimeException(name+" is unknown primitive type.");
}
return "(" + ReflectUtils.getName(cl) + ")"+name;
}
}