关于spring:Spring框架系列11-Spring-AOP实现原理详解之Cglib代理实现

2次阅读

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

咱们在前文中曾经介绍了 SpringAOP 的切面实现和创立动静代理的过程,那么动静代理是如何工作的呢?本文次要介绍 Cglib 动静代理的案例和 SpringAOP 实现的原理。@pdai

  • Spring 框架系列(11) – Spring AOP 实现原理详解之 Cglib 代理实现

    • 引入

      • 动静代理要解决什么问题?

        • 什么是代理?
        • 什么是动静代理?
      • 什么是 Cglib? SpringAOP 和 Cglib 是什么关系?
    • Cglib 代理的案例

      • pom 包依赖
      • 定义实体
      • 被代理的类
      • cglib 代理
      • 应用代理
      • 简略测试
    • Cglib 代理的流程
    • SpringAOP 中 Cglib 代理的实现
    • 示例源码
    • 更多文章

引入

咱们在前文中曾经介绍了 SpringAOP 的切面实现和创立动静代理的过程,那么动静代理是如何工作的呢?本文次要介绍 Cglib 动静代理的案例和 SpringAOP 实现的原理。

要理解动静代理是如何工作的,首先须要理解

  • 什么是代理模式?
  • 什么是动静代理?
  • 什么是 Cglib?
  • SpringAOP 和 Cglib 是什么关系?

动静代理要解决什么问题?

什么是代理?

代理模式(Proxy pattern): 为另一个对象提供一个替身或占位符以管制对这个对象的拜访

举个简略的例子:

我 (client) 如果要买 (doOperation) 房,能够找中介 (proxy) 买房,中介间接和卖方 (target) 买房。中介和卖方都实现交易 (doOperation) 的操作。中介就是代理(proxy)。

什么是动静代理?

动静代理就是,在程序运行期,创立指标对象的代理对象,并对指标对象中的办法进行功能性加强的一种技术。

在生成代理对象的过程中,指标对象不变,代理对象中的办法是指标对象办法的加强办法。能够了解为运行期间,对象中办法的动静拦挡,在拦挡办法的前后执行性能操作。

什么是 Cglib? SpringAOP 和 Cglib 是什么关系?

Cglib 是一个弱小的、高性能的代码生成包,它宽泛被许多 AOP 框架应用,为他们提供办法的拦挡。

  • 最顶层是字节码,字节码相干的常识请参考 JVM 根底 – 类字节码详解
  • ASM 是操作字节码的工具
  • cglib 基于 ASM 字节码工具操作字节码(即动静生成代理,对办法进行加强)
  • SpringAOP 基于 cglib 进行封装,实现 cglib 形式的动静代理

Cglib 代理的案例

这里咱们写一个应用 cglib 的简略例子。@pdai

pom 包依赖

引入 cglib 的依赖包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>tech-pdai-spring-demos</artifactId>
        <groupId>tech.pdai</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>007-spring-framework-demo-aop-proxy-cglib</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

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

</project>

定义实体

User

package tech.pdai.springframework.entity;

/**
 * @author pdai
 */
public class User {

    /**
     * user's name.
     */
    private String name;

    /**
     * user's age.
     */
    private int age;

    /**
     * init.
     *
     * @param name name
     * @param age  age
     */
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {return name;}

    public void setName(String name) {this.name = name;}

    public int getAge() {return age;}

    public void setAge(int age) {this.age = age;}

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

被代理的类

即指标类, 对被代理的类中的办法进行加强

package tech.pdai.springframework.service;

import java.util.Collections;
import java.util.List;

import tech.pdai.springframework.entity.User;

/**
 * @author pdai
 */
public class UserServiceImpl {

    /**
     * find user list.
     *
     * @return user list
     */
    public List<User> findUserList() {return Collections.singletonList(new User("pdai", 18));
    }

    /**
     * add user
     */
    public void addUser() {// do something}

}

cglib 代理

cglib 代理类,须要实现 MethodInterceptor 接口,并指定代理指标类 target

package tech.pdai.springframework.proxy;

import java.lang.reflect.Method;

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

/**
 * This class is for proxy demo.
 *
 * @author pdai
 */
public class UserLogProxy implements MethodInterceptor {

    /**
     * 业务类对象,供代理办法中进行真正的业务办法调用
     */
    private Object target;

