java动态代理的理解
package com.atguigu.java3;

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

/**
 * 动态代理的举例  代理模式:代理类,被代理类,接口
 *
 *
 * @author yangzhnahang
 * @create 2019-08-17-18:42
 */

//代理类之一
interface Human{
    String getBelif();

    void eat(String food);  //快捷键加分号:    Shift+Ctrl+Enter

}

//被代理类  实现接口
class SuperMan implements Human{

    @Override
    public String getBelif() {
        return "I believe I can fly!";
    }

    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃" + food);
    }
}

/**
 * 要想实现动态代理,需要解决的问题?
 * 问题一: 如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象
 * 问题二: 当通过代理类的对象调用方法时,如何动态的去调用被代理类的同名方法
 */


/**
 * 思路:
 *
 * 本类使用过程:
 * 通过class ProxyFactory{}类时,在调用getProxyInstance(Object obj)的时候
 * 先造一个 被代理类的对象,    顺便把class MyInvocationHandler implements InvocationHandler{}
 * 的
 * public void bind(Object obj){   //这里相当于做了一个实例化
 *         this.obj = obj;
 *     }    给实例化了。
 *
 *     实例化之后,在通过Proxy.newProxyInstance(obj.getClass().getClassLoader(),
 *                 obj.getClass().getInterfaces(),handler);
 *  代理类调用
 *  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 *      调用结束之后就得到了,对象,方法    在通过
 *        method.invoke(被代理类的对象,这个参数三就是同名method的参数);
 *  }
 *
 *  由于invoke()有一个返回值,因此,我们采用Object方法去接收
 *
 *
 */

class ProxyFactory{



    //问题一:  调用此方法返回一个代理类的对象 这里要声明成Object的类型,
    // 否则写成Human又写死了,成静态了
    public static Object    getProxyInstance(Object obj){   //obj:被代理类的对象
        MyInvocationHandler handler = new MyInvocationHandler();

        /**
         * new MyInvocationHandler();   造出一个对象之后,
         * 就可以用handler.bind(obj);这个方法了
         * 这个调用相当于给
         * class MyInvocationHandler implements InvocationHandler{}
         * 中的   private Object obj; 进行赋值
         *
         * Object   即代理类的类型
         */
        handler.bind(obj);

        /**Proxy.newProxyInstance   创建代理类的                            对象
         *  obj.getClass().getClassLoader() 找出类的对象由哪个加载器加载
         *  你是哪个加载器加载的,我跟你一样
         *
         *  obj.getClass().getInterfaces()  声明代理类和被代理类用的          同一个接口
         *  这里先找到被代理类实现的是什么接口,把接口放到代理类中,
         *  这样可以保证接口一致
         *
         *   第三个参数放的是同名方法,由于这个参数是一个接口,所以要写上一个实现类
         *   class MyInvocationHandler implements InvocationHandler{}
         *
         * Proxy.newProxyInstance(代理类对象,代理接口,被代理类对象);
         *
         * 因此,一旦调用class ProxyFactory{
         *      interface Human{
         *          String getBelif();
         *          void eat(String food);  //快捷键加分号:    Shift+Ctrl+Enter
         *      }
         *      中的eat();方法,就相当于直接调用   handler的
         *      invoke(Object proxy, Method method, Object[] args) 这个方法
         *
         *
         */
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(),handler);
    }
}


/**Proxy.newProxyInstance(obj.getClass().getClassLoader()   由于这里生成了代理类的对象,
 * 这个对象调用方法时,调用了    第三个参数Xxx    这个参数即   MyInvocationHandler类型的对象
 *  即使用了Proxy.newProxyInstance这个方法就会调用第三个参数,而这个参数是对象,
 *  这个MyInvocationHandler类型的对象中含有
 *invoke(Object proxy, Method method, Object[] args) throws Throwable {}
 * 这个方法,
 *
 * 因此需要在class ProxyFactory{}中在创建一个对象,对象类型是MyInvocationHandler的
 */


class MyInvocationHandler implements InvocationHandler{

    /**method.invoke();要求我们声明一个被代理类,作为第一个参数传进去
     * 这个被代理类也不能写死了,即不写成human,要写成动态的,Object的
     * 赋值时,需要使用被代理类的对象进行赋值
     *
     */
    private Object obj;

