Java 动态代理
参考: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个问题:
- 如何根据加载到内存中的被代理类动态地创建一个代理类及其对象?
- 当通过代理类的对象调用方法a时,如何动态地去调用被代理类中的同名方法a?
第一个问题用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方法了。这就体现了动态代理的动态性。