Java的动态代理

Java动态代理详解
本文深入解析Java动态代理机制,涵盖静态代理与动态代理的区别,重点介绍JDK动态代理与CGLib动态代理的实现原理及代码示例。通过具体案例,展示如何利用动态代理在不修改源码的情况下对方法进行增强。

Java的动态代理

功能增强的方式:
子类继承父类,重写父类方法
装饰者模式(包装类,静态代理),在包装类里进行功能增强
代理模式(动态代理),在代理类里进行功能增强
装饰者模式的使用步骤:
创建一个包装类,要求 和目标类实现相同的接口:保证有共同的方法
包装类里要有成员变量(目标对象),通过构造方法设置进去
包装类里不需要增强的方法:直接调用目标对象的方法
包装类里需要增强的方法:编写增强的功能代码

  1. 什么是代理
    代理:如果不能、不方便直接调用目标对象的时候,可以通过代理对象,间接调用目标对象
    两种代理方式:
    静态代理:代理类是提前创建好的(装饰者模式)
    动态代理:代理类不存在,我们代码在运行过程中,动态生成的代理类、代理对象,之后再调用代理对象
  2. Java的动态代理
    2.1 API
    注意:
    哪怕没有目标对象,这个方法也可以给我们生成一个接口的实现类对象(没有被代理对象了,所有功能要由代理类来完成)
    这个生成代理对象的方法,是基于接口的
    如果没有接口,又想要某对象的代理对象,可以使用一个包:cglib
 Object proxyInstance = Proxy.newProxyInstance(
  ClassLoader loader,
    Class[] interfaces,
    InvocationHandler h
  );

参数:
loader:类加载器,通常是从目标对象里得到:目标对象.getClass().getClassLoader()
interfaces:代理类要实现的接口,通常也可以从目标对象得到:目标对 象.getClass().getInterfaces()
h:代理类的方法里,要执行的内容(要做的事情)
通常是创建一个匿名内部类:InvocationHandler,接口里只有一个方法invoke

new InvocationHandler(){
     /*
    proxy:代理对象
         method:执行的方法(我们调用代理对象时,所调用的方法Method对象)
         args:方法需要的实参(我们调用代理对象的方法时,传递的实参)
     */
     public Object invoke(Object proxy, Method method, Object[] args){
         
         //可以调用目标对象的method方法
         Object result = method.invoke(目标对象, args);
         
         //我们调用代理对象方法时,代理对象给我们返回的结果
         return result;
    }
 }

InvocationHandler匿名内部类,接口里的方法invoke中,是动态代理的功能部分,在方法里对目标对象的控制优先于代理对象在外边的控制,并且在方法中,相当于每个目标对象对应的方法是在invoke方法中循环出现一样(如此理解),要增强目标对象的方法,可以通过方法名来判断,来进行功能增强(分前后).

返回值:一定是我们提供的接口的一个实现类对象,是代理对象

动态代理常用的有两种方式
基于接口的动态代理
提供者:JDK官方的Proxy类。
要求:被代理类最少实现一个接口。
基于子类的动态代理
提供者:第三方的CGLib,如果报asmxxxx异常,需要导入asm.jar。
要求:被代理类不能用final修饰的类(最终类)。
1.使用JDK官方的Proxy类创建代理对象

创建ProxyProducer接口,代码如下:

public interface ProxyProducer {

    /**
     * 销售商品
     * @param money
     */
    public void saleProduct(Float money);

    /**
     * 售后服务
     * @param money
     */
    public void afterService(Float money) ;
}

创建Producer类实现ProxyProducer接口,代码如下:

public class Producer implements ProxyProducer {

    /**
     * 销售商品
     * @param money
     */
    public void saleProduct(Float money) {
        System.out.println("销售商品,金额是:"+money);
    }

    /**
     * 售后服务
     * @param money
     */
    public void afterService(Float money) {
        System.out.println("提供售后服务,金额是:"+money);
    }
}

