前言
必须具备反射相关知识,Spring AOP、IOC都是基于动态代理实现的,如果要学习Spring源码最好学习下动态代理。
JDK 动态代理
代理设计模式的原理:
使用一个代理将对象包装起来,然后使用代理对象取代原始对象。任何对原始对象的调用都要经过代理对象代理。由代理对象决定是否以及何时将方法调用转到原对象上。
动态代理
- 动态代理是指客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
- 动态代理使用场景:
- 调试
- 远程方法调用
首先思考一下,实现动态代理,需要解决哪些问题?
- 如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象?
- 当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a?
解决方案
java.lang.reflect反射包下有一个Proxy类,一个InvocationHandler接口是我们实现动态代理的核心。
1 . InvocationHandler
该接口中仅定义了一个方法:public Object invoke(Object obj, Method method, Object[] args),在使用时,第一个参数obj一般是指代理类,method是被代理的方法,args为该方法的参数数组,即被代理类数组。这个抽象方法在代理类中动态实现。
2 . Proxy
该类即为动态代理类,static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h),返回代理类的一个实例,返回后的代理类可以当作被代理类使用。
动态代理步骤
- 创建被代理类及接口(JDK代理是接口代理)
- 创建Handle类实现 InvocationHandler接口 ,重写invoke方法
- 通过Proxy的newProxyInstance()方法获取代理类对象
- 通过代理类对象调用被代理类的方法
代码实现
1.创建接口
/**
* 人类接口
*
* @author junjun.lei
* @create 2020-03-26 19:31
*/
public interface Human {
/**
* 获取人的信仰
*
* @return 返回信仰
*/
String getBelief();
/**
* 吃东西
*
* @param food
*/
void eat(String food);
}
2.被代理类实现该接口
/**
* 被 代理类
* @author junjun.lei
* @create 2020-03-27 11:33
*/
public class SupperMan implements Human {
@Override
public String getBelief() {
return "I believe i can fly";
}
@Override
public void eat(String food) {
System.out.println("吃" + food);
}
}
- 创建Handle类实现 InvocationHandler接口 ,重写invoke方法,并通过Proxy类的newProxyInstance()方法获取代理类对象
/**
* 代理类
* 要想实现动态代理,需要解决的问题?
* 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
* 问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
*
* @author junjun.lei
* @create 2020-03-27 12:38
*/
public class MyInvocationHandle implements InvocationHandler {
/**
* 被代理类对象
*/
private Object obj;
public MyInvocationHandle(Object obj){
this.obj=obj;
}
/**
* 动态获取代理类对象
*
* @return
*/
public Object getProxyInstance() {
return Proxy.newProxyInstance(this.obj.getClass().getClassLoader(),this.obj.getClass().getInterfaces(),this);
}
/**
* 动态的去调用被代理类中的同名方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue = method.invoke(this.obj, args);
return returnValue;
}
}
4.动态代理测试
/**
* 动态代理测试
* @author junjun.lei
* @create 2020-03-27 13:08
*/
public class ProxyTest {
@Test
public void test01() {
SupperMan supperMan=new SupperMan();
MyInvocationHandle handle = new MyInvocationHandle(supperMan);
Human proxyInstance = (Human)handle.getProxyInstance();
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("盖浇饭");
}
}
5.执行结果