设计模式:代理模式

为什么要使用代理模式:

比较官方的回答:在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用, 其特征是代理类与委托类有同样的接口。代理模式是常用的java设计模式。

看完这个解释依然云里雾里,我觉得代理除了上述功能,最易理解的是,在原有代码的基础上增加功能时,不会修改原有代码,非常符合程序设计的开闭原则。

代理模式一般分为静态代理和动态代理,顾名思义,静态代理是比较死板,不通用的模式,实现的代码是自己实现。

动态代理实现方式有很多种,主要有java提供的InvocationHandler和spring提供的cglib,前者的真实对象必须继承接口,后者不用继承任何接口和类,也可以实现动态代理。spring AOP就是采用cglib技术实现的动态代理。

静态代理:

举例:电影院放映电影,按照以前的方式,电影院从电影开始到电影结束是一套完整的流程。电影院可以播放各种不同的电影,属于一个规范,因此可以把电影院定义为抽象类或者接口:

public interface Movie {
    // 播放电影
    public void play();
}

电影远播开始播放一部具体的电影:

public class RealMovie implements Movie{
    @Override
    public void play() {
        System.out.println("开始播放:《阿甘正转》");
    }
}

主程序调用:

public class Test {

    public static void main(String[] args) {
        RealMovie realMovie = new RealMovie();
        realMovie.playMovie();
    }
后来电影院想增加收入,在播放电影的前后两个环节插入零食贩卖的功能,但是又不希望修改既有代码,于是决定使用静态代理来实现:
public class ProxyMovie implements Movie {

    private RealMovie realMovie = new RealMovie();

    @Override
    public void playMovie() {
        System.out.println("买点零食看电影吃吧(电影开播前广告)");
        realMovie.playMovie();
        System.out.println("买点零食回家吃吧(电影结束后广告)");
    }
}

静态代理的特点:代理类和被代理类都需要继承父接口,实现play方法,这样主程序就无法直接访问被代理类,即使被代理类的play方法中增加了N多复杂的代码,也不影响主程序去调用。

动态代理:java提供的Proxy类

电影1:

public class RealMovie implements Movie{
    @Override
    public void play() {
        System.out.println("开始播放:《阿甘正转》");
    }
}

电影2:

public class RealMovie2 implements Movie{
    @Override
    public void play() {
        System.out.println("开始播放:《复仇者联盟》");
    }
}

代理类:

public class ProxyMovie implements InvocationHandler {

    private Object object;

    public ProxyMovie(Object object){
       this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("买点零食看电影吃吧(电影开播前广告)");
        // 真实对象的方法都交由InvocationHandler.invoke方法执行
        method.invoke(object, args);
        System.out.println("买点零食回家吃吧(电影结束后广告)");
        return null;
    }
}

可以看出代理类不需要继承被代理类的父类,继承的是java提供的InvocationHandler接口,该接口只有一个方法:

public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;

所有被代理的类的方法都会通过该方法调用。且所有扩展功能都在此方法中去实现。

主程序调用代理类:

public class Test {

    public static void main(String[] args) {

        // 动态代理(代理类交由Proxy动态去实现接口(加载时实现))
        RealMovie realMovie = new RealMovie();
        InvocationHandler proxyMovie = new ProxyMovie(realMovie);

        Movie movie = (Movie) Proxy.newProxyInstance(
            realMovie.getClass().getClassLoader(),
            realMovie.getClass().getInterfaces(),
            proxyMovie);

        movie.play();

        RealMovie2 realMovie2 = new RealMovie2();
        InvocationHandler proxyMovie2 = new ProxyMovie(realMovie2);

        Movie movie2 = (Movie)Proxy.newProxyInstance(
            realMovie.getClass().getClassLoader(),
            realMovie.getClass().getInterfaces(),
            proxyMovie2);

        movie2.play();
    }
}

test主程序执行结果:

有点类似于面向切面编程的感觉。Proxy是java提供的生成动态代理实例的类,此类在程序运行时,会继承被代理类的父类,从方法newProxyInstance传入的参数可以看出,无论是传realMovie.getClass().getInterfaces(),还是realMovie2.getClass().getInterfaces(),

都可以实现代理的功能。应为这两个实例的父类是同一个。另外realMovie.getClass().getClassLoader()获取的都是AppClassLoader ,所以只要是同一个类加载器,传什么实例并不是重点。

java提供的动态代理有一个缺点,就是若类既没有继承父类,也没有实现接口,是无法被代理的,cglib提供的动态代理却可以实现代理

cglib动态代理:

public class RealMovie {

    public void playMovie(){
        System.out.println("开始播放:《阿甘正传》");
    }
}

此类没有继承或者实现任何类。

代理类:

public class MyProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("买点零食看电影吃吧(电影开播前广告)");
        methodProxy.invokeSuper(o, objects);
        System.out.println("买点零食回家吃吧(电影结束后广告)");
        return null;
    }
}

test主程序调用”

public class Test {

    public static void main(String[] args) {

        // cglib动态代理
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealMovie.class);
        enhancer.setCallback(new MyProxy());

        RealMovie realMovie = (RealMovie)enhancer.create();
        realMovie.playMovie();
    }
}

执行结果:

代理成功。

在spring framework 中,AOP就是使用cglib实现的动态代理。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值