关于后端:动态代理原理是什么

1次阅读

共计 5361 个字符,预计需要花费 14 分钟才能阅读完成。

如何实现动静代理?JDK Proxy 和 CGLib 有什么区别?

典型答复

动静代理的罕用实现形式是反射。反射机制是指程序在运行期间能够拜访、检测和批改其自身状态或行为的一种能力,应用反射咱们能够调用任意一个类对象,以及类对象中蕴含的属性及办法。

但动静代理不止有反射一种实现形式,例如,动静代理能够通过 CGLib 来实现,而 CGLib 是基于 ASM(一个 Java 字节码操作框架)而非反射实现的。简略来说,动静代理是一种行为形式,而反射或 ASM 只是它的一种实现伎俩而已。

JDK Proxy 和 CGLib 的区别次要体现在以下几个方面:

  • JDK Proxy 是 Java 语言自带的性能,无需通过加载第三方类实现;
  • Java 对 JDK Proxy 提供了稳固的反对,并且会继续的降级和更新 JDK Proxy,例如 Java 8 版本中的 JDK Proxy 性能相比于之前版本晋升了很多;
  • JDK Proxy 是通过拦截器加反射的形式实现的;
  • JDK Proxy 只能代理继承接口的类;
  • JDK Proxy 实现和调用起来比较简单;
  • CGLib 是第三方提供的工具,基于 ASM 实现的,性能比拟高;
  • CGLib 无需通过接口来实现,它是通过实现子类的形式来实现调用的。

考点剖析

本课时考查的是你对反射、动静代理及 CGLib 的理解,很多人常常会把反射和动静代理划为等号,但从严格意义上来说,这种想法是不正确的,真正能搞懂它们之间的关系,也体现了你扎实 Java 的基本功。和这个问题相干的知识点,还有以下几个:

  • 你对 JDK Proxy 和 CGLib 的把握水平。
  • Lombok 是通过反射实现的吗?
  • 动静代理和动态代理有什么区别?
  • 动静代理的应用场景有哪些?
  • Spring 中的动静代理是通过什么形式实现的?

常识扩大

1.JDK Proxy 和 CGLib 的应用及代码剖析

JDK Proxy 动静代理实现

JDK Proxy 动静代理的实现无需援用第三方类,只须要实现 InvocationHandler 接口,重写 invoke() 办法即可,整个实现代码如下所示:

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

