Javassist实现动态代理
介绍
Javassist 也是一个字节码框架,和其他字节码框架不同的是,它提供了两种层级的API,源层级和字节码层级,源层级不需要对字节码规则了解太多就可以操作。Hibernate的懒加载就使用到了Javassist。官网
使用Javassist提供的动态代理接口实现
maven依赖
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
代理接口
/**
* 可以唱歌的
*/
public interface Singable {
/**
* 唱歌
*/
void sing();
}
被代理类
/**
* 歌手
*/
public class Singer implements Singable {
@Override
public void sing() {
System.out.println("I am singing...");
}
}
创建动态代理
public class Client {
public static void main(String[] args) throws Exception {
Singable proxy = createJavassistDynamicProxy();
proxy.sing();
}
private static Singable createJavassistDynamicProxy()
throws Exception {
ProxyFactory proxyFactory = new ProxyFactory();
// 设置实现的接口
proxyFactory.setInterfaces(new Class[]{Singable.class});
Class<?> proxyClass = proxyFactory.createClass();
Singable javassistProxy = (Singable) proxyClass.getDeclaredConstructor().newInstance();
((ProxyObject) javassistProxy).setHandler(new JavassistInterceptor(new Singer()));
return javassistProxy;
}
private static class JavassistInterceptor implements MethodHandler {
// 被代理对象
private Object delegate;
private JavassistInterceptor(Object delegate) {
this.delegate = delegate;
}
/**
* @param self 创建的代理对象
* @param m 被代理方法
* @param proceed 如果代理接口,此参数为null,如果代理类,此参数为父类的方法
* @param args 方法参数
*/
public Object invoke(Object self, Method m, Method proceed,
Object[] args) throws Throwable {
System.out.println("javassist proxy before sing");
Object ret = m.invoke(delegate, args);
System.out.println("javassist proxy after sing");
return ret;
}
}
}
和JDK的动态代理创建方式类似,但Javassist也可以代理类。
public class Client {
public static void main(String[] args) throws Exception {
Singable proxy = createJavassistDynamicProxy();
proxy.sing();
}
private static Singable createJavassistDynamicProxy()
throws Exception {
ProxyFactory proxyFactory = new ProxyFactory();
// 设置父类
proxyFactory.setSuperclass(Singer.class);
Class<?> proxyClass = proxyFactory.createClass();
Singable javassistProxy = (Singable) proxyClass.getDeclaredConstructor().newInstance();
((ProxyObject) javassistProxy).setHandler(new JavassistInterceptor());
return javassistProxy;
}
private static class JavassistInterceptor implements MethodHandler {
/**
* @param self 创建的代理对象
* @param m 被代理方法
* @param proceed 如果代理接口,此参数为null,如果代理类,此参数为父类的方法
* @param args 方法参数
*/
public Object invoke(Object self, Method m, Method proceed,
Object[] args) throws Throwable {
System.out.println("javassist proxy before sing");
// 调用父类的sing方法
Object ret = proceed.invoke(self, args);
System.out.println("javassist proxy after sing");
return ret;
}
}
}
Javassist创建的代理类反编译之后为
public class Singer_$$_jvst3e4_0 extends Singer
implements ProxyObject
{
public final Object _d0clone()
throws CloneNotSupportedException
{
return super.clone();
}
protected final Object clone()
throws CloneNotSupportedException
{
Method amethod[] = _methods_;
return (Object)handler.invoke(this, amethod[0], amethod[1], new Object[0]);
}
public final boolean _d1equals(Object obj)
{
return super.equals(obj);
}
public final boolean equals(Object obj)
{
Method amethod[] = _methods_;
return ((Boolean)handler.invoke(this, amethod[2], amethod[3], new Object[] {
obj
})).booleanValue();
}
public final void _d2finalize()
throws Throwable
{
super.finalize();
}
protected final void finalize()
throws Throwable
{
Method amethod[] = _methods_;
handler.invoke(this, amethod[4], amethod[5], new Object[0]);
}
public final int _d4hashCode()
{
return super.hashCode();
}
public final int hashCode()
{
Method amethod[] = _methods_;
return ((Integer)handler.invoke(this, amethod[8], amethod[9], new Object[0])).intValue();
}
// 调用父类方法
public final void _d7sing()
{
super.sing();
}
public final void sing()
{
Method amethod[] = _methods_;
// amethod[14]为sing方法,amethod[15]为_d7sing方法,可以看静态代码块的初始化
handler.invoke(this, amethod[14], amethod[15], new Object[0]);
}
public final String _d8toString()
{
return super.toString();
}
public final String toString()
{
Method amethod[] = _methods_;
return (String)handler.invoke(this, amethod[16], amethod[17], new Object[0]);
}
public void setHandler(MethodHandler methodhandler)
{
handler = methodhandler;
}
public MethodHandler getHandler()
{
return handler;
}
Object writeReplace()
throws ObjectStreamException
{
return RuntimeSupport.makeSerializedProxy(this);
}
private MethodHandler handler;
public static byte _filter_signature[];
public static final long serialVersionUID = -1L;
private static Method _methods_[];
static
throws ClassNotFoundException
{
Method amethod[] = new Method[24];
Class class1 = Class.forName("com.imooc.sourcecode.java.dynamicproxy.javassist.test3.Singer_$$_jvst3e4_0");
RuntimeSupport.find2Methods(class1, "clone", "_d0clone", 0, "()Ljava/lang/Object;", amethod);
RuntimeSupport.find2Methods(class1, "equals", "_d1equals", 2, "(Ljava/lang/Object;)Z", amethod);
RuntimeSupport.find2Methods(class1, "finalize", "_d2finalize", 4, "()V", amethod);
RuntimeSupport.find2Methods(class1, "hashCode", "_d4hashCode", 8, "()I", amethod);
// 下标14赋值为sing方法,15赋值为_d7sing方法
RuntimeSupport.find2Methods(class1, "sing", "_d7sing", 14, "()V", amethod);
RuntimeSupport.find2Methods(class1, "toString", "_d8toString", 16, "()Ljava/lang/String;", amethod);
_methods_ = amethod;
}
public Singer_$$_jvst3e4_0()
{
handler = RuntimeSupport.default_interceptor;
super();
}
}
看一下RuntimeSupport的find2Methods方法
/**
* Finds two methods specified by the parameters and stores them
* into the given array.
*
* @throws RuntimeException if the methods are not found.
* @see javassist.util.proxy.ProxyFactory
*/
public static void find2Methods(Class<?> clazz, String superMethod,
String thisMethod, int index,
String desc, java.lang.reflect.Method[] methods)
{
methods[index + 1] = thisMethod == null ? null
: findMethod(clazz, thisMethod, desc);
methods[index] = findSuperClassMethod(clazz, superMethod, desc);
}
使用Javassist提供的字节码API实现
代理接口和被代理类同上
public class Client {
public static void main(String[] args) throws Exception {
Singable proxy = createJavassistBytecodeDynamicProxy(new Singer());
proxy.sing();
}
private static Singable createJavassistBytecodeDynamicProxy(Singable delegate) throws Exception {
ClassPool mPool = new ClassPool(true);
CtClass mCtc = mPool.makeClass(Singable.class.getName() + "JavaassistProxy");
mCtc.addInterface(mPool.get(Singable.class.getName()));
mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));
mCtc.addField(CtField.make("public " + Singable.class.getName() + " delegate;", mCtc));
String src = "public void sing() { "
+ "System.out.println(\"javassist bytecode proxy before sing\");"
+ "delegate.sing();"
+ "System.out.println(\"javassist bytecode proxy after sing\"); "
+ "}";
mCtc.addMethod(CtNewMethod.make(src, mCtc));
Class<?> pc = mCtc.toClass();
Singable bytecodeProxy = (Singable) pc.getDeclaredConstructor().newInstance();
Field filed = bytecodeProxy.getClass().getField("delegate");
filed.set(bytecodeProxy, delegate);
return bytecodeProxy;
}
}
Javassist可以直接拼接java源码生成字节码,这是比ASM易用的地方,但也会造成一定的性能损失。
生成的代理类反编译为
public class SingableJavaassistProxy
implements Singable
{
public SingableJavaassistProxy()
{
}
public void sing()
{
System.out.println("javassist bytecode proxy before sing");
_flddelegate.sing();
System.out.println("javassist bytecode proxy after sing");
}
public Singable _flddelegate;
}
可以看到使用字节码生成的类相比代理工厂(JDK,CGLIB,Javassist的ProxyFactory)生成的类要小很多,所以速度也会更快。