创建JDK动态代理,代码如下:

 public class Consumer {public static void main(String[] args) {
         //创建对象实例
         Producer producer = new Producer();
         producer.saleProduct(5000f);
         producer.afterService(1000f);/***
          * 基于动态代理改造
          * 动态代理:
          *     特点:字节码随用随创建,随用随加载
          *     分类:基于接口的动态代理,基于子类的动态代理
          *     作用:不修改源码的基础上对方法增强
          *     基于接口的动态代理:
          *         提供者是:JDK官方
          *         使用要求:被代理类最少实现一个接口。
          *         涉及的类:Proxy
          *         创建代理对象的方法:newProxyInstance
          *         方法的参数:
          *             ClassLoader:类加载器。用于加载代理对象的字节码。和被代理对象使用相同的类加载器。固定写法。
          *             Class[ ]:字节码数组。用于给代理对象提供方法。和被代理对象具有相同的方法。和被代理对象实现相同的接口,就会具有相同的方法。固定写法
          *             InvocationHanlder:要增强的方法。此处是一个接口,我们需要提供它的实现类。通常写的是匿名内部类。
          *             增强的代码随用随写。
          */
         ProxyProducer proxyProducer = (ProxyProducer) Proxy.newProxyInstance(
                 Producer.class.getClassLoader(),
                 Producer.class.getInterfaces(),
                 new InvocationHandler() {
                     /**
                      * 执行被代理对象的任何方法都都会经过该方法,该方法有拦截的作用
                      * 参数的含义
                      * Object proxy:代理对象的引用。一般不用
                      * Method method:当前执行的方法
                      * Object[] args:当前方法所需的参数
                      * 返回值的含义
                      * 和被代理对象的方法有相同的返回值
                      */
                     @Override
                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                         //方法参数
                         Float money = (Float) args[0];//获取方法名字
                         String methodName = method.getName();//定义一个对象,存储返回结果
                         Object result = null;//销售提成25%
                         if(methodName.equals("saleProduct")){
                             result= method.invoke(producer,money*0.75f);
                        }else if(methodName.equals("saleProduct")){
                             //销售提成10%
                             result= method.invoke(producer,money*0.9f);
                        }
                         return result;
                    }
                }
        );
 ​
         proxyProducer.saleProduct(1000f);
         proxyProducer.afterService(1000f);
    }
 }

2.使用CGLib的Enhancer类创建代理对象
还是那个例子,只不过不让他实现接口,此时不能使用JDK的动态代理了,我们可以使用CGLib动态代理,首先我们需要引入依赖。

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

修改Producer,不让它实现任何接口,代码如下:

public class Producer {

    /**
     * 销售商品
     * @param money
     */
    public void saleProduct(Float money) {
        System.out.println("销售商品,金额是:"+money);
    }

    /**
     * 售后服务
     * @param money
     */
    public void afterService(Float money) {
        System.out.println("提供售后服务,金额是:"+money);
    }
}


创建Consumer,使用CGLib动态代理,代码如下:

public class Consumer {

    public static void main(String[] args) {
        //创建对象实例
        Producer producer = new Producer();
        producer.saleProduct(5000f);
        producer.afterService(1000f);
        /**
         * 动态代理:
         * 	 特点:字节码随用随创建,随用随加载
         * 	 分类:基于接口的动态代理,基于子类的动态代理
         * 	 作用:不修改源码的基础上对方法增强
         * 	 基于子类的动态代理
         * 		提供者是:第三方cglib包,在使用时需要先导包(maven工程导入坐标即可)
         * 		使用要求:被代理类不能是最终类,不能被final修饰
         * 		涉及的类:Enhancer
         * 		创建代理对象的方法:create
         * 		方法的参数:
         * 			Class:字节码。被代理对象的字节码。可以创建被代理对象的子类,还可以获取被代理对象的类加载器。
         * 			Callback:增强的代码。通常都是写一个接口的实现类或者匿名内部类。
         * 					 我们在使用时一般都是使用Callback接口的子接口:MethodInterceptor
         */
        Producer proxyProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                //方法参数
                Float money = (Float) args[0];

                //获取方法名字
                String methodName = method.getName();

                //定义一个对象,存储返回结果
                Object result = null;

                //销售提成25%
                if(methodName.equals("saleProduct")){
                    result= method.invoke(producer,money*0.75f);
                }else if(methodName.equals("saleProduct")){
                    //销售提成10%
                    result= method.invoke(producer,money*0.9f);
                }
                return result;
            }
        });

        proxyProducer.saleProduct(1000f);
        proxyProducer.afterService(1000f);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值