3.动态代理
-
动态代理的角色和静态代理的一样 .
-
动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
-
动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
-
- 基于接口的动态代理----JDK动态代理
- 基于类的动态代理–cglib
- 现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
- 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!、
JDK的动态代理需要了解两个类
核心 : InvocationHandler 和 Proxy , 打开JDK帮助文档看看
【InvocationHandler:调用处理程序】
Object invoke(Object proxy, 方法 method, Object[] args);
//参数
//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
【Proxy : 代理】
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
4.实例详解动态代理
package com.ningxiao.atguigu;
/*
*author :wt
*@create 2020-09-25 15:38
*/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Human{
String getBelieve();
void eat(String food);
}
//被代理类
class SuperMan implements Human{
public String getBelieve() {
return "我思故我在";
}
public void eat(String food) {
System.out.println("我喜欢吃"+food);
}
}
/*
* 要想实现动态代理,需要解决的问题?
* 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
* 问题二:当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法。
*
* */
class ProxyFactory{
//调用此方法,返回一个代理类对象,解决问题一
public static Object getProxyInstance(Object object){//object被代理类对象
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(object);
//参数一:我们创建的代理类是那个类的加载器加载的(获取被代理类的类的加载器)
//这里是说obj是那个类的加载器加载的,我就和你一样
//参数二:被代理类实现了那个接口的类的加载器(根据被代理类实现的接口,
// 我们创建的代理类也跟着实现哪些接口)
//参数三:其实是在解决问题二,(我们需要去定义一个接口的实现类)
return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),handler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;//赋值时也需要根据被代理类进行初始化
public void bind(Object obj){//这个方法相当于对我们所要代理的对象进行实例化
this.obj = obj;
}
//当我们通过代理类的对象 ,调用方法a时,就会自动的调用如下的方法:invoke()
//将被代理类要执行的方法a的功能声明在invoke()中,其实解决的是问题二
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("准备工作1");
//参数一:被代理类对象
//参数二:被代理类的方法参数(代理类对象对被代理类的方法执行除了被代理类对象以外还有参数)
Object returnValue = method.invoke(obj, args);//method即为代理对象调用对的方法,此方法也就作为了被代理类要执行的方法
System.out.println("收尾工作");
return returnValue;//method的返回值(其实也是被代理类的返回值)作为invoke()方法的返回值
}
}
public class ProxyTest {
public static void main(String[] args) {
//创建一个被代理类
SuperMan superMan = new SuperMan();
//返回值是一个Object对象,但是在我们这个示例中试试Human对象,因此,我们对其进行强转
// Object proxyInstance = ProxyFactory.getProxyInstance(superMan);
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);//返回的其实是代理类对象
proxyInstance.eat("战斧牛排");
String believe = proxyInstance.getBelieve();
System.out.println(believe);
}
}
-
过程分析
-
1.所谓的动态代理,其实就是在运行的过程中生成一个代理类,
-
**注意:**我们说静态代理,其实是我们在程序运行之前,我们实实在在写好的一个类,我们在使用被代理类额时候对代理类进行了调用,然后动态代理与静态代理实实在在的区别就在于我们事先是不用写代理类的,是在程序运行的过程中,程序调用一系列的方法,帮我们创建的代理对象
-
我们现在一步一步分析,我们是 如何通过代码实现的通过程序帮我们创建代理类的。
-
首先,我们应该有一个被代理类和接口
interface Human{ String getBelieve(); void eat(String food); } //被代理类 class SuperMan implements Human{ public String getBelieve() { return "我思故我在"; } public void eat(String food) { System.out.println("我喜欢吃"+food); } }
-
然后我们接下来的任务就是通过代码实现动态代理的创建
我们首先思考两个问题:
-
我们如何才能根据被代理类创建一个代理类
-
我们如何根据这个代理类调用被代理类的方法
-
-
那我们首先解决第一个问题
我们首先创建一个类,通过它的方法来生成代理对象
class ProxyFactory{ //调用此方法,返回一个代理类对象,解决问题一 public static Object getProxyInstance(Object object){//object被代理类对象 return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),handler); } }
这个类是为了返回一个代理类,我们创建一个方法,这里是说object是向方法传递一个被代理类
public static Object getProxyInstance(Object object){ }
在这个方法中我们用到了Proxy类,
import java.lang.reflect.Proxy;
这个类的如下方法,就是在创建代理类
newProxyInstance();
我们来说一下这个方法的参数
- 参数一:我们创建的代理类是那个类的加载器加载的(获取被代理类的类的加载器)这里是说obj是那个类的加载器加载的,(创建代理类我们得知被代理类是谁)
- 参数二:被代理类实现了哪个接口的类的加载器(根据被代理类实现的接口,我们创建的代理类也跟着实现哪些接口)(创建代理类我们得知道被代理类实现了哪个接口)
- 参数三:其实是在解决问题二,(我们需要去定义一个接口的实现类)(我们得和你的方法对接)
因此我们现在也可以传递两个参数了
Proxy.newProxyInstance(object.getClass().getClassLoader(),);
还差一个参数这个参数类型是 InvocationHandler接口,为了传递这个参数,我们首先得实现一个接口
-------------------------------------------------------------------------------------------------------------------------------------------/
从这里开始到下一个分割线,其实都是在说第三个参数
class MyInvocationHandler implements InvocationHandler{ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { } }
在接口的实现类中有invoke()方法,在我们创建代理类之后,代理类如何才能执行被代理类的方法呢?代理类就是通过这个方法实现的对被代理类方法的调用,我们如何写?
class MyInvocationHandler implements InvocationHandler{ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { } }
在invoke()中,我们调用method方法的invoke()方法
method.invoke()
这个方法的第一个参数应该传递被代理类对象,第二个参数传递的是所要执行方法的参数
但是我们现在没有被代理类,该怎么办,我们需要外接传一个被代理类(我们私有化一个属性,给其赋值),然后我们就有了method.invoke()方法的第一个参数
class MyInvocationHandler implements InvocationHandler{ private Object obj;//赋值时也需要根据被代理类进行初始化 public void bind(Object obj){//这个方法相当于对我们所要代理的对象进行实例化 this.obj = obj; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { method.invoke(obj,args) } }
我们也就有了创建代理类时需要的第三个参数
-------------------------------------------------------------------------------------------------------------------------------------------/
但是我们首先应该实例化第三个参数
MyInvocationHandler handler = new MyInvocationHandler();
有了实例化的参数,我们根据传进来的被代理类对象,给MyInvocationHandler类中的obj属性传参,也就是
handler.bind(object);
然后将第三个参数传进去
class ProxyFactory{ //调用此方法,返回一个代理类对象,解决问题一 public static Object getProxyInstance(Object object){//object被代理类对象 MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(object); return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),handler); } }
-
最后我们测试
public class ProxyTest { public static void main(String[] args) { //1.创建一个被代理类 SuperMan superMan = new SuperMan(); //2.动态生一个代理类 //返回值是一个Object对象,但是在我们这个示例中试试Human对象,因此,我们对其进行强转 // Object proxyInstance = ProxyFactory.getProxyInstance(superMan); Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);//返回的其实是代理类对象 //3.调用其中的方法 proxyInstance.eat("战斧牛排"); String believe = proxyInstance.getBelieve(); System.out.println(believe); } }
-