动态代理的详解(jdk和cglib)

问题:

           在java中如何实现对程序的扩展,对程序的扩展有那几种方式?

面对这种问题的时候,不由得想起了java封装,继承,多态等。其实也就三种方式,面向对象的方式,包装模式,动态代理。今天重点介绍一下动态代理。

 

动态代理:

代理类具用以下属性:

  • 代理类是公共的、最终的,而不是抽象的。
  • 未指定代理类的非限定名称。但是,以字符串 "$Proxy" 开头的类名空间应该为代理类保留。
  • 代理类扩展 java.lang.reflect.Proxy
  • 代理类会按同一顺序准确地实现其创建时指定的接口。
  • 如果代理类实现了非公共接口,那么它将在与该接口相同的包中定义。否则,代理类的包也是未指定的。注意,包密封将不阻止代理类在运行时在特定包中的成功定义,也不会阻止相同类加载器和带有特定签名的包所定义的类。
  • 由于代理类将实现所有在其创建时指定的接口,所以对其 Class 对象调用 getInterfaces 将返回一个包含相同接口列表的数组(按其创建时指定的顺序),对其 Class 对象调用 getMethods 将返回一个包括这些接口中所有方法的 Method 对象的数组,并且调用 getMethod 将会在代理接口中找到期望的一些方法。
  • 如果 Proxy.isProxyClass 方法传递代理类(由 Proxy.getProxyClass 返回的类,或由 Proxy.newProxyInstance 返回的对象的类),则该方法返回 true,否则返回 false。
  • 代理类的 java.security.ProtectionDomain 与由引导类加载器(如 java.lang.Object)加载的系统类相同,原因是代理类的代码由受信任的系统代码生成。此保护域通常被授予 java.security.AllPermission
  • 每个代理类都有一个可以带一个参数(接口 InvocationHandler 的实现)的公共构造方法,用于设置代理实例的调用处理程序。并非必须使用反射 API 才能访问公共构造方法,通过调用 Proxy.newInstance 方法(将调用 Proxy.getProxyClass 的操作和调用带有调用处理程序的构造方法结合在一起)也可以创建代理实例。

动态代理类:

(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。 代理接口 是代理类实现的一个接口。 代理实例 是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序 对象,它可以实现接口 InvocationHandler。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的 Invoke 方法,并传递代理实例、识别调用方法的 java.lang.reflect.Method 对象以及包含参数的 Object 类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。 

JDK动态代理:

Proxy

jdk的动态代理是有Proxy 类实现的,

Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

创建一个接口的代理:

ClassLoader loader类加载器 , 用来加载代理对象
Class<?>[] interfaces目标类的字节码对象数组. 因为代理的是接口,需要知道接口中所有的方法
InvocationHandler h执行句柄, 代理对象处理的核心逻辑就在该接口中

接口 InvocationHandler:
 

proxy

在其上调用方法的代理实例

method

Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。

args

包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(

 案例流程分析:

功能:完成用户登录,记录日志

核心代码:

//1.声明目标对象  UserDaoImpl
    private UserDaoImpl  target;

    //2.创建有参构造

    public ProxyBeanFactory(UserDaoImpl target) {
        this.target = target;
    }

    //3.创建一个获取代理对象的方法
    public Object  getProxyBean(){
        /**
         * Proxy.newProxyInstance 返回一个代理对象
         * 参数1:类加载器
         * 参数2:接口的字节码数组对象
         * 参数3: InvocationHandler 接口的匿名内部类对象  代理对象执行的核心逻辑
         */
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 当代理对象调用login方法的时候,就会进入invoke方法
                     * 参数1:Object proxy 代理对象 在内存中生成
                     * 参数2:执行方法的对象
                     * 参数3:方法传递的实际参数
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //1.调用原方法
                        method.invoke(target);
                        //2.模拟记录日志
                        System.out.println("记录日志完毕");
                        return null;
                    }
                });
    }

 

Cglib动态代理:(maven环境)

该实现代理的方式是spring提供.,所以需要引入spring的依赖 。Enhance 是cglib的核心类,主要用来创建代理对象

核心方法介绍
setSuperClass()设置目标类的字节码对象
setCallback()设置回调函数, 目的让代理对象执行的时候进入回调函数执行具体的逻辑 ; 该方法的参数
Callback 接口 
create()创建代理对象的方法

案例分析:

功能:完成用户登录,记录日志

核心代码:

 

 //1.声明目标类对象
    private PersonService target;

    //2.提供有参构造

    public ProxyBeanFactory(PersonService target) {
        this.target = target;
    }

    //3.创建方法获取代理对象
    public Object getProxyBean(){
        //1)创建Enhance核心对象
        Enhancer enhancer = new Enhancer();
        //2)设置SuperClass
        enhancer.setSuperclass(target.getClass());
        //3)设置回调函数
        /**
         * 给该回调函数中传递一个Callback的子接口 MehtodInterceptor 的匿名内部类
         * MehtodInterceptor 接口中有intercept方法拦截
         */
        enhancer.setCallback(new MethodInterceptor() {
            /**
             * intercept 函数当代理对象执行目标方法时,进入该拦截方法,执行里面的内容
             * 参数1:Object proxy 代理对象
             * 参数2:Method method 当前执行的方法对象
             * 参数3:原目标方法的实际传入的参数Object[] objects
             * 参数4: MethodProxy methodProxy 代理方法对象
             */

            public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                method.invoke(target);
                System.out.println("记录日志");
                return null;
            }
        });
        //4.返回create的代理对象
        return enhancer.create();
    }

环境准备:(强调)

 <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>
    </dependencies>

 

总结:

动态代理:jdk侧重于接口,cglib侧重于类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值