设计模式 之 代理模式 Proxy

代理模式

        代理模式跟单例模式一样,都是面试问的比较频繁的一种设计模式,所以咱们是一定要学会的。一般来说吧,我们只需要掌握以下几点就可以轻松的应对面试官的各种问题了。

  1. 掌握代理模式的应用场景和实现原理。
  2. 了解静态代理和动态代理的区别。
  3. 了解CGLib和JDK Proxy的根本区别。
  4. 手写一次动态代理。

代理模式的定义

        官方定义:代理模式指为其他对象提供一种代理,以控制对这个对象的访问

        我的理解是代理模式是可以在不修改被代理对象的基础上通过扩展代理类,进行一些功能的附加与增强,有效防止目标对象的直接访问。代理类和被代理类应该共同实现一个接口或者共同继承某个类。


代理模式的适用场景

  • 保护目标对象
  • 增强目标对象

静态代理

        静态代理就是按照代理模式写的代码,特点是代理类和目标类在代码里面是确定的,是在程序运行之前就已经存在的代理类的字节码文件,所以叫静态。

        静态代理不够灵活,所以如果想要灵活,动态增加方法,增加行为,就需要动态代理了。

直接上代码

// 需求,设计一个看电影的简单静态代理
// 1.先创建一个接口类,需要目标类跟代理类一起实现的
// 2.创建目标类,实现接口类。
// 3.创建代理类,扩展目标类功能
// 3.1 实现接口类以及接口方法
// 3.2 声明目标类,实现构造方法,扩展目标类功能
public interface Movie{
    //正在播放的电影
    void play();
}

//目标类,实现Movie接口的类,
public class RealMovie implements Movie{
    @Override
    public void play{
        
      //实现具体业务
      System.out.println("您正在看周星驰的电影《功夫》");  
    }
}

//代理类,代理目标类,不修改目标类的结构
public class MovieProxy implements Movie
{
    //声明目标类
    RealMovie movie;
    
    public MovieProxy(RealMovie movie){
        super();
        this.movie = movie;
    }
    
    //扩展目标类功能
    @Override
    public void play{
      //ToDo
      see(true);
      movie.play();
      see(false); 
    }
    
    //扩展目标类功能
    public void see(boolean isStart){
        if(isStart)
        	System.out.println("电影即将开始...");
        else
            System.out.println("电影已经结束...");
    }
    
    //调用
    public static void main(String[] args){
        RealMovie movie1 = new RealMovie();
        Movie movie = new MovieProxy(movie1);
        movie.play();
    }
}

动态代理

        动态代理要为对象动态的增加方法、增加行为,并且动态代理是不存在代理类的字节码文件的,就需要一些实现手段。经常用的实现方式两种:反射实现,CGlib实现

JDK跟CGlib的区别

  • JDK实现:JDK代理对于用户而言,依赖更强,调用也更复杂,基于反射机制目标对象必须要有接口,生成的代理类是接口的一个实现类,通过InvocationHandler的invoke方法,JDK Proxy 生成逻辑较为简单,执行效率更低,每次都要反射。

  • CGlib实现:CGlib 采用继承的方式,覆盖父类的方法,CGlib对目标类没有任何要求,基于字节码实现效率略低,但目标对象不需要有接口,生成的代理类是目标类的子类,目标类不能是final的。

JDK动态代理实现代码

//动态代理实现 - JDK实现
// 1.创建接口类,必须有
// 2.创建目标类,实现接口类的方法
// 3.创建代理类,实现InvocationHandler接口,实现invoke方法
public interface Movie{
    //正在播放的电影
    String play(String name);
}

//目标类
public class RealMovie implements Movie{
    @Override
    public String play(String name){
        System.out.println("你正在观看" + name);
        return "OK";
    }
}

//代理类
public class ProxyHandler<T> implements InvocationHandler{
    
    //声明动态目标类
    private T target;
    
    //动态代理
    public ProxyHandler(T target){
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        System.out.println("电影即将开始...");
        Object ret = method.invoke(target, args);
        System.out.println("电影已经结束...");
        return ret;
    }
    
    //调用
    public static void main(String[] args){
        // 使用 - 实例化目标类
        RealMovie movie = new RealMovie();
        // 声明代理类,通过参数指明目标类
        ProxyHandler proxy = new ProxyHandler(movie);
        // 生成代理对象实例,使用proxy类的一个静态方法
        Movie movie = (Movie) Proxy.newProxyInstance(RealMovie.class.getClassLoader(), new Class[]{Movie.class}, proxy);
        
        movie1.play("《功夫》");
    }
}

Spring中的代理选择原则

  1. 当Bean有实现接口时,Spring就会用JDK的动态代理。
  2. 当Bean没有实现接口时,Spring选择CGlib。
  3. Spring可以通过配置强制使用CGlib,只需在Spring的配置文件中加入如下代码
        
    <aop:aspectj-autoproxy proxy-target-class="true">


总结

优点

  1. 代理模式能将代理对象与真实被调用的目标对象分离。
  2. 一定程度上降低了系统的耦合程度,易于扩展。
  3. 代理可以起到保护目标对象的作用。
  4. 增强目标对象的职责。

缺点

  1. 代理模式会造成系统设计中类的数目增加。

  2. 在客户端和目标对象之间增加了一个代理对象,请求处理速度变慢。

  3. 增加了系统的复杂度。 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值