Java代理

本文介绍了代理模式在Java中的应用,包括静态代理(如通过接口实现的Fightable接口)和动态代理(利用InvocationHandler实现)。文章以生活中的房屋中介为例,阐述了代理如何增强功能和控制访问,并展示了静态代理和动态代理的代码示例。
摘要由CSDN通过智能技术生成

代理是什么

代理,可以通俗理解为中介/中间人,其实非常简单

生活中存在相当多代理的概念,房产中介,股票经纪人,基金经理,商标代理等

Java当中的代理,其内核也是这样的一个概念:

由代理类创建的代理对象,去代理目标类创建的目标对象
以下我们将代理对象简称为proxyObj,将目标对象简称为targetObj

代理的作用

以房屋中介为例,中介作为买房者和房东之间的中间人,是房东的出售/出租房屋的代理

1. 增强功能

没有房屋中介这个代理之前,房东需要自己去寻找租户,在茫茫租房者中,寻找一个觉得价格合适,位置合适,硬件设施合适的租户,非常耗费时间和精力;
有了房屋中介之后,中介有更好的推广渠道,也拥有更专业的沟通技巧;中介可以帮房东做筛选,过滤掉那些谈不拢的租户;中

介可以拟定更为专业的购房合同……

综上,房东找寻租户的能力变强了,这显然归功于房屋中介。

2. 访问控制

没有中介之前,不管什么样的租户都可以直接接触到房东,可能每天都要和几十名潜在租户沟通

现在有了中介,如果租户想要联系房东,必须先经过中介的“校验”,确认你有租房意愿,基本上没什么太大的变动之后,才会作为

中间人,将租户和房东联系起来。

这显然加强了租户对房东的访问限制

当然,代理的功能其实非常强大,还有一些涉及到解耦,懒加载等功能,本文不对此做深入讲解,读者可以再去搜索并了解!

Java中代理的实现方式

在这里插入图片描述
静态代理,简单来说就是,targetObj和proxyObj之间1对1,1对多的关系

