Java 中的动态代理使用方法
使用方法
一、Child.class
package proxys;
public interface Child {
void eat();
}
二、ChildImpl.class
package proxys;
public class ChildImpl implements Child {
@Override
public void eat() {
System.out.println("Child eating...");
}
}
三、MyProxy.class
package proxys;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 基于接口的动态代理: Proxy, InvocationHandler
*/
public class MyProxy {
/**
* Test Method
* @param args 参数列表
*/
public static void main(String[] args) {
// 未使用代理模式
ChildImpl child = new ChildImpl();
child.eat();
// 使用动态代理添加打印信息
System.out.println();
Child proxyObj = getProxyObj(child, Child.class);
proxyObj.eat();
}
/**
* 获取代理对象
* @param obj 被代理的对象
* @param interfaceType 被代理方法所属的接口类型
* @return 代理对象
*/
public static <I> I getProxyObj(Object obj, Class<I> interfaceType) {
// 接口类型检查, 因为 Proxy 是基于接口的动态代理
Class<?>[] proxyInterfaces = null;
Class<?>[] interfaces = obj.getClass().getInterfaces();
for (Class<?> anInterface : interfaces) {
if (anInterface.equals(interfaceType)) {
proxyInterfaces = new Class[]{anInterface};
break;
}
}
if (null == proxyInterfaces) {
throw new ClassCastException("无法为对象 " + obj + " 创建 " + interfaceType.getName() + " 代理");
}
// 核心代码
Object o = Proxy.newProxyInstance(obj.getClass().getClassLoader(), proxyInterfaces,
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 增强调用前的功能
System.out.println("[" + method.getName() + "] Proxy begin...");
// 调用被代理对象的方法
Object ret = method.invoke(obj, args);
// 增强调用后的功能
System.out.println("[" + method.getName() + "] Proxy end...");
return ret;
}
});
return (I) o;
}
}
运行结果:
Child eating...
[eat] Proxy begin...
Child eating...
[eat] Proxy end...
实现方式
- JDK 中基于接口的动态代理方式: Proxy + InvocationHandler
- 原理: 生成实现了指定接口列表的实现类
- 缺点: 必须借助接口才能产生代理目标对象
- CGLib(Code Generation Library)
- 原理: 生成继承自目标类的实现类
- 缺点: 无法代理 final 关键字修饰的类
其中 JDK Proxy 是通过直接生成字节码的方法来创建代理对象的,而 CGLib 是使用 ASM 操作字节码来创建代理对象的,因此在代理对象的创建上 JDK Proxy 的效率是更高的。但是在动态代理对象的方法调用上,JDK Proxy 是通过反射来完成的,而 CGLib 则是直接通过生成的字节码来调用目标方法的,因此在动态代理对象的方法调用上,CGLib 的效率是更高的。