    public Object getUserLogProxy(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 obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // log - before method
        System.out.println("[before] execute method:" + method.getName());

        // call method
        Object result = proxy.invokeSuper(obj, args);

        // log - after method
        System.out.println("[after] execute method:" + method.getName() + ", return value:" + result);
        return null;
    }
}

应用代理

启动类中指定代理指标并执行。

package tech.pdai.springframework;

import tech.pdai.springframework.proxy.UserLogProxy;
import tech.pdai.springframework.service.UserServiceImpl;

/**
 * Cglib proxy demo.
 *
 * @author pdai
 */
public class ProxyDemo {

    /**
     * main interface.
     *
     * @param args args
     */
    public static void main(String[] args) {
        // proxy
        UserServiceImpl userService = (UserServiceImpl) new UserLogProxy().getUserLogProxy(new UserServiceImpl());

        // call methods
        userService.findUserList();
        userService.addUser();}
}

简略测试

咱们启动上述类 main 函数,执行的后果如下:

[before] execute method: findUserList
[after] execute method: findUserList, return value: [User{name='pdai', age=18}]
[before] execute method: addUser
[after] execute method: addUser, return value: null

Cglib 代理的流程

咱们把上述 Demo 的次要流程画进去,你便能很快了解

更多细节:

  • 在上图中,咱们能够通过在 Enhancer 中配置更多的参数来管制代理的行为,比方如果只心愿加强这个类中的一个办法(而不是所有办法),那就减少 callbackFilter 来对指标类中办法进行过滤;Enhancer 能够有更多的参数类配置其行为,不过咱们在学习上述次要的流程就够了。
  • final 办法为什么不能被代理?很显然 final 办法没法被子类笼罩,当然不能代理了。
  • Mockito 为什么不能 mock 静态方法?因为 mockito 也是基于 cglib 动静代理来实现的,static 办法也不能被子类笼罩,所以显然不能 mock。但 PowerMock 能够 mock 静态方法,因为它间接在 bytecode 上工作,更多能够看 Mockito 单元测试。(pdai: 通了没?是不是 so easy…)

SpringAOP 中 Cglib 代理的实现

SpringAOP 封装了 cglib,通过其进行动静代理的创立。

咱们看下 CglibAopProxy 的 getProxy 办法

@Override
public Object getProxy() {return getProxy(null);
}

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace("Creating CGLIB proxy:" + this.advised.getTargetSource());
  }

  try {Class<?> rootClass = this.advised.getTargetClass();
    Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

    // 下面流程图中的指标类
    Class<?> proxySuperClass = rootClass;
    if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {proxySuperClass = rootClass.getSuperclass();
      Class<?>[] additionalInterfaces = rootClass.getInterfaces();
      for (Class<?> additionalInterface : additionalInterfaces) {this.advised.addInterface(additionalInterface);
      }
    }

    // Validate the class, writing log messages as necessary.
    validateClassIfNecessary(proxySuperClass, classLoader);

    // 重点看这里,就是上图的 enhancer,设置各种参数来构建
    Enhancer enhancer = createEnhancer();
    if (classLoader != null) {enhancer.setClassLoader(classLoader);
      if (classLoader instanceof SmartClassLoader &&
          ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {enhancer.setUseCache(false);
      }
    }
    enhancer.setSuperclass(proxySuperClass);
    enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

    // 设置 callback 回调接口,即办法的加强点
    Callback[] callbacks = getCallbacks(rootClass);
    Class<?>[] types = new Class<?>[callbacks.length];
    for (int x = 0; x < types.length; x++) {types[x] = callbacks[x].getClass();}
    // 上节说到的 filter
    enhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
    enhancer.setCallbackTypes(types);

    // 重点:创立 proxy 和其实例
    return createProxyClassAndInstance(enhancer, callbacks);
  }
  catch (CodeGenerationException | IllegalArgumentException ex) {throw new AopConfigException("Could not generate CGLIB subclass of" + this.advised.getTargetClass() +
        ": Common causes of this problem include using a final class or a non-visible class",
        ex);
  }
  catch (Throwable ex) {// TargetSource.getTarget() failed
    throw new AopConfigException("Unexpected AOP exception", ex);
  }
}

获取 callback 的办法如下,提几个了解的要点吧,具体读者在学习的时候倡议把我的例子跑一下,而后打一个断点进行了解。

