Java 代理模式:保护代理中的静态代理和动态代理

什么是代理模式

代理模式用于控制和管理访问。

举个例子,在没有经纪人的情况下,导演、编剧、粉丝A、粉丝B都想和演员见面,演员不得不直接和他们沟通:或拒绝、或接受,严重干扰了自己的生活。因此,他为自己找了一个经纪人,现在所有的请求都要通过经纪人才能到达演员,经纪人会对这些请求先进行处理,比如导演编剧可以和演员见面、粉丝不能和演员见面。这个经纪人就相当与代理模式中的保护代理。(保护代理、远程代理、虚拟代理等都属于代理模式)

保护代理的实现方式有两种:静态代理和动态代理。

先体验静态代理

静态代理的实现很简单。以汽车行驶为例,Car 实现了Moveable接口,拥有move方法。

public interface MoveAble {
    void move();
}
public class Car implements MoveAble {
    @Override
    public void move() {
        System.out.println("汽车行驶中..");
    }
}

现在我希望外界调用car的move方法前后做一些其他的行为(如权限验证、输出日志等),那么,我将这些工作交由car的代理者CarProxy处理,car仍然只处理核心的move方法。

public class CarProxy implements MoveAble{

    MoveAble car;

    public CarProxy(MoveAble car) {
        this.car = car;
    }

    @Override
    public void move() {
        System.out.println("权限控制");
        car.move();
        System.out.println("输出日志");

    }
}

有了代理者之后,我们就可以通过CarProxy来执行move方法了,因为Car和carProxy都实现了Moveable接口(接口的作用就是为了保持类型一致)。

public class Test {

    public static void main(String[] args) {
        Car car = new Car();
    
        CarProxy carProxy = new CarProxy(car);
        carProxy.move();
    }
}

运行结果

权限控制
汽车行驶中..
输出日志

好了,静态代理就这么简单。

静态代理存在的隐患

我们知道,唯一不变的就是变化。当你春风得意之时,任务又来了:你要为实现了Flyable接口的Dark也实现以上一样的功能(权限控制,输出日志)。那还不简单,我再写一个DarkProxy不就好了。嗯,后来领导又说,你这个权限控制、输出日志做的很不错嘛,现在你把这个项目中大大小小100个类都做个权限控制、输出日志吧??

由此可见,静态代理固然简单明了,但由于在静态代理中,代理对象(CarProxy)和被代理对象(Car)直接绑定(因为我们在CarProxy中直接声明了一个Car的引用),在编译时这种关系就已经确定下来,耦合程度太高,不利于代码复用。因此,我们需要动态代理。

动态代理登场

动态代理的动态指的是什么呢?
动态并非指在运行时才实例化代理对象,而是指在运行时才将代理类创建出来,也就是说,在运行时创建一个不存在的类!正是因为如此,代理类才能和被代理类解耦,在编写代码的时候,我并不关心被代理的对象到底是什么(Car也好、Duck也好,无所谓,在代理类中统统都是Object类型的),我只关心在调用原有的方法前后要做什么。

怎么做呢?

我只要在一个实现了InvocationHandler接口的类(比如叫MyHandler)的invoke方法中写这些需要添加的功能就够了。需要注意的是,MyHandler并不是代理类,MyHandler关注的是原有方法被调用时要做些什么(如权限控制、日志输出等),它是辅助代理的类。而代理类是在运行时通过Proxy.newProxyInstance创建的。

具体实现方法如下:
MyHandler :

public class MyHandler implements InvocationHandler {

    //被代理对象
    private Object target;

    public MyHandler(Object object) {
        this.target = object;
    }

    /**
     *
     * @param proxy 代理对象
     * @param method 被代理方法
     * @param args 被代理方法参数
     * @return null
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("权限控制");
        method.invoke(target);
        System.out.println("输出日志");

        return null;

    }
}

测试样例:

public class Test {

    public static void main(String[] args) {
        Car car = new Car();

        Dark dark = new Dark();

	    //创建handler,供JDK的Proxy.newProxyInstance在运行时创建代理类
        InvocationHandler moveHandler = new MyHandler(car);
        
        InvocationHandler flyHandler = new MyHandler(dark);

        //car的代理对象
        MoveAble moveProxy = (MoveAble) Proxy.newProxyInstance(
                car.getClass().getClassLoader(),
                car.getClass().getInterfaces(),
                moveHandler);

        //dark的代理对象
        Flyable flyProxy = (Flyable) Proxy.newProxyInstance(
                dark.getClass().getClassLoader(),
                dark.getClass().getInterfaces(),
                flyHandler);

        moveProxy.move();

        flyProxy.fly();

    }
}

运行结果

权限控制
汽车行驶中..
输出日志
权限控制
鸭子飞了..
输出日志

源码

查看全部源码点击这里,看高兴了赏个star。

有种似曾相识的感觉吗

Java有很多框架都提供或者利用了面向切面编程的方法。

当我接触到Spring boot ,用Aspect、PointCut 等注解做了一个Http请求的日志输出时,我惊叹于Spring boot 简单又强大的魅力,因为我明明没有在Controller层添加任何的代码啊。。

当我接触到Mybatis时,我很困惑为什么我在DAO层的接口能自动映射到xml文件,需要进行数据库操作时,竟然可以直接调用接口!接口被实例化了?

现在有点豁然开朗的感觉了,这些强大的功能都是用动态代理实现的!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值