参考:https://www.bilibili.com/video/BV1Kb411W75N
使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
package com.a.java; /* 静态代理 特点:代理类和被代理类在编译期间被确定。 */ interface ClothFactory { void produceCloth(); } // 代理类 class ProxyClothFactory implements ClothFactory { private ClothFactory factory; // 用被代理类对象进行实例化 public ProxyClothFactory(ClothFactory factory) { this.factory = factory; } @Override public void produceCloth() { System.out.println("代理工厂做一些准备工作"); factory.produceCloth(); System.out.println("代理工厂做一些后续的收尾工作"); } } // 被代理类 class NikeClothFactory implements ClothFactory { @Override public void produceCloth() { System.out.println("Nike工厂生产一批运动服"); } } public class StaticProxyTest { public static void main(String[] args) { // 创建被代理类的对象 NikeClothFactory nike = new NikeClothFactory(); // 创建代理类的对象 ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike); proxyClothFactory.produceCloth(); } }
上面的代码首先声明了一个接口ClothFactory
,里面只有一个无返回值的方法produceCloth
。然后声明了一个代理类ProxyClothFactory
和一个被代理类NikeClothFactory
,它们均实现了ClothFactory
接口。其中,被代理类NikeClothFactory
的produceCloth
方法只输出一句话:“Nike工厂生产一批运动服”。代理类ProxyClothFactory
声明了一个ClothFactory
类型的私有变量factory
,并通过有参的构造方法对factory
进行实例化,这个factory
就是用被代理类对象进行实例化的。之后,代理类ProxyClothFactory
的produceCloth()
中调用了factory
的produceCloth()
,也就是被代理类的produceCloth()
。
main方法中先创建被代理类的对象,然后将被代理类的对象作为参数创建代理类的对象,最后调用代理类的produceCloth(),由于代理类的produceCloth()中也调用了被代理类的produceCloth(),所以被代理类的produceCloth()也被调用了。
上面的代码就是一个静态代理,它的代理类和被代理类都是写死的。
实现动态代理要解决2个问题:
第一个问题用Proxy.newProxyInstance方法解决,第二个问题通过实现InvocationHandler接口中的invoke方法实现。下面是介绍:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) Params: loader – 定义代理类的类加载器(与被代理类相同) interfaces – 代理类要实现的接口列表(与被代理类相同) h – 将方法调用分派到的调用处理程序 Returns: 返回一个代理类的对象
上面的第三个参数InvocationHandler是一个接口:
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
里面声明了一个invoke方法,有三个参数:
proxy – 调用该方法的代理实例
method – 代理类实现的接口中声明的方法
args – method的参数
返回值:执行method后的返回值
所以在动态代理的代码中,我们要自己写一个类来实现InvocationHandler 接口。
package com.a.java; /* 动态代理 */ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface Human { String getBelief(); void eat(String food); } // 被代理类 class SuperMan implements Human{ @Override public String getBelief() { return "I believe I can fly!"; } @Override public void eat(String food) { System.out.println("我喜欢吃" + food); } } /* 实现动态代理要解决的问题: 1. 如何根据加载到内存中的被代理类动态地创建一个代理类及其对象? 2. 当通过代理类的对象调用方法a时,如何动态地去调用被代理类中的同名方法a? */ class ProxyFactory { // 通过调用此方法,返回一个代理类的对象,解决问题1 public static Object getProxyInstance(Object obj) { // obj: 被代理类的对象 MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler); } } class MyInvocationHandler implements InvocationHandler { private Object obj; // 需要使用被代理类的对象进行赋值 public void bind(Object obj) { this.obj = obj; } // 当通过代理类的对象调用方法a时,就会自动地调用下面的invoke() // 将被代理类要执行的方法a的功能就声明在invoke()中 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // method: 代理类对象调用的方法,此方法也作为被代理类对象要调用的方法 // obj: 被代理类的对象 Object returnValue = method.invoke(obj, args); // 代理类对象调用的方法,也即被代理类对象要调用的方法的返回值就作为当前类中的invoke()的返回值 return returnValue; } } public class ProxyTest { public static void main(String[] args) { SuperMan superMan = new SuperMan(); // proxyInstance: 代理类的对象 Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan); // 当通过代理类对象调用方法时,会自动调用被代理类中同名的方法 String belief = proxyInstance.getBelief(); System.out.println(belief); proxyInstance.eat("四川麻辣烫"); System.out.println("******************************************"); NikeClothFactory nikeClothFactory = new NikeClothFactory(); ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory); proxyClothFactory.produceCloth(); } }
上面的代码首先声明一个Human接口,然后被代理类SuperMan和ProxyFactory类实现该接口,ProxyFactory中的getProxyInstance方法会动态地返回一个代理类的对象,这解决了问题1。具体实现是通过Proxy.newProxyInstance方法,这是一个系统内置类的方法,有3个参数,第一个参数是与被代理类相同的ClassLoader,第二个参数是被代理类实现的接口,第三个参数是InvocationHandler类型,是一个接口。因此我们还要自己写一个类来实现InvocationHandler接口,就是后面写的MyInvocationHandler类。MyInvocationHandler类里面实现了invoke方法,invoke方法的第二个参数method是代理类对象调用的方法,此方法也作为被代理类对象要调用的方法,第三个参数是method的参数,有了方法和参数还需要有对象,要建立起被代理类的对象与MyInvocationHandler类的联系,因此声明了一个obj属性并用bind方法将被代理类的对象赋值给obj。然后在invoke方法里执行obj的method方法,并将结果返回。这样就解决了问题2,完成了动态代理的实现。
main方法中先实例化被代理类SuperMan,然后使用ProxyFactory.getProxyInstance(superMan)创建代理类的对象,之后当通过代理类对象调用getBelief和eat方法时,会自动调用被代理类中同名的方法。后面又实例化了NikeClothFactory类,然后再通过ProxyFactory.getProxyInstance(nikeClothFactory)创建一个代理类的对象,又可以执行NikeClothFactory类的produceCloth方法了。这就体现了动态代理的动态性。