动态代理

一、类加载器

       1.类加载器的作用

   动态代理

       2.类加载器的分类

          类加载器有三种,不同类加载器加载不同的

         1)BootStrap:引导类加载器:加载都是最基础的文件

         2)ExtClassLoader:扩展类加载器:加载都是基础的文件

         3)AppClassLoader:应用类加载器:三方jar包和自己编写java文件

      3.获得类加载器

          字节码对象.getClassLoader();

public class Demo {
        
    public static void main(String[] args) {
        Class clazz=Demo.class;  //获得Demo的字节码对象
        ClassLoader classLoader = clazz.getClassLoader();//获得类加载器
        //getResource的参数路径相对classes(src)
        classLoader.getResource("database.properties");//获得classes(src)下的任何资源
        
    }

二、动态代理

  1.什么是动态代理(中介)

    

 调用对象(需要租房的人)----->代理对象(中介)------>目标对象(房主)

   动态代理

 2.动态代理

              不用手动编写一个代理对象,不需要一一编写与目标对象相同的方法,这个过程,在运行时的内存中动态生成代理对象。------字节码对象级别的代理对象

  分类:

            基于子类的动态代理

            基于接口的动态代理

   1)基于接口的动态代理的:       

      * 动态代理的API:Proxy中存在一个生成动态代理的的方法newProxyInstance

      动态代理

            返回值:Object就是代理对象;

           参数:loader:代表与目标对象相同的类加载器-------目标对象.getClass().getClassLoader();

            interfaces:代表与目标对象实现的所有的接口字节码对象数组;

            h:具体的代理的操作,InvocationHandler接口

  步骤:

               1.代理对象和真实对象实现相同的接口;

               2.代理对象=proxy.newProxyInstance();

               3.使用代理对象调用方法

               4.增强方法; 方式:1.增强参数列表;2.增强返回值类型;3.增强方法体执行逻辑  

案例:

      用户------------------->>  代理商(代理对象) --------------->联想公司(真实对象)

//目标对象的接口

public interface SaleComputer {
       
    public String sale(double money);

}
/**
 * 目标类
 */
 public class Lenovo implements SaleComputer{
     

    @Override
    public String sale(double money) {
        System.out.println("花了"+money+"元买了一台电脑");
        return "联想电脑";
    }


}
/**
 * 代理类
 * 
 * @author 撑起一片阳光
 *
 */
public class MyAnnoTest {
    public static void main(String[] args) {
         Lenovo lenovo = new Lenovo ();
        /**
         * 三个参数: 1.类加载器:真实对象.getClass.getClassLoader 
                  * 2.接口数组: 真实对象.getClass.getInterface
                  * 3.处理器:new InvocationHandler() 
                  * 此方法返回一个代理对象,然后转成和目标对象相同的接口
         */
        SaleComputer proxy = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(),
                lenovo.getClass().getInterfaces(),

                new InvocationHandler() {
                    /**
                     * 代理逻辑编写的方法:代理对象调用的所有方法都会触发改方法执行 参数: 1.proxy:代理对象(不用) 
                                         * 2.method:代理对象调用的方法,被封装为对象
                     * 3.args:代理对象调用方法时传入的参数
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                        System.out.println("该方法执行了");
//                        System.out.println(method.getName());//返回sale
//                        System.out.println(args[0]);//8000
                        
                        if (method.getName().equals("sale")) {
                            // 获取到该方法的参数
                            // 1.增强参数
                            double money = (double) args[0];
                            //3.增强方法体
                            System.out.println("专车接你去买电脑");
                            money = money * 0.85; // 参数值修改后代理对象回扣1200返回6800给真实对象
                            System.out.println("免费送货");
                            // 使用真实对象调用该方法
                            Object obj = method.invoke(lenovo , money);
                            //增强返回值
                            return obj+"+鼠标";
                        } 
                        else {
                            Object obj = method.invoke(lenovo , args);
                            return obj;
                        }
                    }
                });

        // 3.调用方法
        String sale = proxy.sale(8000);//用户花了8000
        System.out.println(sale);
    }
}

       2)基于子类的动态代理

                涉及的类:Enhancer

                提供者:第三方的cglib库

           * 创建代理对象:使用Enhancer类中的create方法

             注意:代理的类不能为最终类   

实现步骤:

              第一步:导入依赖        

<dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.1_3</version>
    </dependency>

          第二步:实现:

            

**
 * 生产者,要代理的类
 */
public class Produce {
    /**
     * 销售
     * @param money
     */
    public void saleProduce(float money){
        System.out.println("销售产品,并拿到钱"+money);
    }

    /**
     * 售后
     * @param money
     */
    public void afterService(float money){
        System.out.println("提供售后服务,并拿到钱"+money);
    }
}
**
 * 模拟消费者
 */
public class Client {
    public static void main(String[] args) {
        final Produce produce = new Produce();
        /**
         * 动态代理
         *   creat方法参数:
         *            Class:字节码
         *                它是用于指定被代理对象的字节码
         *            Callback:用于提供增强的代码
         *                它是用于我们写如何代理,一般都写一个该接口的实现类,通常情况下都是匿名但不必须
         *                此接口的实现类谁用谁写
         *                一般写该接口的子接口实现类类,MethodInterceptor
         */
        Produce cglibProduce= (Produce) Enhancer.create(produce.getClass(), new MethodInterceptor() {
            /**
             * 执行被代理对象的任何方法都会经过该方法
             *
             * @param proxy
             * @param method
             * @param args                    以上的三个参数和基于接口的中invoke方法的参数一样
             * @param methodProxy:当前执行方法的代理对象
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //增强的代码
                Object returnValue = null;
                //1.获取方法执行参数
                Float money = (Float) args[0];
                //2.判断当前方法是不是销售
                if ("saleProduce".equals(method.getName())) {
                    returnValue = method.invoke(produce, money * 0.8f);

                }
                return returnValue;
            }
        });
        cglibProduce.saleProduce(15555f);
    }
}