java学习之动态代理

动态代理:应用程序发布后,通过动态创建代理对象;动态代理可以动态地创建代理并动态地处理对所代理方法的调用。

其中动态代理又可分为:

一、JDK原生动态代理

二、CGLIB动态代理

1.JDK原生动态代理

JDK动态代理只能针对实现了接口的类生成代理。

只需要一个代理类,而不是针对每个类编写代理类。

新增一个动态代理类实现实现了 InvocationHandler 接口,那么必须实现该接口的 invoke 方法。

通过调用静态方法Proxy.newProxyInstance()可以创建动态代理,这个方法需要

一个类加载器(通过可以从已经被加载的对象中获取其类加载器)
你希望代理实现的接口列表(不是类或抽象类)
InvocationHandler接口的一个实现
动态代理可以将所有调用重定向到调用处理器,因此通常会向调用处理器的构造器传递给一个“实际”对象的引用,从而使得调用处理器在执行其中介任务时,可以将请求转发。

在invoke()内部,在代理上调用方法时需要格外小心,因为对接口的调用将被重定向为对代理的调用。

Method.invoke()将请求转发给被代理对象,并传入必需的参数。
 

package com.zoo.lion.modules.test.proxy.demo2;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Author: xf
 * @Date: 2019/7/12 10:36
 * @Version 1.0
 */
public class DynamicProxyHandler implements InvocationHandler {


    private Object target;//用于接收具体实现类的实例对象

//    public DynamicProxyHandler(Object object) {
//        this.target = object;
//    }

    public DynamicProxyHandler() {
    }

    public Object build(Object object) {
        this.target = object;
        //通过反射机制,创建一个代理类对象实例并返回。用户进行方法调用时使用
        //创建代理对象时,需要传递该业务类的类加载器(用来获取业务实现类的元数据,在包装方法是调用真正的业务方法)、接口、handler实现类
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("request")) {
            long start = System.currentTimeMillis();
            Object object = method.invoke(target, args);
            long end = System.currentTimeMillis();
            System.out.println(end - start);
            return object;
        }
        return null;
    }


    public static void main(String[] args) {
        /**
         * ClassLoader 生成代理类
         * 代理类应该实现的接口
         * 实现InvocationHandler的切面类
         */
//        UserService userService = (UserService) Proxy.newProxyInstance(
//                UserService.class.getClassLoader(),
//                new Class[]{UserService.class},
//                new DynamicProxyHandler(new UserServiceImpl())
//        );

        DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler();
        UserService userService = (UserService) dynamicProxyHandler.build(new UserServiceImpl());
        userService.request();
    }
}

JDK动态代理有个局限是只能针对实现了接口的类生成代理,如果没有实现接口的类想生成代理呢,那么就能用CGLIB动态代理

 2.CGLIB动态代理

CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。

新建一个Song类:

package com.zoo.lion.modules.test.proxy.demo2;

/**
 * @Author: xf
 * @Date: 2019/7/12 11:40
 * @Version 1.0
 */
public class Song {

    public void sing(String songName) {
        System.out.println("请taylor唱一首:" + songName);
    }

}

加一个CGLibProxy类实现MethodInterceptor接口,要实现intercept这个方法:

package com.zoo.lion.modules.test.proxy.demo2;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @Author: xf
 * @Date: 2019/7/12 11:35
 * @Version 1.0
 */
public class CGLibProxy implements MethodInterceptor {
    private Object target;//业务对象

    //相当于JDK动态代理中的绑定
    public Object getInstance(Object target) {
        //绑定业务对象
        this.target = target;
        //创建加强器,用来创建动态代理类
        Enhancer enhancer = new Enhancer();
        //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
        enhancer.setSuperclass(this.target.getClass());
        //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
        enhancer.setCallback(this);
        // 创建动态代理类对象并返回
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //这里调用切面方法before
        System.out.println("方法执行前");
        //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        Object object = method.invoke(this.target, objects);//调用目标类的目标方法
        System.out.println("方法执行后");
        //这里调用切面方法after
        return object;
    }

    public static void main(String[] args) {
        CGLibProxy cgLibProxy = new CGLibProxy();
        Song song = (Song) cgLibProxy.getInstance(new Song());
        song.sing("blank space");
    }
}

执行结果:

cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。 

总结:

 静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;

    JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;

    CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;


1.JDK动态代理是实现了被代理对象的接口。cglib是继承了被代理对象。
2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。

教科书

我们不管是看书还是看文章亦或是我那个上搜索参考答案,可能很多时候,都可以找到如下的回答:关于两者之间的性能的话,JDK动态代理所创建的代理对象,在以前的JDK版本中,性能并不是很高,虽然在高版本中JDK动态代理对象的性能得到了很大的提升,但是他也并不是适用于所有的场景。主要体现在如下的两个指标中:

  • 1、CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍;
  • 2、但是CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;
  • 3、因此,对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLib动态代理,反正,则比较适用JDK动态代理。

测试总结:

最终的测试结果大致是这样的,在1.6和1.7的时候,JDK动态代理的速度要比CGLib动态代理的速度要慢,但是并没有教科书上的10倍差距,在JDK1.8的时候,JDK动态代理的速度已经比CGLib动态代理的速度快很多了

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值