jdk动态代理跟cglab动态代理区别 -- 总结

说时迟那时快,一段操作猛如虎,代理模式其实就两种,jdk自带的代理模式,和cglib提供的代理模式

动态代理

啥叫做动态代理,可以理解为中介,比如我写了一个方法,然后张三需要,张三需要如果不找我就找中介,中介帮忙处理一下
,可以说等于又进了一层。可能中介再用这个方法,还给你加点小广告啥的。完了这就是动态代理

jdk动态代理
public class JdkProxy implements InvocationHandler {
	//需要代理的目标对象
    private Object target;

    //定义获取代理对象方法
    private Object getJDKProxy(Object targetObject) {
        //为目标对象target赋值
        this.target = targetObject;
        //JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
    }

	
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK动态代理,监听开始!");
        Object result = method.invoke(target, args);
        System.out.println("JDK动态代理,监听结束!");
        return result;
    }


    public static void main(String[] args) {
        JdkProxy jdkProxy = new JdkProxy();//实例化JDKProxy对象
        UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());//获取代理对象
        user.addUser("admin", "123123");//执行新增方法
    }

invoke 其实就算调用方法,实现InvocationHandler调用处理程序这个接口重写的方法,我们可以看看说明

InvocationHandler是由代理实例的调用处理程序实现的接口。

每个代理实例都有一个关联的调用处理程序。 在代理实例上调用方法时,该方法调用将被编码并分派到其调用处理程序的invoke方法。
自从:1.3

处理代理实例上的方法调用并返回结果。 在与之关联的代理实例上调用方法时,将在调用处理程序上调用该方法。

invoke 方法说明

参数:
proxy-在其上调用方法的代理实例
method –与在代理实例上调用的接口方法相对应的Method实例。 Method对象的声明类将是在其中声明该Method的接口,它可能是代理类通过其继承该方法的代理接口的超接口。
args –包含在代理实例的方法调用中传递的参数值的对象数组;如果接口方法不带参数,则为null 。 基本类型的参数包装在适当的基本包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。

返回值:
从代理实例上的方法调用返回的值。 如果接口方法的声明的返回类型是原始类型,则此方法返回的值必须是对应的原始包装器类的实例;否则,返回true。 否则,它必须是可分配给声明的返回类型的类型。 如果此方法返回的值为null ,并且接口方法的返回类型为原始类型,则代理实例上的方法调用将抛出NullPointerException 。 如果此方法返回的值与上述接口方法的声明的返回类型不兼容,则对代理实例的方法调用将抛出ClassCastException 。

抛出:
Throwable –从代理实例上的方法调用引发的异常。 异常的类型必须可以分配给接口方法的throws子句中声明的任何异常类型,也可以分配给未经检查的异常类型java.lang.RuntimeException或java.lang.Error 。 如果此方法引发的检查异常无法分配给接口方法的throws子句中声明的任何异常类型,则包含该方法引发的异常的UndeclaredThrowableException将由该方法的调用引发。代理实例。

在这里插入图片描述

被代理的方法就比较简单了

public interface UserManager {

    /**
     * 新增用户抽象方法
     *
     */
    void addUser(String userName, String password);

    /**
     * 删除用户抽象方法
     *
     * @param userName
     */
    void delUser(String userName);


}


public class UserManagerImpl implements UserManager {


    /**
     * 新增用户抽象方法
     *
     */
    @Override
    public void addUser(String userName, String password) {
        System.out.println("调用了新增的方法!");
        System.out.println("传入参数为 userName: " + userName + " password: " + password);


    }
    /**
     * 删除用户抽象方法
     *
     * @param userName
     */
    @Override
    public void delUser(String userName) {

        System.out.println("调用了删除的方法!");
        System.out.println("传入参数为 userName: " + userName);
    }

核心代码了解一下


    private Object target;//需要代理的目标对象

    //定义获取代理对象方法
    private Object getJDKProxy(Object targetObject) {
        //为目标对象target赋值
        this.target = targetObject;
        //JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), 		  targetObject.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK动态代理,监听开始!");
        Object result = method.invoke(target, args);
        System.out.println("JDK动态代理,监听结束!");
        return result;
    }

中文翻译下, 如果要调用getJDKProxy方法,需要传被代理的实现类。 传入之后,赋值给变量需要代理的方法,然后代理类创建一个实例,传入类加载器,需要代理的接口名称,还有this也就是需要代理代理器,再本类所以就直接用this了

也就是说,如果用户调用getJDKProxy 方法,他就会被代理类进行处理,代理会除法invoke方法。然后回执行输出,然后方法进行调用,调用完成之后再输出 可以测试一下


