• 动态代理


    动态代理:
     *  特点:(代理对象的)字节码随用随创建,随用随加载,

    它与装饰者模式的区别就是:装饰者模式是必须写好一个类,而动态代理是字节码随着用来创建和加载的

     *  作用:不修改源码的基础上对方法增强

    *  分类:
     *      基于接口的动态代理
     *      基于子类的动态代理

    基于接口的动态代理:

    涉及的类:Proxy,提供者:JDK官方

    被代理类(Producer)、被代理对象(producer)、代理对象(proxyProducer)

    *  如何创建代理对象:使用Proxy中的静态方法newProxyInstance()创建。

     *  创建代理对象的要求:被代理类(Producer)最少实现一个接口如果没有则不能使用

    *  newProxyInstance方法的参数:
    1、ClassLoader类加载器:它是用于加载代理对象proxyProducer)字节码的写的是被代理类对象的类加载器被代理对象producer)使用相同的类加载器。即代理谁就写谁的类加载器。固定写法。
    2、Class[]字节码数组:它是用于让代理对象(proxyProducer)和被代理对象(producer)有相同方法。固定写法。只要两个都实现同一个接口,那么两个都可以有接口中的方法。代理谁就写谁的getClass().getInterfaces()。
    3、 InvocationHandler:用于提供增强的代码,它是让我们写如何代理。我们一般都是写一个该接口InvocationHandler的实现类,通常情况下都是匿名内部类,但不是必须的。 此接口的实现类都是谁用谁写。

    匿名内部类的Invoke方法:

    匿名内部类的Invoke方法的作用:代理对象调用被代理对象的任何接口方法saleProduct)都会经过该方法该方法有拦截的功能

    Invoke方法的三个参数的含义:

    1、proxy指代理对象的引用,也就是说方法中如果想用代理对象可以用它,但是一般不用。

    2、method表示当前执行的方法。即代理对象调用的接口方法被封装为的Method对象

    3、args:当前执行方法所需的参数

    Invoke方法的返回值:和被代理对象方法的返回值相同

    Method.invoke方法的第一个参数表示被代理对象(真实对象),第二个参数为方法的参数。即使用被代理对象的方法。

    当匿名内部类访问外部成员变量时,外部成员要求是final修饰的。

    基于接口的动态代理案例

    1、创建maven的jar工程,没有依赖

    2、创建一个Producer类(被代理类),这是生产厂家原来做的,现在把这些事交给代理商,Producer要想实现代理商的要求,就要实现IProducer。

    public class Producer implements IProducer{
        // 销售
        public void saleProduct(float money){
            System.out.println("销售产品,并拿到钱:"+money);
        }
        //售后
        public void afterService(float money){
            System.out.println("提供售后服务,并拿到钱:"+money);
        }
    }

    注意:实现了接口。

    3、创建IProducer的接口,这是对生产厂家要求的接口,必须有销售和售后

    public interface IProducer {
        // 销售
        public void saleProduct(float money);
        //售后
        public void afterService(float money);
    }

    4、 创建Client类,来模拟一个消费者

    public class Client {
        public static void main(String[] args) {
            final Producer producer = new Producer();  // 被代理对象
           IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),  // 代理对象
                    producer.getClass().getInterfaces(),
                    new InvocationHandler() {
                        /**
                         * 作用:执行被代理对象的任何接口方法都会经过该方法
                         * 方法参数的含义
                         * @param proxy   代理对象的引用
                         * @param method  当前执行的方法
                         * @param args    当前执行方法所需的参数
                         * @return        和被代理对象方法有相同的返回值
                         * @throws Throwable
                         */
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            //提供增强的代码
                            Object returnValue = null;
    
                            //1.获取方法执行的参数
                            Float money = (Float)args[0];
                            //2.判断当前方法是不是销售
                            if("saleProduct".equals(method.getName())) {
                                returnValue = method.invoke(producer, money*0.8f);
                            }
                            return returnValue;
                        }
                    });
            proxyProducer.saleProduct(10000f);
        }
    }

     这消费者给的10000块钱,厂家只能拿到8000块钱,2000块钱是代理商的利润。

     我们并没有在生产厂家(producer)的方法上进行任何的修改,但是已经对这个方法进行了增强。这就是基于接口的动态代理,但是它有一个问题:就是被代理类不实现任何接口的时候是不能用的,难道真的没有办法代理一个普通的java类吗?

    基于子类的动态代理(代理普通的java类)

    涉及的类:Enhancer,提供者:第三方cglib

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

    创建代理对象的要求:被代理类不能是最终类,最终类不能创建子类,也就没法创建代理对象了

    create方法的参数:

    1、Class字节码:它是用于指定被代理对象的字节码。
    2、Callback:用于提供增强的代码,它是让我们写如何代理。我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。

    此接口的实现类都是谁用谁写。

    我们一般写的都是该接口(Callback)的子接口MethodInterceptor的实现类,

    执行被代理对象的任何方法都会经过intercept方法,前面的三个参数proxy、method、args和基于接口的动态代理中invoke方法的参数是一样的。methodProxy:当前执行方法的代理对象,这个用不上。返回值与被代理对象方法的返回值是一样的。

    基于子类的动态代理案例

     1、创建maven的jar工程,引入依赖

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

    这种动态代理要求有第三方jar包的支持:

    2、创建Producer类(被代理类),注意该类没有实现接口。  

    public class Producer {
        // 销售
        public void saleProduct(float money){
            System.out.println("销售产品,并拿到钱:"+money);
        }
        //售后
        public void afterService(float money){
            System.out.println("提供售后服务,并拿到钱:"+money);
        }
    }

    3、创建Client类,来模拟一个消费者

    public class Client {
        public static void main(String[] args) {
            final Producer producer = new Producer();
            Producer cglibProducer = (Producer)Enhancer.create(producer.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("saleProduct".equals(method.getName())) {
                        returnValue = method.invoke(producer, money*0.8f);
                    }
                    return returnValue;
                }
            });
            cglibProducer.saleProduct(12000f);
        }
    }
  • 相关阅读:
    TopCoder SRM502 Div1 500 贪心 01背包
    TopCoder SRM502 Div1 1000 动态规划
    LOJ#6433. 「PKUSC2018」最大前缀和 状压dp
    Codeforces 830D Singer House 动态规划
    Codeforces 830C Bamboo Partition 其他
    UOJ#275. 【清华集训2016】组合数问题 数位dp
    Codeforces 806 D. Perishable Roads Dijkstra
    UOJ#53. 【UR #4】追击圣诞老人 树链剖分 k短路
    Java第二天——标识符命名规则、Java的知识、快捷键的使用、Scanner获取值的常用方法
    Scanner的例子
  • 原文地址:https://www.cnblogs.com/zwh0910/p/14627120.html
Copyright © 2020-2023  润新知