/**
* JDK Proxy 相干示例
  */
  public class ProxyExample {
  static interface Car {void running();
  }

  static class Bus implements Car {
    @Override
    public void running() {System.out.println("The bus is running.");
    }
  }

  static class Taxi implements Car {
    @Override
    public void running() {System.out.println("The taxi is running.");
    }
  }

  /**
  * JDK Proxy
    */
    static class JDKProxy implements InvocationHandler {
    private Object target; // 代理对象

    // 获取到代理对象
    public Object getInstance(Object target) {
      this.target = target;
      // 获得代理对象
      return Proxy.newProxyInstance(target.getClass().getClassLoader(),
      target.getClass().getInterfaces(), this);
    }

    /**
    * 执行代理办法
    * @param proxy  代理对象
    * @param method 代理办法
    * @param args   办法的参数
    * @return
    * @throws InvocationTargetException
    * @throws IllegalAccessException
      */
      @Override
      public Object invoke(Object proxy, Method method, Object[] args)
        throws InvocationTargetException, IllegalAccessException {System.out.println("动静代理之前的业务解决.");
          Object result = method.invoke(target, args); // 执行调用办法(此办法执行前后,能够进行相干业务解决)return result;
        }
      }

  public static void main(String[] args) {
    // 执行 JDK Proxy
    JDKProxy jdkProxy = new JDKProxy();
    Car carInstance = (Car) jdkProxy.getInstance(new Taxi());
    carInstance.running();}

以上程序的执行后果是:

 动静代理之前的业务解决.
The taxi is running.

能够看出 JDK Proxy 实现动静代理的外围是实现 Invocation 接口,咱们查看 Invocation 的源码,会发现外面其实只有一个 invoke() 办法,源码如下:

public interface InvocationHandler {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

这是因为在动静代理中有一个重要的角色也就是代理器,它用于对立治理被代理的对象,显然 InvocationHandler 就是这个代理器,而 invoke() 办法则是触发代理的执行办法,咱们通过实现 Invocation 接口来领有动静代理的能力。

CGLib 的实现

在应用 CGLib 之前,咱们要先在我的项目中引入 CGLib 框架,在 pom.xml 中增加如下配置:

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

CGLib 实现代码如下:

package com.lagou.interview;

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

import java.lang.reflect.Method;

public class CGLibExample {

    static class Car {public void running() {System.out.println("The car is running.");
        }
    }

    /**
     * CGLib 代理类
     */
    static class CGLibProxy implements MethodInterceptor {
        private Object target; // 代理对象

        public Object getInstance(Object target) {
            this.target = target;
            Enhancer enhancer = new Enhancer();
            // 设置父类为实例类
            enhancer.setSuperclass(this.target.getClass());
            // 回调办法
            enhancer.setCallback(this);
            // 创立代理对象
            return enhancer.create();}

        @Override
        public Object intercept(Object o, Method method,
                                Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("办法调用前业务解决.");
            Object result = methodProxy.invokeSuper(o, objects); // 执行办法调用
            return result;
        }
    }

    // 执行 CGLib 的办法调用
    public static void main(String[] args) {
      // 创立 CGLib 代理类
      CGLibProxy proxy = new CGLibProxy();
      // 初始化代理对象
      Car car = (Car) proxy.getInstance(new Car());
      // 执行办法
      car.running();}

以上程序的执行后果是:

 办法调用前业务解决.
The car is running.

能够看出 CGLib 和 JDK Proxy 的实现代码比拟相似,都是通过实现代理器的接口,再调用某一个办法实现动静代理的,惟一不同的是,CGLib 在初始化被代理类时,是通过 Enhancer 对象把代理对象设置为被代理类的子类来实现动静代理的。因而被代理类不能被关键字 final 润饰,如果被 final 润饰,再应用 Enhancer 设置父类时会报错,动静代理的构建会失败。

2.Lombok 原理剖析

在开始讲 Lombok 的原理之前,咱们先来简略地介绍一下 Lombok,它属于 Java 的一个热门工具类,应用它能够无效的解决代码工程中那些繁琐又反复的代码,如 Setter、Getter、toString、equals 和 hashCode 等等,向这种办法都能够应用 Lombok 注解来实现。

例如,咱们应用比拟多的 Setter 和 Getter 办法,在没有应用 Lombok 之前,代码是这样的:

public class Person {
  private Integer id;
  private String name;
  public Integer getId() {return id;}
  public void setId(Integer id) {this.id = id;}
  public String getName() {return name;}
  public void setName(String name) {this.name = name;}
}

在应用 Lombok 之后,代码是这样的:

@Data
public class Person {
  private Integer id;
  private String name;
}

能够看出 Lombok 让代码简略和优雅了很多。

小贴士:如果在我的项目中应用了 Lombok 的 Getter 和 Setter 注解,那么想要在编码阶段胜利调用对象的 set 或 get 办法,咱们须要在 IDE 中装置 Lombok 插件才行,比方 Idea 的插件如下图所示:

Lombok 的原理

Lombok 的实现和反射没有任何关系,后面咱们说了反射是程序在运行期的一种自省(introspect)能力,而 Lombok 的实现是在编译期就实现了,为什么这么说呢?

回到咱们方才 Setter/Getter 的办法,当咱们关上 Person 的编译类就会发现,应用了 Lombok 的 @Data 注解后的源码居然是这样的:

能够看出 Lombok 是在编译期就为咱们生成了对应的字节码。

其实 Lombok 是基于 Java 1.6 实现的 JSR 269: Pluggable Annotation Processing API 来实现的,也就是通过编译期自定义注解处理器来实现的,它的执行步骤如下:

从流程图中能够看出,在编译期阶段,当 Java 源码被形象成语法树(AST)之后,Lombok 会依据本人的注解处理器动静批改 AST,减少新的代码(节点),在这所有执行之后就生成了最终的字节码(.class)文件,这就是 Lombok 的执行原理。

3. 动静代理知识点裁减

当面试官问动静代理的时候,常常会问到它和动态代理的区别?动态代理其实就是当时写好代理类,能够手工编写也能够应用工具生成,但它的毛病是每个业务类都要对应一个代理类,特地不灵便也不不便,于是就有了动静代理。

动静代理的常见应用场景有 RPC 框架的封装、AOP(面向切面编程)的实现、JDBC 的连贯等。

Spring 框架中同时应用了两种动静代理 JDK Proxy 和 CGLib,当 Bean 实现了接口时,Spring 就会应用 JDK Proxy,在没有实现接口时就会应用 CGLib,咱们也能够在配置中指定强制应用 CGLib,只须要在 Spring 配置中增加 <aop:aspectj-autoproxy proxy-target-class=”true”/> 即可。

总结

本课时咱们介绍了 JDK Proxy 和 CGLib 的区别,JDK Proxy 是 Java 语言内置的动静代理,必须要通过实现接口的形式来代理相干的类,而 CGLib 是第三方提供的基于 ASM 的高效动静代理类,它通过实现被代理类的子类来实现动静代理的性能,因而被代理的类不能应用 final 润饰。

除了 JDK Proxy 和 CGLib 之外,咱们还讲了 Java 中罕用的工具类 Lombok 的实现原理,它其实和反射是没有任何关系的;最初讲了动静代理的应用场景以及 Spring 中动静代理的实现形式,心愿本文能够帮忙到你。

本文由 mdnice 多平台公布

正文完
 0