Spring源码解析9——静态AOP

1、创建AOP静态代理

  AOP 的静态代理主要是在虚拟机启动时通过改变 目标对象字节码的方式来完成对目标对象的增强,它与动态代理相比具有更高的效率,因为在动态代理调用的过程中,还需要一个动态创建代理类并代理目标对象的步骤,而静态代理则是在启动时便完成了字节码增强,当系统再次调用目标类时与调用正常的类井元差别, 所以在效率上会相对高些。

1.1、Instrumentation的使用

  Java 在1.5 引人java.lang.instrument,你可以由此实现一个 Java agent,通过此 agent来修改类的字节码即改变一个类。本节会通过 Java Instrument 实现一个简单的 profiler。

  • 实现一个ClassFileTransformer.class的子类
package com.xxx.spring源码深度解析_第七章.AopStaticProxy;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtBehavior;
import javassist.CtClass;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;

public class PerfMonXformer implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        byte[] transformed = null;
        System.out.println("Transforming " + className);
        ClassPool pool = ClassPool.getDefault();
        CtClass cl = null;
        try {
            cl = pool.makeClass(new java.io.ByteArrayInputStream(classfileBuffer));
            if (cl.isInterface() == false) {
                CtBehavior[] methods = cl.getDeclaredBehaviors();
                for (int i = 0; i < methods.length; i++) {
                    //修改test()函数的字节码
                    if ("test".equals(methods[i].getName())) {
                        doMethod(methods[i]);
                    }
                }
                transformed = cl.toBytecode();
            }
        } catch (Exception e) {
            System.err.println("Could not instrument " + className + ", exception :" + e.getMessage());
        } finally {
            if (cl != null) {
                cl.detach();
            }
        }
        return transformed;
    }

    private void doMethod(CtBehavior method) throws CannotCompileException {
        method.insertBefore("System.out.println(\"函数执行前\");");
        method.insertAfter("System.out.println(\"函数执行后\");");
    }
}

  • agent类
package com.xxx.spring源码深度解析_第七章.AopStaticProxy;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;

public class PerfMonAgent {
    static private Instrumentation inst = null;

    public static void premain(String agentArgs, Instrumentation _inst) {
        System.out.println("PerfMonAgent.premain() was called.");
        inst = _inst;
        ClassFileTransformer trans = new PerfMonXformer();
        System.out.println("Adding a PerfMonXformer instance to the JVM.");
        inst.addTransformer(trans);

    }
}
  • 通过以下pom文件打包agent
<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xxx</groupId>
    <artifactId>Spring03_proxy</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <!--JAR 的 META-INF/MANIFEST.MF 加入 Premain-Class:【PerfMonAgent.class的classPath路径(相对路径)】 -->
                            <Premain-Class>com.xxx.spring源码深度解析_第七章.AopStaticProxy.PerfMonAgent</Premain-Class>
                            <!--JAR 的 META-INF/MANIFEST.MF 加入 Boot-Class-Path:【javassist-3.8.0.GA.jar的绝对路径】 -->
                            <Boot-Class-Path>D:\maven_repository\javassist\javassist\3.8.0.GA\javassist-3.8.0.GA.jar</Boot-Class-Path>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.1_3</version>
        </dependency>
        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.8.0.GA</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
  • 打包应用
package com.xxx.spring源码深度解析_第七章.AopStaticProxy;

public class App {
    public static void main(String[] args) {
        new App().test();
    }

    public void test() {
        System.out.println("Hello World!!");
    }
}
  • 执行App.class::main()函数时在VM options(idea中)加入--javaagent:【通过上面的maven配置打好的jar包】
    image

  • 该agent就会在App.class::test()函数执行前,执行System.out.println("函数执行前");和System.out.println("函数执行前");语句,执行结果如下所示:
    image
    image
    image
    以上结果表明:通过Instrment 实现 agent 使得监控代码和应用代码完全隔离了。
      通过这个案例可以说明,在 Spring 中的静态 AOP 直接使用了AspectJ提供的方法,而 AspectJ又是在 Instrument基础上进行的封装。就以上面的例子来看,至少在 AspectJ 中会有如下功能。
    ①、读取 META-INF/aop.xml.
    ②、将 aop.xml 中定义的增强器通过自定义的 ClassFileTransformer 织入对应的类中。

