什么是代理?

举个例子,蔡徐坤要开演唱会,那么他需要布置场地,宣传,收钱等工作。这么多繁杂的工作让他一个人做肯定是不的,所以就请了一个经纪人,也就是代理,让经纪人去干这些繁琐的事。而蔡徐坤只需要唱歌就可以了。而这个经纪人也必须代表蔡徐坤演唱会 (也就是拥有委托类的所有方法)。那么,代理模式就是代理类与委托类有相同的接口,代理类是不修改委托类代码的前提下对类的功能进行扩展。

静态代理

比较容易理解,写个代码就知道了。先定义一个接口

interface a1{ //定义一个接口
public void cell();
}

然后再写一个委托类。

class a2 implements a1 { //设置委托类来重写方法
public void cell(){

System.out.println("方法被重写了");
}
}

然后再写一个代理类,代理类也继承于接口。

class ProxyClass implements a1{ //设置代理类来重写方法
private a2 m1;
public ProxyClass(a2 m1){
this.m1 = m1;
}

@Override
public void cell() {
m1.cell();
System.out.println("方法又被重写了");
}
}

引入委托类的实例对象。最后写个测试类

public class test03 { //测试类
public static void main(String[] args){
a1 m1 = new a2();
System.out.println("未代理之前:");
m1.cell();
a1 m2 = new ProxyClass((a2) m1);
System.out.println("代理之后:");
m2.cell();
}
}

将委托类的实例对象传入代理类的构造函数中,得到输出:

这样代理就对类功能进行了扩展,但是静态代理有很多缺点:如果有一百个对象需要代理,那么就需要实现一百个代理类,如果一个接口需要增加一个方法,那么原实现类和代理类都需要增加这个方法,造成代码臃肿。这时就有了动态代理。

动态代理

如何在不编写代理类的情况下创建代理实例,是动态代理解决的问题。在此之前复习一下对象的创建,在加载类的过程中,类加载器会将Class字节码文件加载到内存中,在堆中创建对应的class类对象,该Class类对象拥有该类的所有成员变量,构造器,字段等。再由Class类对象配合构造器创建对象实例。

至于什么是Class类对象,贴上如下解释:

  • Class类也是类的一种,只是名字和class关键字高度相似
  • Class类的对象是创建的类的类型信息,比如创建一个cat类,那么java就会生成一个内容是cat类的Class类对象。
  • 在cat类加载时会为每个类生成一个Class类的对象在堆中,每个cat类型的实例都要通过这个Class对象来进行实例化
  • 无论一个类有多少实例对象,在JVM中都只有一个Class对象

相关文章:到底什么是Class对象呢

说到这里,大概有些思路,我们可以通过代理Class类来创建代理对象实例,绕过代理类的编写。怎么直接获取代理Class类?代理类和委托类都共用一套接口,我们可以通过接口来获取代理类需要的信息。但是接口不能创建对象,该怎么办?不用慌,jdk提供了相关类与API,接下来要说的就是Proxy.getProxyClass()方法。

#import java.lang.reflect.Proxy;
Proxy.getProxyClass(a1,b1)

参数a1为类加载器,b2为公用的接口。怎么理解呢?个人感觉像是一种copy操作,将接口Class类对象的类结构信息copy到一个新的Class对象上,然而不同的是,这个新的Class对象存在构造器,能够创建对象实例,也就是代理实例。

接下来就是通过代理Class对象创建代理实例了。需要使用InvocationHandler接口,创建调用处理器,每次调用代理对象的方法,最终都会调用InvocationHandler类的invoke方法。怎么一回事呢?从代理实例调用方法,会先调用invoke方法,再调用委托类对象的方法。写个例子就好懂多了。

首先创建个接口。

interface hobby{
public String eat();
public void tag();
public String play();
}

接着创建一个委托类来重写方法。

class person implements hobby{

@Override
public void eat(String food) {
System.out.println("我喜欢的食物是"+food);
}

@Override
public void tag() {
System.out.println("我的口头禅是666");
}

@Override
public void play(String toy) {
System.out.println("我喜欢的玩具是"+toy);
}
}

接着就是重点,创建一个继承于InvocationHandler接口的实现类,重写invoke方法。

class personProxy implements InvocationHandler {
private Object m1;
public personProxy(){
}
public personProxy(Object m1){
this.m1 = m1;//接收委托类对象
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
proxy:代理类对象,method:调用的什么方法,args:调用方法的参数
*/
System.out.println("invoke以被调用");//设置标志
//方法调用
Object value = method.invoke(m1,args);
return value;
}
}

然后创建一个专门生产代理对象的工厂,

class Proxyfactory{
//定义一个静态方法
public static Object getProxy(Object obj){
personProxy a1 = new personProxy(obj);
//通过反射创建代理类实例
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),a1);
}
}

最后就是测试类了。

public class test04 {
public static void main(String[] args){
hobby m1 = new person();
hobby m3 = (hobby) Proxyfactory.getProxy(m1);
m3.eat("土豆");
m3.tag();
m3.play("变形金刚");
}
}

运行结果为:

可以发现每次调用代理类的方法都会去调用invoke。此篇文章记录自己对于代理机制的学习与感悟,了解更多细节知识参考以下文章,真心写的不错。

参考链接

https://www.zhihu.com/question/40536038

https://zhuanlan.zhihu.com/p/65501610