设计模式-代理

定义:
结构型-代理模式(proxy pattern)是指为其它对象提供一种代理,以控制对最终目标对象的访问,来达到具体的增强,生活中常见的代理场景例如:房产中介,快递,黄牛等..。

示例1: 从静态代理到jdk动态代理
示例2: Cglib动态代理

区别:
cglib:继承的方式覆盖父类的方法,对目标类没有任何要求,底层没有用到反射,效率及性能更好,值得注意的是坑是,目标代理类不能有final修饰的方法,会被忽略
jdk:采用实现的方式,必须要求代理的目标对象一定要实现一个接口,用于用户而言,依赖性更强,调用也更复杂,执行效率偏低,每次都要用反射。
它们的思想都是通过生成一个字节码,重组成一个新的类

使用场景:
登录鉴权,下单,日志等等都可用,太多了 ....
优点:
能将代理对象与真实目标对象分离,一定程度上降低了系统耦合性,易于扩展,代理还可以起到保护目标对象的作用,增强目标对象的职责。
缺点:
代理模式会造成系统设计中类的数目增加,在客户端和目标对象之间增加了一个代理对象,请求处理速度会变慢,增加了系统的复杂度。
spring中代理选择原则:
1.当bean有实现接口时,会用jdk动态代理,没有实现接口spring会选择cglib。
2.因为spring官方推荐jdk,so,会默认使用jdk代理,但是可通过配置强制使用cglib,只需在配置文件中加入如下代码:
<aop:aspectj-autoproxy proxy-target-class="true"/>

示例1:

/**
 * 相亲过程: 静态代理
 */
public interface Iperson {
    // 人需要相亲
    void findLove();
}
// 张三应该是个人,so 实现person
public class Zhangsan implements Iperson {
    private final static Logger logger = Logger.getLogger(Zhangsan.class);
    // 张三需要相亲
    @Override
    public void findLove() {
        logger.info("张三的要求: 肤白貌美大长腿");
    }
}
// 张三的老爷子:张老三,也需要相亲,但他是给自己的儿子相亲, 如果给自己相亲那就完了:关系比较乱
public class Zhanlaosan implements Iperson {
    private final static Logger logger = Logger.getLogger(Zhanlaosan.class);

    // 张老三要把张三的需求拿到
    private Zhangsan zhangsan;
    public Zhanlaosan(Zhangsan zhangsan){
        this.zhangsan = zhangsan;
    }
    // 张老三帮张三张罗相亲
    @Override
    public void findLove() {
        // 张老三在相亲前做一系列事情...
        logger.info("张老三开始张罗 ...");
        zhangsan.findLove();
        // 张老三在相亲后做一系列事情...
        logger.info("开始交往 ...");
    }
}
public class MainExcute {
    public static void main(String[] args) {
        // 1.此处执行完后感觉没问题,正常执行相亲流程
        Zhanlaosan meipo = new Zhanlaosan(new Zhangsan());
        meipo.findLove();
        // 2.但是如果来了个李四也要寻找真爱,怎么办呢?不可能叫张三老爷子去张罗啊,况且他们互不认识
        /**
         *  全国那么多人都需要找真爱,产业链(珍爱网/媒婆)孕育而生,使服务越来越专业,通用,于是产生动态代理。
         *  目前java已经提供两个动态代理类库,可以不用再重复造轮子
         */
    }
}
------------------------------------
/**
 * 相亲过程: jdk动态代理
 */
