JDK的动态代理实现的原理其实是动态生成Proxy的.java文件,再动态编译.java文件成为对应的.class文件,再通过ClassLoader将字节码对象加载到内存中从而实现动态的效果。现在主要是测试一下如何使用JDK的动态代理,不做原理的分析。如果想了解原理可以观看
马士兵的设计模式之动态代理
深入剖析。
package cn.shaines.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
/**
* 定义一个接口
*/
interface MyInterface {
int getInt(int i);
void close();
}
/**
* 接口实现类
*/
class MyInterfaceImpl implements MyInterface {
@Override
public int getInt(int i) {
return i;
}
@Override
public void close() {
System.out.println("==>>close()");
}
public void otherMethod(){
System.out.println("==>>otherMethod()");
}
}
/**
* 测试类JDK代理
*/
public class Test {
/**
* 反射执行方法
* @param o 调用对象
* @param methodName 方法名称
* @param parameterTypes 参数类型.class
* @param parameters 参数实体
*/
public static <R> R methodInvoke(Object o, String methodName, Class<?>[] parameterTypes, Object[] parameters) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
return (R) ((parameterTypes == null) ? o.getClass().getMethod(methodName).invoke(o) : o.getClass().getMethod(methodName, parameterTypes).invoke(o, parameters));
}
// 入口main
public static void main(String[] args) {
// 使用JDK提供的动态代理方法步骤如下:
// 步骤01: 创建需要代理的实体类对象
MyInterfaceImpl myInterfaceImpl = new MyInterfaceImpl();
// 步骤02: 获取实体类对象的Class对象
Class<? extends MyInterfaceImpl> clazz = myInterfaceImpl.getClass();
// 步骤03: 调用JDK提供的Proxy.newProxyInstance()方法
Object proxyInstance = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 步骤04: 动态修改getInt()方法
if (method.getName().equals("getInt")){
System.out.println("代理类收到的参数是:" + args[0]);
return 100;
}
// 步骤05: 注意这里的实际调用必须是实现类对象
return method.invoke(myInterfaceImpl);
}
});
// cn.shaines.test.MyInterfaceImpl
System.out.println(clazz.getClass().getName());
// java.lang.Class
System.out.println(clazz.getName());
// 步骤06: 使用代理对象(强制转换为接口对象MyInterface,不可以强制转换为MyInterfaceImpl,
// 通过上面的打印可以知道proxyInstance的class和MyInterfaceImpl的class是不同的,强制转换会报错)
// MyInterfaceImpl myInterfaceImpl2 = ((MyInterfaceImpl) proxyInstance);// 报错
MyInterface myInterface1 = ((MyInterface) proxyInstance);
int anInt = myInterface1.getInt(20);
System.out.println("anInt:" + anInt);// anInt:100 被动态修改值
myInterface1.close();
// 放射执行方法,无需强制转为对象都可以使用proxyInstance调用任意方法
try {
int i = methodInvoke(proxyInstance, "getInt", new Class[]{int.class}, new Object[]{10});
methodInvoke(proxyInstance, "close", new Class[]{}, new Object[]{});
// methodInvoke(proxyInstance, "otherMethod", new Class[]{}, new Object[]{});
// 这个方法会报错:java.lang.NoSuchMethodException: cn.shaines.test.$Proxy0.otherMethod()
// 找不到该方法,原因很简单,该动态代理对象是实现接口(MyInterface),不是继承接口实现类(MyInterfaceImpl),所以只有接口的方法,没有具体实现对象(myInterfaceImpl)的方法
} catch (Exception e) {
e.printStackTrace();
}
}
}