Spring学习之动态代理(JDK动态代理和CGLIB动态代理)

所属分类: 软件编程 / java 阅读数: 14
收藏 0 赞 0 分享

前言

动态代理,是一种通过运行时操作字节码,以达到增强类的功能的技术,也是Spring AOP操作的基础,关于AOP的内容,将在后面的笔记中详细讲解,本小节主要是理清楚动态代理,毕竟,Spring的AOP是基于动态代理技术,对动态代理技术有所了解,对于学习Spring AOP也会有帮助

动态代理技术详解

动态代理,现在主要是用于增强类的功能,同时由于是具有动态性,所以避免了需要频繁创建类的操作,同时,也使得原有的代码在不需要改变的情况下,对类的功能进行增强,主要的动态代理技术有:通过实现目标接口,重写其方法,以增强其能力,典型的以JDK动态代理为代表;或者,通过继承类,重写其方法以增强其能力,典型的以CGLib为代表,这两种技术分别从不同的方向来对类的能力进行扩充,接下来来具体看下这两种技术的特点以及差异。

基于JDK动态代理

基于JDK的动态代理技术,其主要特点就是目标类,也就是需要被代理的类,必须有接口,并且代理类必须实现跟它一样的接口,从而来起到代理其事务的功能,具体使用如下代码所示,假设有一个UserService类,主要用于负责用户的登录和退出,同时,有个日志类,负责记录用户的操作信息,直接将信息日志写在对应的UserService实现类中,可以达到目的,但显然这种方式不是很合理,特别是在UserService有很多个方法需要做日志记录的时候,就会使得日志记录代码遍布整个UserService,不仅使得代码的冗余很大,而且当需要进行修改的时候,也需要逐个修改,非常麻烦,这个时候,采用动态代理技术就是一种非常好的方法了。

/**
 * UserService接口
 */
interface UserService{

  void login();
  void logout();
}

/**
 * UseService实现类
 */
class UserServiceImpl implements UserService{

  @Override
  public void login() {
    System.out.println("someone login....");
  }

  @Override
  public void logout() {
    System.out.println("someone logout....");
  }
}


/**
 * 实现InvocationHandle接口,用于织入所要增强的代码
 */
class UserServiceHandle implements InvocationHandler{

  private UserService userService;

  public UserServiceHandle(UserService userService) {
    this.userService = userService;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    LogService.info();
    Object object = method.invoke(userService, args);
    LogService.info();
    return object;
  }
}

/**
 * 代理类工厂,用于产生UseService类的代理类
 */
class ProxyFactory{

  public static UserService getProxyObject(UserService userService){

    // 使用JDK动态代理技术来创建对应的代理类
    return (UserService) Proxy.newProxyInstance(
        userService.getClass().getClassLoader(),
        userService.getClass().getInterfaces(),
        new UserServiceHandle(userService)
    );
  }
}

这样,当我们需要使用UseService类的时候,只需要从ProxyFactory中获取即可,而且获取的对象是UserService对象的代理类,也就是说,获得的对象是UserService对象的增强版

基于CGLib的动态代理技术

从上面的ProxyFactory工厂中可以看到,在使用JDK进行创建动态代理对象的时候,需要为其提供接口,或者说,如果所要增强的目标类没有实现任何接口,则JDK动态代理技术是无法为其创建对应的代理对象的,这是JDK动态代理技术的一种缺点,而CGLib动态代理技术则恰好弥补了这个缺点,CGLib动态代理技术使用的是继承该类的方式,从而避免了需要接口的缺陷,具体使用如下所示,注意,需要导入对应的依赖文件

/**
 * 基于CGLib的动态代理技术
 * 注意这里需要实现MethodInterceptor接口
 */
class ProxyFactory implements MethodInterceptor{

  // 提供对应的增强操作类
  private Enhancer enhancer = new Enhancer();

  public UserService getProxyObject(Class clazz){
    // 设置所要增强的类的父类
    enhancer.setSuperclass(clazz);
    // 设置回调对象
    enhancer.setCallback(this);
    // 创建对应的对象
    return (UserService) enhancer.create();
  }

  // 实现拦截方法,用于拦截对应的方法,并且对对应的方法进行增强
  // 参数含义:传入的对象, Method对象,方法的参数,进行代理后的Method对象
  @Override
  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

    LogService.info();
    // 这里需要注意,由于methodProxy对象是增强后的Method对象,所以这里需要调用的
    // 是methodProxy父类的方法,也就是所以增强的类的方法,以实现原来的功能
    Object object = methodProxy.invokeSuper(o, objects);
    LogService.info();
    return object;
  }
}

可以看到,使用CGLib动态代理技术可以在不需要实现接口的情况下东塔为对象创建代理对象,在很大程度上弥补了JDK动态代理技术的缺点,不过由于CGLib动态代理技术是采用继承目标类的方式,所以也存在一些问题,比如说,对于final以及private修饰的方法是无法为其增强的,这里还需要注意一下。

动态代理技术是实现AOP技术的基础,也是一种很方便地实现方式,常用的动态代理技术有基于JDK动态代理技术以及基于CGLib的动态代理技术,两种技术各有千秋,也都各有缺点基于JDK的动态代理技术需要为其提供接口,基于CGLib的动态代理技术不能为final以及private修饰的方法进行增强,在使用的时候需要根据具体进行进行合理选择。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

更多精彩内容其他人还在看

Java数据类型的规则

这篇文章主要介绍了Java数据类型的规则的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

Spring整合TimerTask实现定时任务调度

这篇文章主要介绍了Spring整合TimerTask实现定时任务调度的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

详解SpringMVC使用MultipartFile实现文件的上传

本篇文章主要介绍了SpringMVC使用MultipartFile实现文件的上传,本地的文件上传到资源服务器上,比较好的办法就是通过ftp上传。这里是结合SpringMVC+ftp的形式上传的,有兴趣的可以了解一下。
收藏 0 赞 0 分享

SpringMVC上传文件的三种实现方式

本篇文章主要介绍了SpringMVC上传文件的三种实现方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

微信公众帐号开发-自定义菜单的创建及菜单事件响应的实例

本篇文章主要介绍了微信公众帐号开发-自定义菜单的创建及菜单事件响应的实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
收藏 0 赞 0 分享

浅析Java中的继承与组合

本文将介绍组合和继承的概念及区别,并从多方面分析在写代码时如何进行选择。文中通过示例代码介绍的很详细,有需要的朋友可以参考借鉴,下面来一起看看吧。
收藏 0 赞 0 分享

利用反射获取Java类中的静态变量名及变量值的简单实例

下面小编就为大家带来一篇利用反射获取Java类中的静态变量名及变量值的简单实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

java启动线程的3种方式对比分析

这篇文章主要为大家对比分析了java启动线程的3种方式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

SpringMVC上传和解析Excel方法

这篇文章主要介绍了SpringMVC上传和解析Excel方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

JAVA中String类与StringBuffer类的区别

这篇文章主要为大家详细介绍了JAVA中String类与StringBuffer类的区别,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享
查看更多