Java的动态代理技术很强大,能够在运行时动态生成接口的,具体用法如下:
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(..);
// 通过 Proxy 直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,
new Class[] { Interface.class },
handler );
JDK动态代理机制优缺点
首先是动态生成的代理类本身的一些特点:
1、包:如果所代理的接口都是 public 的,那么它将被定义在顶层包(即包路径为空),如果所代理的接口中有非 public 的接口(因为接口不能被定义为 protect 或 private,所以除 public 之外就是默认的 package 访问级别),那么它将被定义在该接口所在包(假设代理了 com.ibm.developerworks 包中的某非 public 接口 A,那么新生成的代理类所在的包就是 com.ibm.developerworks),这样设计的目的是为了最大程度的保证动态代理类不会因为包管理的问题而无法被成功定义并访问;
2、类修饰符:该代理类具有 final 和 public 修饰符,意味着它可以被所有的类访问,但是不能被再度继承;
3、类名:格式是“$ProxyN”,其中 N 是一个逐一递增的阿拉伯数字,代表 Proxy 类第 N 次生成的动态代理类,值得注意的一点是,并不是每次调用 Proxy 的静态方法创建动态代理类都会使得 N 值增加,原因是如果对同一组接口(包括接口排列的顺序相同)试图重复创建动态代理类,它会很聪明地返回先前已经创建好的代理类的类对象,而不会再尝试去创建一个全新的代理类,这样可以节省不必要的代码重复生成,提高了代理类的创建效率。
4、类继承关系:该类的继承关系如图:
由图可见,Proxy 类是它的父类,这个规则适用于所有由 Proxy 创建的动态代理类。而且该类还实现了其所代理的一组接口,这就是为什么它能够被安全地类型转换到其所代理的某接口的根本原因。
JDK动态代理的缺点
JDK动态代理仅支持 interface 代理的桎梏,因为动态生成的代理类的继承自 Proxy,Java 的继承机制注定了这些动态代理类们无法实现对 class 的动态代理,原因是多继承在 Java 中本质上就行不通。
Javassist 实现动态代理
javassist 是一款非常优秀的Java 字节码引擎工具,能够在运行时编译、生成Java Class。
maven依赖:
org.javassist
javassist
3.21.0-GA
方式1
通过javassist.util.proxy.ProxyFactory
类来生成代理,代码如下:
public Object getProxy(Class> type) throws IllegalAccessException, InstantiationException {
ProxyFactory f = new ProxyFactory();
f.setSuperclass(type);
f.setFilter(new MethodFilter() {
public boolean isHandled(Method m) {
// ignore finalize()
return !m.getName().equals("finalize");
}
});
Class c = f.createClass();
MethodHandler mi = new MethodHandler() {
public Object invoke(Object self, Method m, Method proceed,
Object[] args) throws Throwable {
System.out.println("method name: " + m.getName()+" exec");
return proceed.invoke(self, args); // execute the original method.
}
};
Object proxy = c.newInstance();
((Proxy)proxy).setHandler(mi);
return proxy;
}
方式2
采用和Dubbo 框架中 com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
似的方式,首先定义ProxyFactory:
public interface ProxyFactory {
T getProxy(Object target, InvocationHandler handler) throws Throwable;
}
com.bytebeats.codelab.javassist.proxy.javassist.JavassistProxyFactory
代码如下:
import com.bytebeats.codelab.javassist.proxy.ProxyFactory;
import java.lang.reflect.InvocationHandler;
/**
* ${DESCRIPTION}
*
* @author Ricky Fung
* @date 2017-02-20 14:55
*/
public class JavassistProxyFactory implements ProxyFactory {
@Override
public T getProxy(Object target, InvocationHandler handler) throws Throwable {
return (T) ProxyGenerator.newProxyInstance(Thread.currentThread().getContextClassLoader(),
target.getClass(), handler);
}
}
最关键的 ProxyGenerator 代码如下:
package com.bytebeats.codelab.javassist.proxy.javassist;
import com.bytebeats.codelab.javassist.util.ClassUtils;
import javassist.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* ${DESCRIPTION}
*
* @author Ricky Fung
* @date 2017-03-18 14:55
*/
public class ProxyGenerator {
private static final AtomicInteger counter = new AtomicInteger(1);
private static ConcurrentHashMap, Object> proxyInstanceCache = new ConcurrentHashMap<>();
public static Object newProxyInstance(ClassLoader classLoader, Class> targetClass, InvocationHandler invocationHandler)
throws Exception {
if(proxyInstanceCache.containsKey(targetClass)){
return proxyInstanceCache.get(targetClass);
}
ClassPool pool = ClassPool.getDefault();
//生成代理类的全限定名
String qualifiedName = generateClassName(targetClass);
// 创建代理类
CtClass proxy = pool.makeClass(qualifiedName);
//接口方法列表
CtField mf = CtField.make("public static java.lang.reflect.Method[] methods;", proxy);
proxy.addField(mf);
CtField hf = CtField.make("private " + InvocationHandler.class.getName() + " handler;", proxy);
proxy.addField(hf);
CtConstructor constructor = new CtConstructor(new CtClass[]{pool.get(InvocationHandler.class.getName())}, proxy);
constructor.setBody("this.handler=$1;");
constructor.setModifiers(Modifier.PUBLIC);
proxy.addConstructor(constructor);
proxy.addConstructor(CtNewConstructor.defaultConstructor(proxy));
// 获取被代理类的所有接口
List> interfaces = ClassUtils.getAllInterfaces(targetClass);
List methods = new ArrayList<>();
for (Class cls : interfaces) {
CtClass ctClass = pool.get(cls.getName());
proxy.addInterface(ctClass);
Method[] arr = cls.getDeclaredMethods();
for (Method method : arr) {
int ix = methods.size();
Class> rt = method.getReturnType();
Class>[] pts = method.getParameterTypes();
StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
for(int j=0;j 0 )
sb.append(',');
sb.append(getParameterType(pts[i]));
sb.append(" arg").append(i);
}
sb.append(')');
Class>[] ets = method.getExceptionTypes(); //方法抛出异常
if( ets != null && ets.length > 0 )
{
sb.append(" throws ");
for(int i=0;i 0 )
sb.append(',');
sb.append(getParameterType(ets[i]));
}
}
sb.append('{').append(code.toString()).append('}');
CtMethod ctMethod = CtMethod.make(sb.toString(), proxy);
proxy.addMethod(ctMethod);
methods.add(method);
}
}
proxy.setModifiers(Modifier.PUBLIC);
Class> proxyClass = proxy.toClass(classLoader, null);
proxyClass.getField("methods").set(null, methods.toArray(new Method[0]));
Object instance = proxyClass.getConstructor(InvocationHandler.class).newInstance(invocationHandler);
Object old = proxyInstanceCache.putIfAbsent(targetClass, instance);
if(old!=null){
instance = old;
}
return instance;
}
private static String modifier(int mod) {
if( Modifier.isPublic(mod) ) return "public";
if( Modifier.isProtected(mod) ) return "protected";
if( Modifier.isPrivate(mod) ) return "private";
return "";
}
/**
* 数组类型返回 String[]
* @param c
* @return
*/
public static String getParameterType(Class> c) {
if(c.isArray()) { //数组类型
StringBuilder sb = new StringBuilder();
do {
sb.append("[]");
c = c.getComponentType();
} while( c.isArray() );
return c.getName() + sb.toString();
}
return c.getName();
}
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 "(" + getParameterType(cl) + ")"+name;
}
private static String generateClassName(Class> type){
return String.format("%s$Proxy%d", type.getName(), counter.getAndIncrement());
}
}
验证
被代理的接口:
import com.bytebeats.codelab.javassist.model.User;
import java.util.List;
/**
* ${DESCRIPTION}
*
* @author Ricky Fung
* @date 2017-02-20 15:16
*/
public interface HelloService {
void say(String msg);
String echo(String msg);
String[] getHobbies();
int insert(User user);
User getUser();
List getUser(String group, int age);
}
客户端代码:
final HelloService target = new HelloServiceImpl();
ProxyFactory factory = factory = new JavassistProxyFactory();
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
//打印日志
System.out.println("[before] The method " + methodName + " begins");
Object result = null;
try{
//前置通知
result = method.invoke(target, args);
//返回通知, 可以访问到方法的返回值
System.out.println(String.format("after method:%s execute", method.getName()));
} catch (Exception e){
e.printStackTrace();
//异常通知, 可以访问到方法出现的异常
}
//后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值
//打印日志
System.out.println("[after] The method ends with " + result);
return result;
}
};
HelloService proxy = factory.getProxy(target, handler);
System.out.println(proxy);
proxy.say("ricky");
proxy.echo("world");
proxy.getHobbies();
proxy.insert(new User());
proxy.getUser();
proxy.getUser("A", 23);
Ending!
参考资料
dubbo com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
javassist ProxyFactory