    //赋值可以采用构造器赋值,也可以采用方法赋值,这里用方法赋值

    /**
     * 造完对象之后,可以在class ProxyFactory{}中写上
     * handler.bind()  private Object obj;给赋值,
     * 如果不赋值,则 method.invoke()就成了空指针了
     *
     *
     *
     * 赋值,赋的是被代理类的对象
     * public static Object    getProxyInstance(Object obj){
     * 中参数   obj,   即被代理类的对象就是要赋的值
     *
     * 这里意味着:
     *
     *
     * @param obj
     */

    public void bind(Object obj){   //这里相当于做了一个实例化
        this.obj = obj;
    }
    /**
     * 下面方法中invoke()体现一个动态的性质。
     * 当我们通过代理类的对象,调用方法a时,会自动的调用如下的方法:invoke()
     *  将被代理类要执行的方法a的功能就声明在invoke()方法中
     *
     *  Object proxy参数:即代理类的对象
     *  Method method   代理类的对象是什么方法这里就是什么方法
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        /**
         * 通过反射调用    public static Object    getProxyInstance(Object obj){
         * //obj:被代理类的对象    这里的方法(这个方法收集了 代理类,接口,被代理类)
         *  本质上 method.invoke(obj, args);   这句代码invoke的是被代理类的对象,
         *  但是
         *      @Override
         *     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         *     这里没有提供有  被代理类的对象
         *     因此,在class MyInvocationHandler implements InvocationHandler{
         *     里边再声明一个,但是要写成   Object类型,否则就写死了,不符合动态本质
         *
         */

        //method:即为代理类对象调用的方法,此方法也作为被代理类对象要调用的方法
        //obj:被代理类的对象
        Object returnValue = method.invoke(obj, args);

        //上述方法(代理类对象调用的方法)的返回值就作为当前类的invoke()的返回值
        return returnValue;
    }


}



public class ProxyTest {


    //测试了
    public static void main(String[] args) {

        //第二步:再传一个被代理类的对象
        SuperMan superMan = new SuperMan();

        //先创建一个 代理类的对象  proxyInstance:即代理类的对象   把被代理类对象放进来
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);

        //当通过代理类对象调用方法时,会自动的调用,并且时动态的调用被代理类中同名方法
        String belif = proxyInstance.getBelif();
        /**
         * 实质:调用proxyInstance.getBelif();这个方法时,自动调用了
         * public Object invoke(Object proxy, Method method,
         * Object[] args) throws Throwable {}中的method.invoke(obj, args);
         *
         * 这里的 method 就是 getBelif() 由于getBelif()中没有参数,所以相当于空了
         *
         *
         * 下面的也一样:  调用
         * proxyInstance.eat("四川麻辣烫");  这个方法时,自动调用了
         * public Object invoke(Object proxy, Method method,
         * Object[] args) throws Throwable {}中的method.invoke(obj, args);
         * 这里的 method 就是 eat("四川麻辣烫");
         * 这里的  四川麻辣烫    即args即实参。
         *
         * 通过测试可以看到:我们调用这两个方法
         * proxyInstance.getBelif();
         * proxyInstance.eat("四川麻辣烫");
         * 实质都是被代理类中的两个方法
         *
         *
         * 当通过代理类调用方法时,会自动调用被代理类中的同名方法。
         */


        System.out.println(belif);
        proxyInstance.eat("四川麻辣烫");






        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ClothFactory proxyClothFactory = (ClothFactory)
                ProxyFactory.getProxyInstance(nikeClothFactory);

        /**
         * 创建被代理类对象: nikeClothFactory
         * ClothFactory proxyClothFactory = (ClothFactory)
         *                 ProxyFactory.getProxyInstance(nikeClothFactory);
         *                 这句代码动态创建了代理类 的对象 ,
         *                 同时声明成接口类型的ClothFactory
         */

        proxyClothFactory.produceCloth();
        /**
         *proxyClothFactory.produceCloth();动态的调用nikeClothFactory
         * 这个对象nikeClothFactory中的方法
         *
         * 说明:
         *nikeClothFactory明明是被代理类的中方法,
         * 但是我们看到可以通过   代理类对象proxyClothFactory
         * 来代理执行的。
         *
         *
         * 此时,我只需要提供被代理类,接口,然后就可以动态的创建代理类,
         * 用代理类来执行就可以了
         */

    }
}