  • rootClass: 即指标代理类
  • advised: 蕴含上文中咱们获取到的 advisor 增强器的汇合
  • exposeProxy: 在 xml 配置文件中配置的,背景就是如果在事务 A 中应用了代理,事务 A 调用了指标类的的办法 a,在办法 a 中又调用指标类的办法 b,办法 a,b 同时都是要被加强的办法,如果不配置 exposeProxy 属性,办法 b 的加强将会生效,如果配置 exposeProxy,办法 b 在办法 a 的执行中也会被加强了
  • DynamicAdvisedInterceptor: 拦截器将 advised(蕴含上文中咱们获取到的 advisor 增强器)构建配置的 AOP 的 callback(第一个 callback)
  • targetInterceptor: xml 配置的 optimize 属性应用的(第二个 callback)
  • 最初连同其它 5 个默认的 Interceptor 返回作为 cglib 的拦截器链,之后通过 CallbackFilter 的 accpet 办法返回的索引从这个汇合中返回对应的拦挡增强器执行加强操作。
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
  // Parameters used for optimization choices...
  boolean exposeProxy = this.advised.isExposeProxy();
  boolean isFrozen = this.advised.isFrozen();
  boolean isStatic = this.advised.getTargetSource().isStatic();

  // Choose an "aop" interceptor (used for AOP calls).
  Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

  // Choose a "straight to target" interceptor. (used for calls that are
  // unadvised but can return this). May be required to expose the proxy.
  Callback targetInterceptor;
  if (exposeProxy) {
    targetInterceptor = (isStatic ?
        new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
        new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
  }
  else {
    targetInterceptor = (isStatic ?
        new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
        new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
  }

  // Choose a "direct to target" dispatcher (used for
  // unadvised calls to static targets that cannot return this).
  Callback targetDispatcher = (isStatic ?
      new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());

  Callback[] mainCallbacks = new Callback[] {
      aopInterceptor,  // 
      targetInterceptor,  // invoke target without considering advice, if optimized
      new SerializableNoOp(),  // no override for methods mapped to this
      targetDispatcher, this.advisedDispatcher,
      new EqualsInterceptor(this.advised),
      new HashCodeInterceptor(this.advised)
  };

  Callback[] callbacks;

  // If the target is a static one and the advice chain is frozen,
  // then we can make some optimizations by sending the AOP calls
  // direct to the target using the fixed chain for that method.
  if (isStatic && isFrozen) {Method[] methods = rootClass.getMethods();
    Callback[] fixedCallbacks = new Callback[methods.length];
    this.fixedInterceptorMap = CollectionUtils.newHashMap(methods.length);

    // TODO: small memory optimization here (can skip creation for methods with no advice)
    for (int x = 0; x < methods.length; x++) {Method method = methods[x];
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass);
      fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
      this.fixedInterceptorMap.put(method, x);
    }

    // Now copy both the callbacks from mainCallbacks
    // and fixedCallbacks into the callbacks array.
    callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
    System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
    System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
    this.fixedInterceptorOffset = mainCallbacks.length;
  }
  else {callbacks = mainCallbacks;}
  return callbacks;
}

能够联合调试,不便了解

示例源码

https://github.com/realpdai/t…

更多文章

首先,从 Spring 框架的整体架构和组成对整体框架有个认知。

  • Spring 根底 – Spring 和 Spring 框架组成

    • Spring 是什么?它是怎么诞生的?有哪些次要的组件和外围性能呢? 本文通过这几个问题帮忙你构筑 Spring 和 Spring Framework 的整体认知。

其次,通过案例引出 Spring 的外围(IoC 和 AOP),同时对 IoC 和 AOP 进行案例应用剖析。

  • Spring 根底 – Spring 简略例子引入 Spring 的外围

    • 上文中咱们简略介绍了 Spring 和 Spring Framework 的组件,那么这些 Spring Framework 组件是如何配合工作的呢?本文次要承接上文,向你展现 Spring Framework 组件的典型利用场景和基于这个场景设计出的简略案例,并以此引出 Spring 的外围要点,比方 IOC 和 AOP 等;在此基础上还引入了不同的配置形式,如 XML,Java 配置和注解形式的差别。
  • Spring 根底 – Spring 外围之管制反转(IOC)

    • 在 Spring 根底 – Spring 简略例子引入 Spring 的外围中向你展现了 IoC 的根底含意,同时以此发散了一些 IoC 相干知识点; 本节将在此基础上进一步解读 IOC 的含意以及 IOC 的应用形式
  • Spring 根底 – Spring 外围之面向切面编程(AOP)

    • 在 Spring 根底 – Spring 简略例子引入 Spring 的外围中向你展现了 AOP 的根底含意,同时以此发散了一些 AOP 相干知识点; 本节将在此基础上进一步解读 AOP 的含意以及 AOP 的应用形式。

基于 Spring 框架和 IOC,AOP 的根底,为构建下层 web 利用,须要进一步学习 SpringMVC。

