静态代理是指在虚拟机载入字节码文件时动态织入切面行为,相对于动态代理具有更高的效率,因为动态代理调用的过程中,还需要一个动态创建代理类并代理目标对象的步骤,而静态代理在启动时便完成了字节码增强,当系统调用目标类时将与调用正常的类没有区别,它以一种更底层、更松耦合的方式改变了一个类的行为。
Instrumentation 先说下javaagent,它是java命令的一个参数,用于指定一个jar包,但是对该jar包有2个要求:
jar 包的 MANIFEST.MF 中必须指定 Premain-Class
Premain-Class 指定的类必须实现 premain() 方法
premain
意思就是运行在 main 函数之前,即当Java虚拟机启动时,在执行 main 函数之前,JVM会先运行 -javaagent 所指定 jar 包内 Premain-Class 指定的类的 premain 方法 。
但是,对于 javaagent 指定的类有一些约束要求,即必须要有 premain() 方法,并且方法的签只能是以下两种格式:
JVM会优先加载带Instrumentation
签名的方法,加载成功则忽略第二种
1 2 3 public static void premain (String agentArgs, Instrumentation inst) public static void premain (String agentArgs)
下面看下接口Instrumentation
,它是java1.5新增的,用来帮助开发者修改目标类的class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public interface Instrumentation { void addTransformer (ClassFileTransformer transformer, boolean canRetransform) ; void addTransformer (ClassFileTransformer transformer) ; boolean removeTransformer (ClassFileTransformer transformer) ; boolean isRetransformClassesSupported () ; void retransformClasses (Class<?>... classes) throws UnmodifiableClassException ; boolean isRedefineClassesSupported () ; void redefineClasses (ClassDefinition... definitions) throws ClassNotFoundException, UnmodifiableClassException ; boolean isModifiableClass (Class<?> theClass) ; @SuppressWarnings ("rawtypes" ) Class[] getAllLoadedClasses(); @SuppressWarnings ("rawtypes" ) Class[] getInitiatedClasses(ClassLoader loader); long getObjectSize (Object objectToSize) ; void appendToBootstrapClassLoaderSearch (JarFile jarfile) ; void appendToSystemClassLoaderSearch (JarFile jarfile) ; boolean isNativeMethodPrefixSupported () ; void setNativeMethodPrefix (ClassFileTransformer transformer, String prefix) ; }
因此,我们便可以在agent
中通过Instrumentation
来修改类的字节码,达到实现Aop的效果,即在运行之前修改目标类class,插入一些操作。
示例 首先创建一个agent包
test.selfagent.PerMonformer 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class PerMonformer implements ClassFileTransformer { @Override public byte [] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte [] classfileBuffer) throws IllegalClassFormatException { byte [] transformed = null ; ClassPool classPool = ClassPool.getDefault(); CtClass ctClass = null ; try { ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer)); if (!ctClass.isInterface()){ CtBehavior[] methods = ctClass.getDeclaredBehaviors(); for (int i = 0 ; i < methods.length; i++){ if (!methods[i].isEmpty()){ doMethod(methods[i]); } } transformed = ctClass.toBytecode(); } }catch (Exception e){ System.out.println("instrument " + className + " failed: " + e.getMessage()); }finally { if (ctClass != null ){ ctClass.detach(); } } return transformed; } private void doMethod (CtBehavior method) throws CannotCompileException { String methodName = method.getLongName(); method.addLocalVariable("stime" , CtClass.longType); method.insertBefore("long stime = System.nanoTime();" ); method.insertAfter("System.out.println(\"" + methodName + " cost=\" + (System.nanoTime() - stime));" ); } }
test.selfagent.TestAgent 1 2 3 4 5 6 7 8 9 10 public class TestAgent { static private Instrumentation inst = null ; public static void premain (String agentArgs, Instrumentation _inst) { inst = _inst; ClassFileTransformer trans = new PerMonformer(); inst.addTransformer(trans); } }
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-jar-plugin</artifactId > <version > 2.2</version > <configuration > <archive > <manifestEntries > <Premain-Class > test.selfagent.TestAgent</Premain-Class > <Boot-Class-Path > D:/repository/javassist/javassist/3.8.0.GA/javassist-3.8.0.GA.jar</Boot-Class-Path > </manifestEntries > </archive > </configuration > </plugin > </plugins > </build > <dependencies > <dependency > <groupId > javassist</groupId > <artifactId > javassist</artifactId > <version > 3.8.0.GA</version > </dependency > </dependencies >
然后写个demo测试下,通过-javaagent:D:/temp/selfagent/target/selfagent-0.0.1-SNAPSHOT.jar
指定路径(只能绝对路径)
Demo.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Demo { public static void main (String[] args) { new Demo().hello(); } public void hello () { System.out.println("Hello World." ); } } ... sun.launcher.LauncherHelper$FXHelper.<clinit>() cost=11869 sun.launcher.LauncherHelper$FXHelper.doesExtendFXApplication(java.lang.Class) cost=1142557478651491 sun.launcher.LauncherHelper$FXHelper.access$100 (java.lang.Class) cost=43945 Hello World. demo.Demo.hello() cost=58058 demo.Demo.main(java.lang.String[]) cost=89493 instrument java/lang/Shutdown failed: no method body java.lang.Shutdown$Lock(java.lang.Shutdown$1 ) cost=10906 java.lang.Shutdown$Lock(java.lang.Shutdown$1 ) cost=642
AspectJ AspectJ在Instrument的基础上进行了封装,可以更方便的修改class,但是它定义了新的关键字aspect
,需要在Jdk中安装AspectJ才能进行编译(点击下载 ,然后在文件目录下执行java -jar aspectj-1.9.6.jar
进行安装即可),另外如果是Eclipse,需要安装Aspect对应版本的插件,下载地址:http://www.eclipse.org/ajdt/downloads/index.php
示例 Test.java 1 2 3 4 5 6 public class Test { public void hello () { System.out.println("Hello World." ); } }
TestAspect.java 1 2 3 4 5 6 7 public aspect TestAspect { void around () :call (void hello() ) { System.out.println("Hello AspectJ." ); proceed(); } }
App.java 1 2 3 4 5 6 7 8 9 10 public class App { public static void main (String[] args) { Test test = new Test(); test.hello(); } } Hello AspectJ. Hello World.
spring 静态Aop 示例 spring静态Aop直接使用AspectJ提供的方法,如果要将前面[spring. 动态Aop ]中的示例改成静态Aop的话,只需修改三处
bean.xml 需要添加标签:context:load-time-weaver
bean.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop ="http://www.springframework.org/schema/aop" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <aop:aspectj-autoproxy proxy-target-class ="false" expose-proxy ="true" /> <bean id ="testBean" class ="test.aop.TestBean" /> <bean id ="logAspect" class ="test.aop.LogAspect" /> <context:load-time-weaver aspectj-weaving ="on" /> </beans >
另外添加 META-INF/aop.xml,告诉AspectJ需要对那些包进行织入,以及使用那些增强器
META-INF/ aop.xml 1 2 3 4 5 6 7 8 9 <aspectj > <weaver > <include within ="* *..*Bean.*(..)" /> </weaver > <aspects > <aspect name ="test.aop.LogAspect" /> </aspects > </aspectj >
还需要指定javaagent,不过jar包是spring提供的
-javaagent:D:/repository/org/springframework/spring-instrument/5.1.3.RELEASE/spring-instrument-5.1.3.RELEASE.jar
其它的则不需要修改
TestBean.java 1 2 3 4 5 6 7 8 9 10 11 public class TestBean { public int calculate (int i, int j) { this .valid(i, j); return i / j; } public void valid (int i, int j) { Assert.isTrue(i > 0 && j > 0 , "" ); } }
LogAspect.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Aspect public class LogAspect { private ThreadLocal<Long> localTime = new ThreadLocal<>(); @Pointcut ("execution(* *..*Bean.*(..))" ) public void point () { } @Before ("point()" ) public void before (JoinPoint point) { localTime.set(System.currentTimeMillis()); String method = point.getSignature().getName(); System.out.println("aspect before: " + method + Arrays.asList(point.getArgs())); } @AfterReturning (pointcut = "point()" , returning = "result" ) public void after (Object result) { long now = System.currentTimeMillis(); System.out.println("aspect: after: result=" + result + ", cost=" + (now - localTime.get()) + "ms" ); } }
从打印的class可以发现,这里的代理并没有像之前的动态代理利用继承或者接口方式创建一个新的class来代替,而是直接对目标class进行了修改,这样即便对于自我嵌套调用也可以直接体现出代理行为,而不再需要通过暴露的代理对象
App.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class App { public static void main ( String[] args ) { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml" ); TestBean test = (TestBean)context.getBean("testBean" ); System.out.println(test.getClass()); test.calculate(4 , 2 ); } } class test .aop .TestBean aspect before: calculate[4, 2] aspect before: valid[4 , 2 ] aspect: after: result=null , cost=0 ms aspect: after: result=2 , cost=0 ms
LoadTimeWeaverBeanDefinitionParser 下面还是从标签入手进行分析,首先找到ContextNamespaceHandler
,并定位到LoadTimeWeaverBeanDefinitionParser
org.springframework.context.config.ContextNamespaceHandler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class ContextNamespaceHandler extends NamespaceHandlerSupport { @Override public void init () { registerBeanDefinitionParser("property-placeholder" , new PropertyPlaceholderBeanDefinitionParser()); registerBeanDefinitionParser("property-override" , new PropertyOverrideBeanDefinitionParser()); registerBeanDefinitionParser("annotation-config" , new AnnotationConfigBeanDefinitionParser()); registerBeanDefinitionParser("component-scan" , new ComponentScanBeanDefinitionParser()); registerBeanDefinitionParser("load-time-weaver" , new LoadTimeWeaverBeanDefinitionParser()); registerBeanDefinitionParser("spring-configured" , new SpringConfiguredBeanDefinitionParser()); registerBeanDefinitionParser("mbean-export" , new MBeanExportBeanDefinitionParser()); registerBeanDefinitionParser("mbean-server" , new MBeanServerBeanDefinitionParser()); } }
LoadTimeWeaverBeanDefinitionParser
实现了BeanDefinitionParser
接口,那么从parse
开始进行分析,这里实际上就是注册了两个BeanDefinition
org.springframework.beans.factory.xml.AbstractBeanDefinitionParser 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public final BeanDefinition parse (Element element, ParserContext parserContext) { AbstractBeanDefinition definition = parseInternal(element, parserContext); if (definition != null && !parserContext.isNested()) { try { String id = resolveId(element, definition, parserContext); if (!StringUtils.hasText(id)) { parserContext.getReaderContext().error( "Id is required for element '" + parserContext.getDelegate().getLocalName(element) + "' when used as a top-level tag" , element); } String[] aliases = null ; if (shouldParseNameAsAliases()) { String name = element.getAttribute(NAME_ATTRIBUTE); if (StringUtils.hasLength(name)) { aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name)); } } BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases); registerBeanDefinition(holder, parserContext.getRegistry()); if (shouldFireEvents()) { BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder); postProcessComponentDefinition(componentDefinition); parserContext.registerComponent(componentDefinition); } }catch (BeanDefinitionStoreException ex) { String msg = ex.getMessage(); parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element); return null ; } } return definition; }
首先parseInternal
中构造了一个BeanDefinition
,以loadTimeWeaver
为id,class为DefaultContextLoadTimeWeaver
org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 protected final AbstractBeanDefinition parseInternal (Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); String parentName = getParentName(element); if (parentName != null ) { builder.getRawBeanDefinition().setParentName(parentName); } Class<?> beanClass = getBeanClass(element); if (beanClass != null ) { builder.getRawBeanDefinition().setBeanClass(beanClass); }else { String beanClassName = getBeanClassName(element); if (beanClassName != null ) { builder.getRawBeanDefinition().setBeanClassName(beanClassName); } } builder.getRawBeanDefinition().setSource(parserContext.extractSource(element)); BeanDefinition containingBd = parserContext.getContainingBeanDefinition(); if (containingBd != null ) { builder.setScope(containingBd.getScope()); } if (parserContext.isDefaultLazyInit()) { builder.setLazyInit(true ); } doParse(element, parserContext, builder); return builder.getBeanDefinition(); }
另外在doParse
中是注册了AspectJWeavingEnabler
org.springframework.context.config.LoadTimeWeaverBeanDefinitionParser 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 private static final String ASPECTJ_WEAVING_ENABLER_CLASS_NAME = "org.springframework.context.weaving.AspectJWeavingEnabler" ; private static final String DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME = "org.springframework.context.weaving.DefaultContextLoadTimeWeaver" ; protected String resolveId (Element element, AbstractBeanDefinition definition, ParserContext parserContext) { return ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME; } protected String getBeanClassName (Element element) { if (element.hasAttribute(WEAVER_CLASS_ATTRIBUTE)) { return element.getAttribute(WEAVER_CLASS_ATTRIBUTE); } return DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME; } protected void doParse (Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); if (isAspectJWeavingEnabled(element.getAttribute(ASPECTJ_WEAVING_ATTRIBUTE), parserContext)) { if (!parserContext.getRegistry().containsBeanDefinition(ASPECTJ_WEAVING_ENABLER_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ASPECTJ_WEAVING_ENABLER_CLASS_NAME); parserContext.registerBeanComponent(new BeanComponentDefinition(def, ASPECTJ_WEAVING_ENABLER_BEAN_NAME)); } if (isBeanConfigurerAspectEnabled(parserContext.getReaderContext().getBeanClassLoader())) { new SpringConfiguredBeanDefinitionParser().parse(element, parserContext); } } }
AbstractApplicationContext 后面AbstractApplicationContext
在refresh()
时会调用prepareBeanFactory
,其中就根据有没有loadTimeWeaver
来决定是否注册LoadTimeWeaverAwareProcessor
org.springframework.context.support.AbstractApplicationContext 1 2 3 4 5 6 7 8 9 10 11 12 13 protected void prepareBeanFactory (ConfigurableListableBeanFactory beanFactory) { if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
LoadTimeWeaverAwareProcessor LoadTimeWeaverAwareProcessor
就是一个BeanPostProcessor
,它实现了postProcessBeforeInitialization
方法,其逻辑就是实例化DefaultContextLoadTimeWeaver
,并赋值给了AspectJWeavingEnabler
,可以理解成为AspectJWeavingEnabler
做了一个准备操作
org.springframework.context.weaving.LoadTimeWeaverAwareProcessor 1 2 3 4 5 6 7 8 9 10 11 12 13 14 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; }
同时DefaultContextLoadTimeWeaver
实现了接口BeanClassLoaderAware
,那么在其初始化时会调用方法setBeanClassLoader
,其中初始化了一个InstrumentationLoadTimeWeaver
org.springframework.context.weaving.DefaultContextLoadTimeWeaver 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public void setBeanClassLoader (ClassLoader classLoader) { LoadTimeWeaver serverSpecificLoadTimeWeaver = createServerSpecificLoadTimeWeaver(classLoader); if (serverSpecificLoadTimeWeaver != null ) { if (logger.isDebugEnabled()) { logger.debug("Determined server-specific load-time weaver: " + serverSpecificLoadTimeWeaver.getClass().getName()); } this .loadTimeWeaver = serverSpecificLoadTimeWeaver; }else if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) { logger.debug("Found Spring's JVM agent for instrumentation" ); this .loadTimeWeaver = new InstrumentationLoadTimeWeaver(classLoader); }else { try { this .loadTimeWeaver = new ReflectiveLoadTimeWeaver(classLoader); if (logger.isDebugEnabled()) { logger.debug("Using 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
时初始化了Instrumentation
1 2 3 4 public InstrumentationLoadTimeWeaver (@Nullable ClassLoader classLoader) { this .classLoader = classLoader; this .instrumentation = getInstrumentation(); }
顺着调用关系,将会一直跟到提供了premain
方法的InstrumentationSavingAgent
,而对应的jar包正是上面示例中javaagent所指定的spring-instrument,也就是说当AspectJWeavingEnabler
实例化时,它已经拿到了premain
所提供的Instrumentation
了,接下来就是怎么使用了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public final class InstrumentationSavingAgent { private static volatile Instrumentation instrumentation; private InstrumentationSavingAgent () { } public static void premain (String agentArgs, Instrumentation inst) { instrumentation = inst; } public static void agentmain (String agentArgs, Instrumentation inst) { instrumentation = inst; } public static Instrumentation getInstrumentation () { return instrumentation; } }
另外,AspectJWeavingEnabler
自己实现了BeanFactoryPostProcessor
接口,从之前对应用上下文refresh()
过程的介绍可以知道,当所有BeanDefinition
解析完成时,会率先对BeanFactoryPostProcessor
实例化并调用其postProcessBeanFactory
方法,而在实例化的过程中必然会调用上面的postProcessBeforeInitialization
,也就是说实际上在调用之前一切已经准备就绪
而这里所做的实际上就是创建一个ClassFileTransformer
交给上面准备好的Instrumentation
,其中封装了如何对加载了的bean的字节码进行修改的逻辑
org.springframework.context.weaving.AspectJWeavingEnabler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException { enableAspectJWeaving(this .loadTimeWeaver, this .beanClassLoader); } public static void enableAspectJWeaving ( @Nullable LoadTimeWeaver weaverToUse, @Nullable ClassLoader beanClassLoader) { if (weaverToUse == null ) { if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) { weaverToUse = new InstrumentationLoadTimeWeaver(beanClassLoader); }else { throw new IllegalStateException("No LoadTimeWeaver available" ); } } weaverToUse.addTransformer(new AspectJClassBypassingClassFileTransformer(new ClassPreProcessorAgentAdapter())); }
最终交给Instrumentation
的AspectJClassBypassingClassFileTransformer
其实是一个简单的装饰器,实际上对字节码的修改委托给了ClassPreProcessorAgentAdapter
,而它又委托给了ClassPreProcessor
的实现,具体的逻辑这里就不再细究下去了
org.springframework.context.weaving.AspectJClassBypassingClassFileTransformer 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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; } return this .delegate.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); } }
参考:
《spring源码深度解析》 郝佳
https://www.cnblogs.com/rickiyang/p/11368932.html