public interface Iperson {
    // 人需要相亲
    void findLove();
    // 扩展保险业务
    void buyInsure();
}
// 张三实现人的接口
public class Zhangsan implements Iperson {
    private final static Logger logger = Logger.getLogger(Zhangsan.class);
    // 张三需要相亲
    @Override
    public void findLove() {
        logger.info("张三的要求: 肤白貌美大长腿");
    }
    @Override
    public void buyInsure() {
        logger.info("张三买30万的保险");
    }
}
// 李四
public class Lisi implements Iperson {
    private final static Logger logger = Logger.getLogger(Lisi.class);
    // 李四需要相亲
    @Override
    public void findLove() {
        logger.info("李四的要求: 温柔善良学历高");
    }
    @Override
    public void buyInsure() {
        logger.info("李四买100万的保险");
    }
}
// jdk 媒婆, 需要实现InvocationHandler
public class JdkMeiPo implements InvocationHandler {
    private final static Logger logger = Logger.getLogger(JdkMeiPo.class);
    private Iperson target;
    /**
     * 需要拿到目标对象的引用
     * @param target 抽象
     * @return 目标对象
     */
    public Iperson getInstance(Iperson target){
        this.target = target;
        Class<?> clazz = target.getClass();
        /**
         * java.lang.reflect.Proxy包下面的Proxy类
         * newProxyInstance参数:
         * ClassLoader: 类加载器,这个类通过哪个加载器加载
         * interfaces:类需要实现哪些接口,以上已经获取了class对象,可以通过该对象拿到所有的接口
         * InvocationHandler: 实际上就是我们的媒婆,相当于是规范
         */
        return (Iperson)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }
    /**
     * 可在该方法控制逻辑,由生成这个对象时动态调用: Proxy.newProxyInstance(...)
     * @param proxy 生成以后的对象
     * @param method 需要调用的函数
     * @param args 函数中的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        // 因为它底层是通过反射执行,所以你在外面调用的是什么方法这个执行的就是什么
        Object result = method.invoke(this.target, args);
        after();
        return result;
    }
    private void after() {
        logger.info("调用后加逻辑: 双方同意,开始交往 ...");
    }
    private void before() {
        logger.info("调用前加逻辑: 我是媒婆,已经接到你的需求,开始物色 ...");
    }
}
public class MainExcute {
    private final static Logger logger = Logger.getLogger(MainExcute.class);
    public static void main(String[] args) {
        // 1.让媒婆生成代理类,不管张三还是李四,谁都能来相亲,反正我是专业的
        JdkMeiPo meipo = new JdkMeiPo();
        Iperson zhangsan = meipo.getInstance(new Zhangsan());
        zhangsan.findLove();
        Iperson lisi = meipo.getInstance(new Lisi());
        lisi.findLove();
        // 假设需要再扩展业务:即根据业务场景调用即可
        lisi.buyInsure();
        logger.info("发现扩展业务也调用了前后逻辑,需根据自己的业务场景选择是否调用");
    }
}
1622648257(1).jpg

示例2:

// Cglib动态代理: 媒婆, 需要实现 MethodInterceptor
/*<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.11</version>
</dependency>*/
public class CglibMeiPo implements MethodInterceptor {
    private final static Logger logger = Logger.getLogger(CglibMeiPo.class);
    /**
     * 创建代理对象
     * @param clazz 传入class即可,不需要实现任何接口,根据class来获得所有的方法
     * @return 实例
     */
    public Object getInstance(Class<?> clazz){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        // 调用父类的
        Object result = methodProxy.invokeSuper(o, objects);
        after();
        return result;
    }
    private void after() {
        logger.info("调用后加逻辑: 双方同意,开始交往 ...");
    }
    private void before() {
        logger.info("调用前加逻辑: 我是媒婆,已经接到你的需求,开始物色 ...");
    }
}
// 张三,cglib代理 不用实现任何接口了
public class Zhangsan {
    private final static Logger logger = Logger.getLogger(Zhangsan.class);
    // 张三需要相亲
    public void findLove() {
        logger.info("张三的要求: 肤白貌美大长腿");
    }
    public void buyInsure() {
        logger.info("张三买30万的保险");
    }
}
public class MainExcute {
    private final static Logger logger = Logger.getLogger(MainExcute.class);
    public static void main(String[] args) {
        logger.info("直接用就可以了,不需要用户有任何的前置条件,只要是个class就可以");
        Zhangsan zhangsan = (Zhangsan)new CglibMeiPo().getInstance(Zhangsan.class);
        zhangsan.findLove();
        /**
         * 跟jdk最大的区别:
         * jdk的类必须实现接口,因为生成代理类的时候需要知道你实现了哪个接口,而cglib是直接继承你这个类的
         */
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 设计模式是语言的表达方式,它能让语言轻便而富有内涵、易读却功能强大。代理模式在Java中十分常见,有为扩展某些类的...
    CatalinaX阅读 3,097评论 0 0
  • 设计模式之代理模式 今天学到Spring的动态代理实现AOP,对代理这个概念很模糊,看了一篇文章发现这使用了设计模...
    王小冬阅读 3,446评论 1 13
  • 定义 代理模式是对象的结构模式。代理模式给某一个对象提供代理对象,并由代理对象控制对源对象的引用。 代理模式的结构...
    步积阅读 11,529评论 0 1
  • 代理(Proxy)是一种常用的行为型设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的...
    皮多堡阅读 2,875评论 3 3
  • 概述 代理模式是通过在不改变委托类的情况下通过代理类来增强一些功能。 来自于《大话设计模式》 实现模式 具体有两种...
    蓝黑R9阅读 1,371评论 0 0