动态代理,则是1个proxyObj,对应任意数量的targetObj(1对多

静态代理的1对多,和动态代理的1对多,不是一个概念,我们下面用代码来阐述

不管哪种代理,大道至简,我们需要做的事情都是只有4件:

  1. 创建targetObj
  2. 创建proxyObj
  3. 明确要代理的targetObj的方法
  4. 执行proxyObj的方法间接调用targetObj的被代理方法

静态代理

静态代理借助Java的接口实现

Fightable接口

// 连接目标类和代理类的桥梁:接口
public interface Fightable {

    void fight(Integer power);

}

目标类,实现Fightable接口

// 静态代理目标类
public class Hero implements Fightable {

    @Override
    public void fight(Integer power) {
        System.out.println("英雄战斗力为" + power);
    }
}

代理类,也去实现Fightable接口,目的是明确要代理的targetObj的方法

public class HeroProxy implements Fightable {

    private Hero hero = new Hero();

    @Override
    public void fight(Integer power) {
        System.out.println("hero被代理……");
        power++;
        hero.fight(power);
    }
}

执行proxyObj的方法间接调用targetObj的被代理方法

public class ToRun {

    public static void main(String[] args) {

        HeroProxy heroProxy = new HeroProxy();

        heroProxy.fight(6);
		// hero被代理……
		// 英雄战斗力为6
		
    }
}

我们发现hero对象的方法被执行,同时增加了一些heroProxy自己的业务代码,增强了hero的战斗力(功能)

上述的代理,有一个很大的缺点,就是如果当代理类创建的targetObj多起来之后,实现代理就会变得非常复杂

比如下面这两种方式:

1. 静态代理多对多

在这里插入图片描述
每多一种目标类,就会多一种目标类

在这里插入图片描述

2. 静态代理1对多

在这里插入图片描述

ABCProxy

public class ABCProxy implements A1, B1, C1 {

    private A a = new A();
    private B b = new B();
    private C c = new C();

    @Override
    public void a1() {

        System.out.println("ABCProxy实现了A1,B1,C1接口");
        a.a1();

    }

    @Override
    public void b1() {
        System.out.println("ABCProxy实现了A1,B1,C1接口");
        b.b1();
    }

    @Override
    public void c1() {
        System.out.println("ABCProxy实现了A1,B1,C1接口");
        c.c1();
    }
}

ToRun类

public class ToRun {

    public static void main(String[] args) {

        ABCProxy abcProxy = new ABCProxy();

        abcProxy.a1();
        abcProxy.b1();
        abcProxy.c1();
    }
}

这种写法,虽然也是一种一对多,但是却是一种非常笨的1对多,假设我有100个需要代理的类对象,

你总不能让代理类实现100个接口吧?那样的话,代码可读性和可维护性会非常差,最终变成屎山代码

在这里插入图片描述

动态代理

接下来就要讲到我们聪明的动态代理了

动态代理,proxyObj是自动生成的

我们先来看一种过渡的动态代理情况,这种写法并没有实现动态代理(proxyObj仍然需要我们自己创建),但是可以帮助我们初步理解动态代理的写法,让学习曲线不那么陡峭

不是jdk动态代理的一种“动态代理”

在这里插入图片描述
和静态代理的一对多,其实是很相似的,不同的点在于,代理类的proxyObj不是一个对象,而是多个对象

在这里插入图片描述
一对多的静态代理
在这里插入图片描述
我们很容易能看出区别

这样做的好处是,不需要让代理类同时实现目标类实现的所有接口,非常简便

ClothesFactoryBuilder接口

public interface ClothesFactoryBuilder {
    void prdClothes(String color);
}

目标类ClothesFactory,实现了ClothesFactoryBuilder接口

public class ClothesFactory implements ClothesFactoryBuilder {
    @Override
    public void prdClothes(String color) {
        System.out.println("服装厂可以生产" + color + "颜色的衣服!");
    }
}

ShoesFactoryBuilder接口

public interface ShoesFactoryBuilder {

    void prdShoes(int size);
}

目标类ShoesFactory,实现了ShoesFactoryBuilder接口

public class ShoesFactory implements ShoesFactoryBuilder {
    @Override
    public void prdShoes(int size) {
        System.out.println("鞋厂能生产鞋码为" + size + "码的鞋");
    }
}

代理类FactoryProxy

public class FactoryProxy {

    // jdk动态代理也是依靠接口来实现的,和静态代理类似

    // 定义被代理的对象
    public Object targetObject; // 用Object,避免了静态代理创建多个目标类对象

    public FactoryProxy(Object o) {
        this.targetObject = o;
    }

}

使用Object当作代理对象,这样不用写多个代理对象,A类的对象作为A构造方法的参数创建代理对象,A类就会被代理

调用main方法

public class ToRun {

    public static void main(String[] args) {
        ShoesFactory shoesFactory = new ShoesFactory();
        ClothesFactory clothesFactory = new ClothesFactory();
        // 用户手动创建代理对象和目标对象,完成动态代理(代理类FactoryProxy创建多个代理对象代理目标类的对象)
        FactoryProxy shoesProxy = new FactoryProxy(shoesFactory);
        FactoryProxy clothesProxy = new FactoryProxy(clothesFactory);
        
        shoesTargetObject.prdShoes(100);
        clothesTargetObject.prdClothes("red");
    }
}

这里我们已经能清晰的看到,创建了多个proxyObj,分别代理两个目标类的targetObj

基于JDK的动态代理(利用接口)

上面那种写没有实现动态代理,因为目标类的方法没有得到“增强”,刚才的写法甚至代理类完全不清楚代理什么方法,所以代理类也无法增强

jdk动态代理,主要依赖的反射机制实现,采用了反射当中的Proxy包的方法

目标类和目标方法的写法不变,重点是代理类的写法

代理类FactoryProxy

public class FactoryProxy implements InvocationHandler {

    // jdk动态代理也是依靠接口来实现的,和静态代理类似

    // 定义被代理的对象
    private Object targetObject; // 用Object,避免了静态代理创建多个私有目标类对象的问题

    public FactoryProxy(Object o) {
        this.targetObject = o;
    }

    // 用反射获取interface对象,进而获取到需要代理增强的方法,避免代理类实现多个接口的问题
    public Object getInterface() {
        // 固定写法
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);//this是一个实现了InvocationHandler接口的对象
    }

    // jdk帮我们实现的,能够让我们调用目标类的核心方法(接口方法)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 功能增强的业务代码.因为有targetObject的存在,通过反射可以执行几乎目标类的任何方法
        if (targetObject instanceof ShoesFactory) {
            System.out.println("代理了ShoesFactory类的对象");
        } else if (targetObject instanceof ClothesFactory) {
            System.out.println("代理了ClothesFactory类的对象");
        } else {
            System.out.println("代理了" + targetObject.getClass().getName() + "类的对象");
        }

        // 传入目标类
        return method.invoke(targetObject, args);
    }
}

InvocationHandler在Spring框架实现中被大量使用,我们如果能理解透彻InvocationHandler,后续自己手写AOP的实现,理解Spring等肯定也都不是问题

getInterface方法让我们获取到目标类的targetObj的需要被代理的方法

invoke方法让我们能够执行targetObj的方法,只不过是通过接口来调用,而不是通过targetObj来调用

我们再来看最终运行的写法

这里用到了IOC,也就是控制反转,读者在这里可以把它理解成帮用户自动创建targetObj的一种机制

public static void main(String[] args) {
        // 用IOC(控制反转)避免用户创建targetObj

        // 让Spring帮我们创建目标类的对象,我们只需要创建代理类的对象即可,用代理类的对象代理目标类的对象

        // 反射获取目标类的核心方法的执行接口对象

        ShoesFactoryBuilder shoesFactoryBuilder =
                (ShoesFactoryBuilder) new FactoryProxy(IOC.shoesFactory).getInterface();

        shoesFactoryBuilder.prdShoes(100);

        System.out.println("============================");

        ClothesFactoryBuilder clothesFactoryBuilder =
                (ClothesFactoryBuilder) new FactoryProxy(IOC.clothesFactory).getInterface();

        clothesFactoryBuilder.prdClothes("red");

        System.out.println("============================");

        Fightable fightable = (Fightable) new FactoryProxy(IOC.hero).getInterface();

        fightable.fight(5);
    }

我们只需要新建一个FactoryProxy对象,在其中传入一个早已被IOC创建好的targetObj,就可以代理任何类的任何对象,执行任何接口中的方法

进行增强,进行访问控制

在这里插入图片描述

cjlib的代理,读者可以自己后续搜索,原理都是相同的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值