1.2、织入

  LoadTimeWeaverAwareProcessor.class 实现 BeanPostProcessor.interface接口中的函数,那么对于 BeanPostProcessor 接口来讲,postProcessBeforeInitialization()函数与postProcessAfterlnitialization()函数 有着其特殊意义,也就是说在所有 bean 的初始化之前与之后都会分别调用对应的方法,那么在 LoadTimeWeaverAwareProcessor.class::postProcessBeforeInitialization()函数中完成了什么逻辑呢?
LoadTimeWeaverAwareProcessor.class::postProcessBeforeInitialization()

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof LoadTimeWeaverAware) {
            LoadTimeWeaver ltw = this.loadTimeWeaver;
            if (ltw == null) {
                Assert.state(this.beanFactory != null,
                        "BeanFactory required if no LoadTimeWeaver explicitly specified");
                ltw = this.beanFactory.getBean(
                        ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME, LoadTimeWeaver.class);
            }
            ((LoadTimeWeaverAware) bean).setLoadTimeWeaver(ltw);
        }
        return bean;
    }

  我们综合之前讲解的所有信息,将所有相关信息串联起来一起分析这个函数。在LoadTimeWeaverAwareProcessor.class::postProcessBeforelnitialization() 函数中,因为最开始的if判断注定这个后处理器只对 LoadTimeWeaverAware 类型的 bean 起作用,而纵观所有的bean,实现 LoadTimeWeaverAware 接口的类只有AspectJWeavingEnabler.class。
  当在 Spring 中调用 AspectJWeavingEnabler 时,this.loadTimeWeaver()函数尚未被初始化,那么,会直接调用 beanFactory.getBean()函数获取对应的 DefaultContextLoadTimeWeaver.class 类型的 bean,并将其设置为 AspectJWeavingEnabler.class 类型 bean 的loadTimeWeaver属性中。当然 AspectJWeavingEnabler 同样实现了 BeanClassLoaderAware.interface 以及Ordered.interface 接口,实现 BeanClassLoaderAware.interface 接口保证了在 bean初始化的时候调用AbstractAutowireCapableBeanFactory.class::invokeAwareMethods()函数的时候将beanClassLoader 赋值给当前类。而实现 Ordered.interface 则保证在实例化 bean 时当前 bean 会被最先初始化。
而 DefaultContextLoadTimeWeaver.class又同时实现了 LoadTimeWeaver、BeanClassLoaderAware以及 DisposableBean。其中 DisposableBean.interface保证在 bean 销毁时会调用 destroy()函数进行 bean的清理,而 BeanClassLoaderAware.interface接口则保证在 bean 的初始化调用 AbstractAutowireCapableBeanFactory.class::invokeAwareMethods()函数时调用DefaultContextLoadTimeWeaver.class:setBeanClassLoader()函数。
DefaultContextLoadTimeWeaver.class::setBeanClassLoader()函数

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        LoadTimeWeaver serverSpecificLoadTimeWeaver = createServerSpecificLoadTimeWeaver(classLoader);
        if (serverSpecificLoadTimeWeaver != null) {
            if (logger.isInfoEnabled()) {
                logger.info("Determined server-specific load-time weaver: " +
                        serverSpecificLoadTimeWeaver.getClass().getName());
            }
            this.loadTimeWeaver = serverSpecificLoadTimeWeaver;
        }
        else if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
            //检查当前虚拟机中的Instrumentation实例是否可用
            logger.info("Found Spring's JVM agent for instrumentation");
            //实例化了一个 InstrumentationLoadTimeWeaver 类型的实例,而且在实例化过程中还做了一些额外的操作。
            this.loadTimeWeaver = new InstrumentationLoadTimeWeaver(classLoader);
        }
        else {
            try {
                this.loadTimeWeaver = new ReflectiveLoadTimeWeaver(classLoader);
                if (logger.isInfoEnabled()) {
                    logger.info("Using a reflective load-time weaver for class loader: " +
                            this.loadTimeWeaver.getInstrumentableClassLoader().getClass().getName());
                }
            }
            catch (IllegalStateException ex) {
                throw new IllegalStateException(ex.getMessage() + " Specify a custom LoadTimeWeaver or start your " +
                        "Java virtual machine with Spring's agent: -javaagent:org.springframework.instrument.jar");
            }
        }
    }

