代理模式
    定义:
        代理模式 给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。(中介)

  为什么使用代理模式

    中介隔离:
               有些时候,客户类 不想或不能 直接引用一个委托对象
               而 代理类对象可以在 客户类 + 委托对象 间起到中介作用

           特征:代理类和委托类实现相同接口

    开闭原则,增加功能:
        除了中介之外,还可以通过 给代理类增加额外的功能 拓展委托类功能(自如)
            这样秩序修改代理类 而不需要修改委托类  符合开闭原则


        代理类  主要负责:
                    委托预处理消息
                    过滤消息
                    把消息转发给委托类
                    事后 对返回结果的处理

              代理类本身不真正实现服务,通过调用委托类的相关方法,提供特定服务

        真正的服务功能还是需要委托类来实现,但是可以在业务功能前后 加入公告服务

        例如:项目加入缓存,日志,可以使用代理类完成

  两种代理模式(按照代理创建的时期分类)
        静态代理
                对其编译,程序运行之前,代理类.class文件已经被创建

            优点:可以做到在符合开闭原则的情况下对目标对象进行功能拓展
            缺点:为每一个服务都得创建代理类,工作量大。同时接口一旦改变。代理类也要改变


        动态代理
             程序在运行是通过 反射机制 动态创建

            注意Proxy.newProxyInstance()方法接受三个参数:

            ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
            Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
            InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法

              仅支持interface代理  共同父类Proxy,无法对class进行动态代理


        CGLIB代理
            JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,
            如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,
            其原理是通过字节码技术为一个类创建子类,
            并在子类中采用方法拦截的技术拦截所有父类方法的调用,
            顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。
            JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。


package 静态代理;

/**
 * 服务类接口
 */
public interface BuyHouse {
    /**
     * 买房子
     */
    void buyHouse();
}

package 静态代理;

/**
 * 服务类接口实现类
 */
public class BuyHouseImpl implements BuyHouse{
    @Override
    public void buyHouse() {
        System.out.println("我要买房");
    }
}

(一)静态代理!

package 静态代理;

/**
 * 代理类,,,要实现 被代理类 一样的接口
 */
public class BuyHouseProxy implements BuyHouse{

    private BuyHouse buyHouse;

    public BuyHouseProxy(final BuyHouse buyHouse){
        this.buyHouse = buyHouse;
    }

    @Override
    public void buyHouse() {
        System.out.println("买房前准备");
        buyHouse.buyHouse();
        System.out.println("买房后装修");
    }
}

package 静态代理;

public class ProxyTest {

    public static void main(String[] args) {
        BuyHouse buyHouse = new BuyHouseImpl();
        buyHouse.buyHouse();

        BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse);
        buyHouseProxy.buyHouse();
    }
}

(二)动态代理实现

(常用做法)

package 动态代理;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 编写动态处理器
 */
public class DynamicProxyHandler implements InvocationHandler {

    private Object object;

    public DynamicProxyHandler(final Object object){
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("买房前做准备");
        Object result = method.invoke(object, args);
        System.out.println("买房后装修");

        return result;
    }
}

package 动态代理;

import 静态代理.BuyHouse;
import 静态代理.BuyHouseImpl;

import java.lang.reflect.Proxy;

public class DynamicProxyTest {
    public static void main(String[] args) {
        BuyHouse buyHouse = new BuyHouseImpl();

        BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(
                BuyHouse.class.getClassLoader(),
//ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
                new Class[]{BuyHouse.class},
//Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
                new DynamicProxyHandler(buyHouse));
//InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的invoke方法

        proxyBuyHouse.buyHouse();
    }
}

动态代理的 粗俗理解,由于Interface 无法实例,通过 Proxy 这个父类 newProxyInstance这个方法,创造 一个Interface的 代理实例,通过这个代理实例 不仅可以 调用 实例的方法,还可以 通过动态处理器 ,对实例的方法进行加强,无需修改 原本 真正实例内的方法代码。

(不常用做法)但是 可以更加理解 这个原理

JDK提供 java.lang.reflect.InvocationHandler接口(增强方法)
       java.lang.reflect.Proxy类(入口)

Proxy:静态方法:getProxyClass(ClassLoader,Interface),
      传入类加载器+接口,返回代理的Class的对象

   等于就是 将接口的结构信息 拷贝到了 一个新的Class对象(拥有构造器,可以实例)

Proxy.getProxyClass()这个方法的本质是:以Class造Class
代码操作一下:(不常用方法)


package 动态代理.Demo2;