  • Spring 根底 – SpringMVC 申请流程和案例

    • 前文咱们介绍了 Spring 框架和 Spring 框架中最为重要的两个技术点(IOC 和 AOP),那咱们如何更好的构建下层的利用呢(比方 web 利用),这便是 SpringMVC;Spring MVC 是 Spring 在 Spring Container Core 和 AOP 等技术根底上,遵循上述 Web MVC 的标准推出的 web 开发框架,目标是为了简化 Java 栈的 web 开发。本文次要介绍 SpringMVC 的申请流程和根底案例的编写和运行。

Spring 进阶 – IoC,AOP 以及 SpringMVC 的源码剖析

  • Spring 进阶 – Spring IOC 实现原理详解之 IOC 体系结构设计

    • 在对 IoC 有了初步的认知后,咱们开始对 IOC 的实现原理进行深刻了解。本文将帮忙你站在设计者的角度去看 IOC 最顶层的结构设计
  • Spring 进阶 – Spring IOC 实现原理详解之 IOC 初始化流程

    • 上文,咱们看了 IOC 设计要点和设计构造;紧接着这篇,咱们能够看下源码的实现了:Spring 如何实现将资源配置(以 xml 配置为例)通过加载,解析,生成 BeanDefination 并注册到 IoC 容器中的
  • Spring 进阶 – Spring IOC 实现原理详解之 Bean 实例化(生命周期, 循环依赖等)

    • 上文,咱们看了 IOC 设计要点和设计构造;以及 Spring 如何实现将资源配置(以 xml 配置为例)通过加载,解析,生成 BeanDefination 并注册到 IoC 容器中的;容器中寄存的是 Bean 的定义即 BeanDefinition 放到 beanDefinitionMap 中,实质上是一个ConcurrentHashMap<String, Object>;并且 BeanDefinition 接口中蕴含了这个类的 Class 信息以及是否是单例等。那么如何从 BeanDefinition 中实例化 Bean 对象呢,这是本文次要钻研的内容?
  • Spring 进阶 – Spring AOP 实现原理详解之切面实现

    • 前文,咱们剖析了 Spring IOC 的初始化过程和 Bean 的生命周期等,而 Spring AOP 也是基于 IOC 的 Bean 加载来实现的。本文次要介绍 Spring AOP 原理解析的切面实现过程(将切面类的所有切面办法依据应用的注解生成对应 Advice,并将 Advice 连同切入点匹配器和切面类等信息一并封装到 Advisor,为后续交给代理加强实现做筹备的过程)。
  • Spring 进阶 – Spring AOP 实现原理详解之 AOP 代理

    • 上文咱们介绍了 Spring AOP 原理解析的切面实现过程(将切面类的所有切面办法依据应用的注解生成对应 Advice,并将 Advice 连同切入点匹配器和切面类等信息一并封装到 Advisor)。本文在此基础上持续介绍,代理(cglib 代理和 JDK 代理)的实现过程。
  • Spring 进阶 – Spring AOP 实现原理详解之 Cglib 代理实现

    • 咱们在前文中曾经介绍了 SpringAOP 的切面实现和创立动静代理的过程,那么动静代理是如何工作的呢?本文次要介绍 Cglib 动静代理的案例和 SpringAOP 实现的原理。
  • Spring 进阶 – Spring AOP 实现原理详解之 JDK 代理实现

    • 上文咱们学习了 SpringAOP Cglib 动静代理的实现,本文次要是 SpringAOP JDK 动静代理的案例和实现局部。
  • Spring 进阶 – SpringMVC 实现原理之 DispatcherServlet 初始化的过程

    • 前文咱们有了 IOC 的源码根底以及 SpringMVC 的根底,咱们便能够进一步深刻了解 SpringMVC 次要实现原理,蕴含 DispatcherServlet 的初始化过程和 DispatcherServlet 解决申请的过程的源码解析。本文是第一篇:DispatcherServlet 的初始化过程的源码解析。
  • Spring 进阶 – SpringMVC 实现原理之 DispatcherServlet 解决申请的过程

    • 前文咱们有了 IOC 的源码根底以及 SpringMVC 的根底,咱们便能够进一步深刻了解 SpringMVC 次要实现原理,蕴含 DispatcherServlet 的初始化过程和 DispatcherServlet 解决申请的过程的源码解析。本文是第二篇:DispatcherServlet 解决申请的过程的源码解析。
正文完
 0