动态代理
学习黑马程序员的知识总结,文章代码并非原创,文字为自己总结所得
基于JDK的动态代理
使用:
proxy = Proxy.newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
第一个参数:类加载器,通常传入目标对象的类加载器即可:
eg: target.getClass().getClassLoader() 一般都是固定的这么写的,也可以传入null 使用默认加载器
第二个参数:***实现了接口***的字节码对象,通常传入
eg: target.getClass().getInterfaces() 一般也是固定这么写
第三个参数是重点关注对象,它传入了InvocationHandler对象
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
该对象只实现了应该invoke方法,proxy即为代理类对象,在动态代理中无需关注,会自己生成;
method参数,就是方法的反射,在原对象中执行上面方法,都会调用到invoke()方法中,调用形式为 method.invoke(object , args) object 为实际执行method的对象,args为存储实际方法参数的数组
使用示例:
代码1:OrderInterface接口
package jdk_proxy;
public interface OrderInterface {
public String order(String foodName);
public void test();
public void test2();
}
代码2:定义应该实现了 OrderInterface 接口的customer类
package jdk_proxy;
public class Customer implements OrderInterface{
@Override
public String order(String foodName) {
return "已经下单"+ foodName;
}
@Override
public void test() {
}
@Override
public void test2() {
}
}
代码3:动态代理的测试类
package jdk_proxy;
import java.lang.reflect.Proxy;
public class DynamicTest {
public static void main(String[] args) {
Customer customer = new Customer();
OrderInterface deliveryClerk = (OrderInterface) Proxy.newProxyInstance(
customer.getClass().getClassLoader(),
customer.getClass().getInterfaces(),
((proxy, method, args1) -> {
Object result = method.invoke(customer, args1);
System.out.println("接收到订单");
System.out.println("正在派送");
return result + "结束";
}));
String result = deliveryClerk.order("麻婆豆腐");
System.out.println(result);
}
}
基于上述使用示例剖析底层原理
上述示例的代理对象是在运行时自动生成的,接下来我们自己写一个代理对象,模仿JDK运行时,生成的代理对象的工作过程
package jdk_proxy.explain;
import jdk_proxy.OrderInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DeliveryClerk implements OrderInterface {
private InvocationHandler handler;
public DeliveryClerk(InvocationHandler handler){
this.handler = handler;
}
@Override
public String order(String foodName) throws Throwable {
Method method = OrderInterface.class.getMethod("order", String.class);
Object result = handler.invoke(this, method, new Object[] {foodName});
return (String) result;
}
@Override
public void test() {
try {
Method method = OrderInterface.class.getMethod("test");
Object result = handler.invoke(this, method, null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
@Override
public void test2() {
try {
Method method = OrderInterface.class.getMethod("test2");
Object result = handler.invoke(this, method, null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
可以看到改代理类和目标类(customer)是实现了相同的接口,并且构造函数传入了一个InvocationHandler对象,和上述动态代理中的第三个参数是一样的。
这个代理类实现了接口的所有方法,并且在实现每一个方法时都调用了InvocationHandler 对象的invoke()方法,这也就是为什么,只要代理对象调用方法时就一定会走到invoke()方法的原因。同上invoke()方法有3个参数,Object proxy, Method method, Object[] args
proxy即为代理对象,也就是我们写的代理对象自己,所以传入this即可;
method为方法对象,这里要使用反射机制,获取当前执行的方法代理方法;
args依然为当前方法的参数,所以order()方法中传入了foodName, 而两个test()方法,因为都没有参数所以传入null。
下面我们到动态代理测试代码中使用这个代理类,如果实现结果与上述代码相同,则说明我们自己写的这个代理类是能正确模拟JDK动态生成的代理类的。
package jdk_proxy;
import jdk_proxy.explain.DeliveryClerk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class DynamicTest {
public static void main(String[] args) throws Throwable {
Customer customer = new Customer();
// OrderInterface deliveryClerk = (OrderInterface) Proxy.newProxyInstance(
// customer.getClass().getClassLoader(),
// customer.getClass().getInterfaces(),
// ((proxy, method, args1) -> {
// Object result = method.invoke(customer, args1);
// System.out.println("接收到订单");
// System.out.println("正在派送");
// return result + "结束";
// }));
InvocationHandler handler = ((proxy, method, args1) -> {
Object result = method.invoke(customer, args1);
System.out.println("接收到订单");
System.out.println("正在派送");
return result + "结束";
});
OrderInterface deliveryClerk = new DeliveryClerk(handler);
String result = deliveryClerk.order("麻婆豆腐");
System.out.println(result);
}
}
最终我们可以看到他们的运行结果是一致的