• 代理模式


    一 : 什么是代理(Proxy)模式?

    代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。通俗讲.就是真正的业务功能还是由委托类来实现,但是在实现业务之前的一些公共服务,例如在项目开发中忘记了加入缓冲、日志等的功能。后期想加入,就可以使用代理来实现而没有必要打开已经封装好的委托类。

    现实生活中的代理模式:房东-->中介-->租客 中介就是代理方,负责看房,谈价格,签合同等琐事,房东只需收钱.

    二 : 为什么要有代理模式?

    (1).职责清晰:真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
    (2).代理对象可以在客户端和目标对象之间起到中介的作用,客户端使用的是代理对象中的方法,这样起到了中介的作用和保护了目标对象的作用。
    (3).高拓展性:无论被代理对象如何改变,只要代理类和被代理类都实现了统一接口,都不同修改代理类,而且即使扩展了新的被代理类,代理类也可以使用,只要创建代理类的时候传入对应的被代理类对象。

    三 : 怎么使用代理模式?

    3.1 静态代理 

      静态代理定义 : 静态代理是由程序员创建或特定工具自动生成源代码,再对其编译。最大的特点就是在程序运行时代理类的.class文件就已经存在了,但是这有一个很大的缺陷即每一个代理类只能为一个接口服务。

    3.1.1 静态代理示例:

    ①:定义服务接口

    package com.canner.proxy;
    
    /**
     * @Auther: canner
     * @Date: 12:18 2018/11/02 
     */
    public interface ICustomer {
        void buyHosue();
    }

    ②:定义服务实现类

    import com.canner.proxy.ICustomer;
    
    /**
     * @Auther: canner
     * @Date: 12:20 2018/11/02 
     */
    public class CustomerImpl implements ICustomer{
    
        @Override
        public void buyHosue() {
            System.out.println("顾客买了一套房");
        }
    }

    ③:创建客户代理类

    package com.canner.proxy;
    
    import com.canner.proxy.ICustomer;
    
    /**
     * @Auther: canner
     * @Date: 12:33 2018/11/02 
     */
    public class CustomerProxy implements ICustomer{
    
        private ICustomer customer;
    
        public CustomerProxy(final ICustomer customer) {
            this.customer= customer;
        }
    
        @Override
        public void buyHosue() {
            System.out.println("客户买房前做的事情");
            customer.buyHosue();
            System.out.println("客户买房后做的事情");
    
        }
    }

    ④:编写测试

    /**
     * @Auther: canner
     * @Date: 12:25 2018/11/02
     */
    public class ProxyTest {
        public static void main(String[] args) {
            ICustomer customer = new CustomerImpl();
            CustomerProxy customerProxy = new CustomerProxy(customer);
            customerProxy.buyHosue();
        }
    }

    静态代理总结:代理对象和实际对象实现的是同一个接口

    优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。

    缺点:1:每一个代理类只能为一个接口服务,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。 

          2:在程序运行之前静态代理的.class文件已经存在了。

               3:如果接口新增一个方法,代理类也得维护新增内容。

    3.2 动态代理

    3.2.1 JDK动态代理

    动态代理 : 在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK在运行时利用反射为我们动态的来创建。

    JDK动态代理必须要求真实对象是有接口

    JDK动态代理操作步骤 :

    ① 实现 InvocationHandler 接口,创建自己增强代码的处理器。

    ② 给 Proxy 类提供 ClassLoader 对象和代理接口类型数组,创建动态代理对象。

    ③ 在处理器中实现增强操作。

    JDK动态代理类:

     

    public class TransactionManagerAdvice implements java.lang.reflect.InvocationHandler {
    
        private Object target;//真实对象(对谁做增强)
        private TransactionManager txManager;//事务管理器(模拟)
    
        public void setTxManager(TransactionManager txManager) {
            this.txManager = txManager;
        }
    
        public void setTarget(Object target) {
            this.target = target;
        }
    
        //创建一个代理对象
        public <T> T getProxyObject() {
            return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), //类加载器,一般跟上真实对象的类加载器
                    target.getClass().getInterfaces(), //真实对象所实现的接口(JDK动态代理必须要求真实对象有接口)
                    this);//如何做事务增强的对象
        }
    
        //如何为真实对象的方法做增强的具体操作
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         //System.out.println(proxy); 如果打印这行会报栈溢出
    if (method.getName().startsWith("get") || method.getName().startsWith("list")) { return method.invoke(target, args);//放行 } Object ret = null; txManager.begin(); try { //--------------------------------------------------------------- ret = method.invoke(target, args);//调用真实对象的方法 //--------------------------------------------------------------- txManager.commit(); } catch (Exception e) { e.printStackTrace(); txManager.rollback(); } return ret; } }

     

    注意事项:反编译字节码文件后,发现底层对hashCode(),equals(),toString()方法和真实对象中的方法做了增强,所以在invoke()方法中如果直接打印proxy,相当于调用toString()方法,因此会递归调用导致栈溢出.调用proxy.getClass()方法则不会出现问题.

     

    3.2.2 CGLIB动态代理

    使用JDK的动态代理,只针对于目标对象有接口的情况,如果目标对象没有接口,则需要使用CGLIB的动态处理方式

    CGLIB动态代理类:

    public class TransactionManagerAdvice implements org.springframework.cglib.proxy.InvocationHandler {
    
        private Object target;//真实对象(对谁做增强)
        private TransactionManager txManager;//事务管理器(模拟)
    
        public void setTxManager(TransactionManager txManager) {
            this.txManager = txManager;
        }
    
        public void setTarget(Object target) {
            this.target = target;
        }
    
        //创建一个代理对象
        public <T> T getProxyObject() {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(target.getClass());//将继承于哪一个类,去做增强
            enhancer.setCallback(this);//设置增强的对象
            return (T) enhancer.create();//创建代理对象
        }
    
        //如何为真实对象的方法做增强的具体操作
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object ret = null;
            txManager.begin();
            try {
                //---------------------------------------------------------------
                ret = method.invoke(target, args);//调用真实对象的方法
                //---------------------------------------------------------------
                txManager.commit();
            } catch (Exception e) {
                e.printStackTrace();
                txManager.rollback();
            }
            return ret;
        }
    }
  • 相关阅读:
    Java开发中回车换行符的区别
    Eclipse中快捷键使用
    数组基础常用方法
    获取一组数组中最大值和最小值
    解决Java工程URL路径中含有中文的情况
    python录音,无声自动停止,或定时停止
    ChatterBot人工智能,聊天机器人,无坑指南(安装,使用)(2.使用篇)
    ChatterBot机器学习,聊天机器人,无坑指南(安装,使用)(1.安装篇)
    python播放mp3最佳方法
    自动天气
  • 原文地址:https://www.cnblogs.com/icanner/p/9896230.html
Copyright © 2020-2023  润新知