本文介绍: 动态代理有两种:① jdk动态代理,要求必须有接口,最终生成的代理类和目标实现相同接口,在com.sun.proxy包下类名为$proxy+数字例如:$proxy6)② cglib动态代理,最终生成的代理类会继承目标类,并且和目标类在相同包下

没有实现接口或者不需要实现接口的类,我们可以通过cglib动态代理对它进行代理。

基于cglib实现的动态代理需要引入第三方cglib库,之后我们可以实现基于子类的动态代理。

使用cglib实现的动态代理也有一个约束条件就是被代理类不能被final修饰

使用cglib实现的动态代理核心是Enhancer类,仔细观察我们发现cglib动态代理的实现过程和JDK实现动态代理的过程极其类似。

提示:务必仔细看代码注释!!!注释上有很详细解释

cglib动态代理

注意:这里需要引入依赖cglib 2.1_3.jar
(如果不是maven项目,也可以手动导入cglibjar包进行测试

<dependency>
	&lt;groupId&gt;cglib</groupId&gt;
	<artifactId>cglib</artifactId>
	<version>2.1_3</version>
</dependency>

测试demo结构

在这里插入图片描述

代码

被代理类 Star

package com.tong;

/**
 * @author tong
 * @Description:被代理类
 */
public class Star {
    public void sing() {
        System.out.println("唱.......");
    }

    public void dance() {
        System.out.println("跳......");
    }
}

被代理类 ChineseCartoon:

package com.tong;

/**
 * 国漫:斗破苍穹
 *
 * @author tong
 * @Description:被代理类
 */
public class ChineseCartoon {

    public String person(String arg1, String arg2) {
        if (arg1.equals("青莲地心火") &amp;&amp; arg2.equals("陨落心炎")) {
            System.out.println("斗破苍穹---萧炎");
            return "萧炎";
        }
        return "123";
    }
}

生成代理类的工厂类 ProxyFactory

package com.tong;

import com.oracle.jrockit.jfr.Producer;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @author tong
 * @Description:生成代理类的工厂
 */
public class ProxyFactory {
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    // 通过该方法可以生成任意目标类所对应的代理类
    public static Object getProxy(Object target) {
        // proxy就是我们创建的代理对象这个对象可以执行被代理类中所有的方法,并且我们可以在代理对象中对被代理类的方法进行增强
        Object proxy = Enhancer.create(target.getClass(), new MethodInterceptor() {
            /**
             * 这一步是整个过程的关键,代理类的实现要通过Enhancer类,我们需要通过Enhancer类中的create方法创建一个代理对象
             * @param o 是一个代理对象引用 (即:增强对象)
             * @param method当前执行,即被拦截的被代理类方法
             * @param objects 是当前执行方法所用的参数索引顺序即为方法定义参数顺序
             * @param methodProxy 指的是当前执行的方法的代理对象
             * @return 通过反射调用method对象所表示的方法, 并获取该方法的返回值
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) {
                Object result = null;
                try {
                    // 提供增强代码
                    System.out.println("[动态代理][日志] " + method.getName() + ",参数:" + Arrays.toString(objects));
                    //通过反射调用method对象所表示的方法,并获取该方法的返回值
                    //在具有指定参数的指定对象上调用此method对象表示的底层方法。
                    //此处就是通过target来确定调用的是具体哪个类中的方法
                    result = method.invoke(target, objects);
                    System.out.println("[动态代理][日志] " + method.getName() + ",结 果:" + result);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("[动态代理][日志] " + method.getName() + ",异常:" + e.getMessage());
                } finally {
                    System.out.println("[动态代理][日志] " + method.getName() + ",方法执行完毕");
                }
                return result;
            }
        });
        // 返回代理对象
        return proxy;
    }
}

测试类 ProxyTest:

package com.tong;

import org.junit.Test;

/**
 * @author tong
 * @Description:测试类
 */
public class ProxyTest {

    @Test
    public void test(){
        Star star = new Star();
        /* proxy就是我们创建的代理对象,这个对象可以执行被代理类中所有的方法,
        并且我们可以在代理对象中对被代理类的方法进行增强,
        注意这里使用了强转,因为getProxy方法的返回值是Object类型的对象*/
        Star proxy = (Star)ProxyFactory.getProxy(star);
        proxy.sing();
        System.out.println("-----------------分割线------------------");
        proxy.dance();
        System.out.println("-----------------分割线------------------");
        // 创建被代理类的对象
        ChineseCartoon chineseCartoon = new ChineseCartoon();
        // 获取代理对象
        ChineseCartoon proxy1 = (ChineseCartoon)ProxyFactory.getProxy(chineseCartoon);
        proxy1.person("青莲地心火","陨落心炎");
    }
}

运行结果

在这里插入图片描述

结论:

测试类中通过父类引用proxy调用了方法proxy.sing()时,由于父类 Star类子类(动态代理类继承【即:代理子类已经重写父类中的sing()】。所以,当使用父类引用proxy调用sing()方法时,实际执行的是动态代理类 (子类) 中的 sing()方法。而在这个动态代理类重写的方法中,又会去调用 MethodInterceptor接口的匿名实现类中重写的 intercept() 方法对父类方法的调用进行拦截【即:在子类中采用方法拦截的技术拦截父类所有的方法调用】。在这个方法中,可以实现对被代理方法的功能增强【即:织入横切逻辑最后通过method.invoke()反射技术来调用父类中的方法。

❤ 由于我们的代理类工厂中有参构造的参数是Object类型的,所以最终实现的效果是动态生成代理类对象。

tips父类类型的引用可以调用父类中定义的所有属性和方法,而对于子类中定义而父类中没有的方法,父类引用是无法调用的。

MethodInterceptor接口的匿名实现类中重写的 intercept() 方法的官方文档解释
在这里插入图片描述

JDK动态代理

代码

接口 Calculator :

package com.tong.spring.calculator;

/**
 * @author tong
 */
public interface Calculator {

    int add(int i, int j);

    int sub(int i, int j);

    int mul(int i, int j);

    int div(int i, int j);
}

接口实现类 CalculatorImpl:

package com.tong.spring.calculator;

/**
 * @author tong
 */
public class CalculatorImpl implements Calculator{

    @Override
    public int add(int i, int j) {
        int result = i + j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
    @Override
    public int sub(int i, int j) {
        int result = i - j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
    @Override
    public int mul(int i, int j) {
        int result = i * j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
    @Override
    public int div(int i, int j) {
        int result = i / j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
}

生产代理对象的工厂类 ProxyFactory:

/**
 * 动态代理有两种:
 * 1. jdk动态代理,要求必须有接口,最终生成的代理类和目标类实现相同的接口,在com.sun.proxy包下类名为$proxy+数字例如:$proxy6)
 * 2. cglib动态代理,最终生成的代理类会继承目标类,并且和目标类在相同包下
 */
public class ProxyFactory {
    private Object target;
    public ProxyFactory(Object target) {
        this.target = target;
    }
    // 通过该方法可以生成任意目标类所对应的代理类
    public Object getProxy(){
        /**
         * newProxyInstance():创建一个代理实例
         * 其中有三个参数:
         * 1、classLoader:指定加载动态生成的代理类的类加载器(注:所有引入第三方类库以及自己编写java类 都是由 应用加载负责加载的)
         【根类加载器(Bootstrap)> 扩展加载器(Extension)> 系统类加载器(System)
         系统类加载器又称为应用类加载器】
         * 2、interfaces:获取目标对象实现的所有接口的class对象所组成的数组
         * 3、invocationHandler:设置代理对象实现目标对象的接口的方法的过程,即代理类中如何重写接口中的抽象方法
         */
        //第一个参数,获取代理对象的类加载器 (类加载器是程序默认的类加载器,一般来说,Java应用的类都是由它来完成加载。所以,此处通过代理类或者被代理类获取到的类加载器都是同一个,或通过任何一个类获取到的类加载器都是同一个。)
        ClassLoader classLoader = this.getClass().getClassLoader();
        //第二个参数,被代理对象实现的所有接口数组
        Class<?>[] interfaces = target.getClass().getInterfaces();
        //当通过代理类的对象发起对被重写的方法的调用时,都会转换为对如下invoke方法的调用
        //代理实例的调用处理程序
        //第三个参数InvocationHandler的实现类,这里用了匿名内部类的方式
        InvocationHandler invocationHandler = new InvocationHandler() {
             //重写InvocationHandler的invoke方法,他有三个参数可以供我们使用
            @Override
            public Object invoke(Object proxy, Method method, Object[] args){
                /**
                 * proxy:表示代理对象
                 * method:表示要执行的方法(代理对象需要实现的抽象方法,即其中需要重写的 和目标类同名的方法)
                 * args:表示要执行的方法的参数列表(method所对应方法的参数列表)
                 */
                Object result = null;
                try {
                    //method.getName(): 返回此method对象表示的方法的名称,作为字符串
                    System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));
                     //通过反射调用method对象所表示的方法,并获取该方法的返回值
                     //在具有指定参数的指定对象上调用此method对象表示的底层方法。
                     //此处就是通过target来确定调用的是具体哪个类中的方法
                     result = method.invoke(target, args);
                     System.out.println("[动态代理][日志] "+method.getName()+",结 果:"+ result);
                  } catch (Exception e) {
                   e.printStackTrace();
                   System.out.println("[动态代理][日志] "+method.getName()+",异常:"+e.getMessage());
                  } finally {
                      System.out.println("[动态代理][日志] "+method.getName()+",方法执行完毕");
                  }
                  return result;
               }   
            }; 
        //返回指定接口的代理类的实例,该实例将方法调用分派给指定的调用处理程序
        return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
      }
}                                     

测试类 ProxyTest:

package com.tong.proxy;

import com.tong.spring.calculator.Calculator;
import com.tong.spring.calculator.CalculatorImpl;
import com.tong.spring.calculator.proxy.ProxyFactory;
import org.junit.Test;

/**
 * @author tong
 */
public class ProxyTest {
    @Test
    public void testDynamicProxy(){
        ProxyFactory factory = new ProxyFactory(new CalculatorImpl());
        Calculator proxy = (Calculator) factory.getProxy();
        //创建好了代理对象,代理对象就可以执行被代理类实现的接口的方法;当通过代理类的对象发起对被重写的方法的调用时,都会转换为对调用处理器实现类中的invoke方法的调用,invoke方法中就可以对被代理类进行功能增强.
        proxy.add(1, 5);
//        proxy.div(1,0);
    }
}

运行结果

在这里插入图片描述

结论:

通过factory.getProxy()创建好了代理对象后,代理对象proxy就可以执行被代理类实现的接口的方法;当通过代理类的对象发起对被重写的方法的调用时,都会转换为对调用处理器实现类中的invoke方法的调用,invoke方法中就可以对被代理类进行功能增强并通过反射调用被代理的同名方法。

编写不易,有帮到各位朋友理解的,点个赞再走哦!๑(≥▽≤)๑

原文地址:https://blog.csdn.net/weixin_43935152/article/details/130514814

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_39376.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注