    public static void main(String[] args) {
        JdkProxy jdkProxy = new JdkProxy();//实例化JDKProxy对象
        UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl());//获取代理对象
        user.addUser("admin", "123123");//执行新增方法
    }
    

在这里插入图片描述

中间两个输出是方法输出, 上下两个是动态代理输出;

所以你想这个东西可以做什么,你想啊,写一个方法是不是要打印日志啊,看入参,方法结束之后是不是要看日志或者后处理,什么的,这不就用上了;

至于还有一种代理模式,也一起写了,大家看看 cglib

Cglib

// 很直白的跟你说,着是方法拦截器
public class CgLibProxy implements MethodInterceptor {


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("JDK动态代理,监听开始!");
        methodProxy.invokeSuper(o, objects);
        System.out.println("JDK动态代理,监听结束!");
        return null;
    }

    public static void main(String[] args) {
        //创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        //设置父类,即要代理的类
        enhancer.setSuperclass(UserManagerImpl.class);
        //设置回调对象
        enhancer.setCallback(new CgLibProxy());
        //创建代理类
        UserManager userManager = (UserManager) enhancer.create();
        //通过代理类调用目标方法
        userManager.addUser("admin", "123123");//执行新增方法
    }

}


参数解释 :

  • Object var1 代表的是子类代理对象,
  • Method var2代表的是要调用的方法反射对象,
  • 第三个参数是传递给调用方法的参数,前三个参数和JDK的InvocationHandler接口的invoke方法中参数含义是一样的
  • 第四个参数MethodProxy对象是cglib生成的用来代替method对象的,使用此对象会比jdk的method对象的效率要高如果使用method对象来调用目标对象的方法: method.invoke(var1, var3),则会陷入无限递归循环中, 因为此时的目标对象是目标类的子代理类对象

说下为啥要写一个增强剂Enhancer进行处理
MethodProxy 是代理对象,用来代替method对象的

Enhancer

生成动态子类以启用方法拦截。 此类开始是JDK 1.3附带的标准动态代理支持的替代,但该类允许代理除了实现接口外,扩展一个具体的基类。 动态生成的子类覆盖超类的非最终方法,并具有钩子,这些钩子回调到用户定义的拦截器实现。
最原始和最通用的回调类型是MethodInterceptor ,按AOP术语,它启用“周围建议”-也就是说,您可以在调用“ super”方法之前和之后都调用自定义代码。 另外,您可以在调用super方法之前修改参数,或者根本不调用它。

尽管MethodInterceptor具有足够的通用性,可以满足任何拦截需求,但它通常是过大的。 为了简化和提高性能,还提供了其他专用的回调类型,例如LazyLoader 。 通常一个回调将每个增强类可以使用,但你可以控制回调在每一方法的基础与使用CallbackFilter 。
此类的最常见用法体现在静态帮助器方法中。 对于高级需求,例如自定义要使用的ClassLoader ,您应该创建一个Enhancer的新实例。 CGLIB中的其他类遵循类似的模式。
除非使用setUseFactory显式禁用此功能,否则所有增强的对象均实现Factory接口。 Factory接口提供用于更改现有对象的回调的API,以及创建相同类型新实例的更快,更轻松的方法。

有关java.lang.reflect.Proxy的几乎直接替代品,请参见Proxy类。

也就是说创建增强剂,代理子类,设置要代理的方法,回调方法拦截,调用方法的时候,会进行回调到方法拦截器

CGLIB所创建的动态代理对象的性能比JDK所创建的动态代理对象的性能高很多,但创建动态代理对象时比JDK创建动态代理对象要花费更长的时间。

看一下返回的参数
在这里插入图片描述

总结

没啥区别,如果说cglib和jdk最大的区别,我觉得性能都是可以接收的,实现方式jdk是通过methed反射技术来处理的 都需要实现接口,一个是方法拦截器,一个是方法调用处理程序

同时哈,cglib实现字节码进行处理的,性能要好一点,但是创建动态代理要耗时一点,这个需要看源码就可以看到反色调用方法是循环处理的,cglib是字节码处理的

再则
jdk 动态代理,class文件缓存在内存,在运行时动态生成代理类进行处理的,只能基于接口进行代理。
cglib 动态代理,是通过继承代理,

还有就是jdk 只能接口进行代理,局限性
cglib 可以代理类

个人觉得局限性在与一个只能通过类进行代理类处理,但是呢cglib可以对方法,比如说一些逻辑组件处理,使用代理类处理。然后逻辑组件再用切面可以适配所有方法。我感觉很爽

所以一般我再写切面处理日志或者一些解耦的东西都是用cglib处理的

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值