实例化InstrumentationLoadTimeWeaver.class过程中会对private final Instrumentation instrumentation属性进行初始化,代码如下:
InstrumentationAccessor.class的构造函数

    public InstrumentationLoadTimeWeaver(@Nullable ClassLoader classLoader) {
        this.classLoader = classLoader;
        this.instrumentation = getInstrumentation();
    }

也就是说在 InstrumentationLoadTimeWeaver 实例化后其属性 Instrumentation 已经被初始化为代表着当前虚拟机的实例了。综合我们讲过的例子,对于注册转换器,如 addTransformer()函数等,便可以直接使用此属性进行操作了。
也就是经过以上程序的处理后,在 Spring 中的 bean 之间的关系如下:
①、AspectJWeavingEnabler.class 类型的 bean 中的 loadTimeWeaver 属性被初始化为 DefaultContextLoadTimeWeaver 类型的 bean。
②、DefaultContextLoadTimeWeaver.class 类型的 bean 中的 loadTimeWeaver 属性被初始化为InstrumentationLoadTimeWeaver.
  因为 AspectJWeavingEnabler.class同样实现了 BeanFactoryPostProcessor,所以当所有 bean 解析结束后会调用其 postProcessBeanFactory() 函数。
BeanFactoryPostProcessor.interface

@FunctionalInterface
public interface BeanFactoryPostProcessor {

    /**
     * Modify the application context's internal bean factory after its standard
     * initialization. All bean definitions will have been loaded, but no beans
     * will have been instantiated yet. This allows for overriding or adding
     * properties even to eager-initializing beans.
     * @param beanFactory the bean factory used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

AspectJWeavingEnabler.class

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        enableAspectJWeaving(this.loadTimeWeaver, this.beanClassLoader);
    }


    /**
     * Enable AspectJ weaving with the given {@link LoadTimeWeaver}.
     * @param weaverToUse the LoadTimeWeaver to apply to (or {@code null} for a default weaver)
     * @param beanClassLoader the class loader to create a default weaver for (if necessary)
     */
    public static void enableAspectJWeaving(
            @Nullable LoadTimeWeaver weaverToUse, @Nullable ClassLoader beanClassLoader) {

        if (weaverToUse == null) {
            //此时已经被初始化为DefaultContextLoadTimeWeaver
            if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
                weaverToUse = new InstrumentationLoadTimeWeaver(beanClassLoader);
            }
            else {
                throw new IllegalStateException("No LoadTimeWeaver available");
            }
        }
        //使用DefaultContextLoadTimeWeaver类型的bean中的loadTimeWeaver属性注册转换器
        weaverToUse.addTransformer(
                new AspectJClassBypassingClassFileTransformer(new ClassPreProcessorAgentAdapter()));
    }
    
    private static class AspectJClassBypassingClassFileTransformer implements ClassFileTransformer {

        private final ClassFileTransformer delegate;

        public AspectJClassBypassingClassFileTransformer(ClassFileTransformer delegate) {
            this.delegate = delegate;
        }

        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

            if (className.startsWith("org.aspectj") || className.startsWith("org/aspectj")) {
                return classfileBuffer;
            }
            //委托给AspectJ代理继续处理
            return this.delegate.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
        }
    }

  AspectJClassBypassingClassFileTransformer.class 的作用仅仅是告诉AspectJ以org.aspectj开头的或者 org/aspectj 开头的类不进行处理。
  AspectJWeavingEnabler.class与BeanFactoryPostProcessor.interface、AspectJClassBypassingClassFileTransformer.class之间的UML关系图,如下所示:
image

posted @ 2026-01-01 21:07  Carey_ccl  阅读(2)  评论(0)    收藏  举报