代理模式

一 : 什么是代理(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;
    }
}
原文地址:https://www.cnblogs.com/icanner/p/9896230.html