import 静态代理.BuyHouse;
import 静态代理.BuyHouseImpl;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyTest{
    public static void main(String[] args) throws Throwable {
        /**
         * 参数1:ByHouse的类加载器(把ByHouse加载进内存的类加载器)
         * 参数2:代理对象需要和目标对象实现相同接口ByHouse
         */
        Class BuyHouseProxyClazz = Proxy.getProxyClass(
                BuyHouse.class.getClassLoader(),
                BuyHouse.class);



        //得到有参构造器,$Proxy0(InvocationHandler h)
        Constructor constructor = BuyHouseProxyClazz.getConstructor(InvocationHandler.class);

        //通过反射创建代理实例
        BuyHouse buyHouseProxyImpl = (BuyHouse) constructor.newInstance(new InvocationHandler() {



            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //手动new一个 创造一个真正的实例
                BuyHouse buyHouse = new BuyHouseImpl();
                //反射执行目标对象的方法
                Object result = method.invoke(buyHouse, args);
                return result;
            }
        });

        buyHouseProxyImpl.buyHouse();
    }
}

来看一下 代理类的对象 和 被代理类的对象

不管是什么类型,都能接收!giao!

(三)Cglib 动态代理方式

package CGLIB代理类.Demo2;

//目标类
public class Programmer {

    private String name;

    public void setName(String name){
        System.out.println("Setting Name.");
        this.name = name;
    }
    public void code(){
        System.out.println(name + " is writing bugs.");
    }
}

package CGLIB代理类.Demo2;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

//代理类

public class ProgrammerProxy implements MethodInterceptor {

    //内部持有委托类的引用


    private Object target;

    //创建代理类对象
    public Programmer createProxy(Programmer object){
        target = object;
        //创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        //设置要代理的目标类,加强方法
        enhancer.setSuperclass(this.target.getClass());

        //设置单一回调对象,在回调中 拦截对目标方法的调用
        enhancer.setCallback(this);

        //设置类加载器
        enhancer.setClassLoader(object.getClass().getClassLoader());

        //创建代理对象
        return (Programmer) enhancer.create();
    }


    /**
     *  回调方法,在代理实例上拦截并处理目标方法的调用,返回结果
     * @param proxy  代理类
     * @param method 被代理的方法
     * @param params 该方法的参数组
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
        //调用之前处理
        doBefore();

        //调用原方法
        method.invoke(target,params);

        //调用之后处理
        doAfter();

        return null;
    }

    public void doAfter(){
        System.out.println("do after.");
    }

    public void doBefore(){
        System.out.println("do before");
    }
}

package CGLIB代理类.Demo2;

import org.junit.Test;

public class ProxyTest {

    @Test
    public void testCglibProxy(){
        //创建一个Programmer对象
        Programmer programmerA = new Programmer();
        programmerA.setName("Frank");

        programmerA.code();
        System.out.println("----------------");
        System.out.println();
        System.out.println("代理对象");
        //创建代理对象
        Programmer programmerProxyA = new ProgrammerProxy().createProxy(programmerA);
        programmerProxyA.code();

        System.out.println("-----------------");
        //修改代理对象
        System.out.println("修改代理对象");
        programmerProxyA.setName("Wang");
        programmerProxyA.code();

        System.out.println("------------------");

        System.out.println();
        System.out.println("查看委托对象");
        programmerA.code();

        System.out.println("-----------------");
        //修改委托类对象
        programmerA.setName("Song");
        programmerProxyA.code();
    }
}

结果:

分析:结果看看到。

当修改 委托类的参数时,代理类也会 跟着修改,

当修改 代理类的参数时,委托类也会修改,

说明 代理类是委托类的一种引用

Cglib的总结:

先创建一个类实现 MethodInterceptor接口重写intercept方法,在intercep中可以截获委托类的所有非final修饰的public和protected方法,上例中,method.invoke(target,params);即为调用原对象的原方法,在代理类中保存了委托类对象的引用,这一点跟JDK动态代理是一样的。在调用原方法前先调用了doBefore方法,调用之后还调用了doAfter方法,从而实现了代理功能。至于createProxy方法,也只是一个固定步骤,先创建Enhance对象然后将委托类的一些属性往里塞,然后调用create方法来动态生成代理对象。

在测试类中,为了更明显的说明代理类与委托类的关系,分别用代理类对象programmerProxyA和委托类对象programmerA对name字段进行修改,可以产生一样的效果。

下面来对比一下Cglib动态代理与JDK动态代理:

1.两者都是动态代理,都是运行时动态生成代理对象。

2.JDK动态代理利用的是接口信息来实现的代理,委托类必须实现某个或者某些接口,而Cglib则是利用继承关系,利用asm在运行时动态生成委托类的子类,从而实现对委托类的代理。因此不依赖接口。

3.Cglib由于是利用继承关系来实现代理的,因此无法代理被final修饰的类以及被final修饰的方法

4.Cglib一般来说效率要比JDK动态代理效率更高,可以实现的代理也更为强大。

当然,具体情况具体分析,虽然Cglib比Jdk动态代理更强大,但并不一定各个地方都强行使用,有时候JDK动态代理相对来说更加简单粗暴。