Spring之LoadTimeWeaver——一个需求引发的思考
最近有个需求——记录应用中某些接口被调用的轨迹,说白了,记录下入参、出参等即可。
?
?
public class DemoBean { public void run() { System.out.println("Run"); }}???接着,我们编写分析方法执行效率的切面。
?
@Aspectpublic class ProfilingAspect { @Around("profileMethod()") public Object profile(ProceedingJoinPoint pjp) throws Throwable { StopWatch sw = new StopWatch(getClass().getSimpleName()); try { sw.start(pjp.getSignature().getName()); return pjp.proceed(); } finally { sw.stop(); System.out.println(sw.prettyPrint()); } } @Pointcut("execution(public * com.shansun..*(..))") public void profileMethod() {}}?
?
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd"><aspectj> <weaver> <include within="com.shansun..*" /> </weaver> <aspects> <!-- weave in just this aspect --> <aspect name="com.shansun.multidemo.spring.ltw.ProfilingAspect" /> </aspects></aspectj>
?
?
<?xml version="1.0" encoding="GBK"?> <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" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <context:load-time-weaver aspectj-weaving="autodetect" /> <context:component-scan base-package="com.shansun.multidemo"></context:component-scan></beans>
?
通过 <context:load-time-weaver ?aspectj-weaving="on" /> 使 spring 开启loadtimeweaver, 注意 aspectj-weaving 有三个选项 : on,off, auto-detect,?如果设置为 auto-detect, spring 将会在 classpath中查找 aspejct 需要的 META-INF/aop.xml, 如果找到则开启aspectj weaving, 这个逻辑在LoadTimeWeaverBeanDefinitionParser#isAspectJWeavingEnabled 方法中:
protected boolean isAspectJWeavingEnabled(String value, ParserContext parserContext) { if ("on".equals(value)) { return true; } else if ("off".equals(value)) { return false; } else { // Determine default... ClassLoader cl = parserContext.getReaderContext().getResourceLoader().getClassLoader(); return (cl.getResource(ASPECTJ_AOP_XML_RESOURCE) != null); }}?
?
public class Main { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");// DemoBean bean = (DemoBean) ctx.getBean("demoBean"); DemoBean bean = new DemoBean(); bean.run(); }}??
?
????????
?
输出结果如下:
Run
StopWatch'ProfilingAspect': running time (millis) = 0
-----------------------------------------
ms???? %????Task name
-----------------------------------------
?
Exception in thread "main" java.lang.IllegalStateException: Must start with Java agent to use InstrumentationLoadTimeWeaver. See Spring documentation. at org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver.addTransformer(InstrumentationLoadTimeWeaver.java:88) at org.springframework.context.weaving.AspectJWeavingEnabler.postProcessBeanFactory(AspectJWeavingEnabler.java:69) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:553) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:536) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:362) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83) at com.shansun.multidemo.spring.Main.main(Main.java:25)
?
public void addTransformer(ClassFileTransformer transformer) { Assert.notNull(transformer, "Transformer must not be null"); FilteringClassFileTransformer actualTransformer = new FilteringClassFileTransformer(transformer, this.classLoader); synchronized (this.transformers) { if (this.instrumentation == null) { throw new IllegalStateException( "Must start with Java agent to use InstrumentationLoadTimeWeaver. See Spring documentation."); } this.instrumentation.addTransformer(actualTransformer); this.transformers.add(actualTransformer); }}?
?
?
public class ExtInstrumentationLoadTimeWeaver extends InstrumentationLoadTimeWeaver { @Override public void addTransformer(ClassFileTransformer transformer) { try { super.addTransformer(transformer); } catch (Exception e) {} }}????
?
?
<?xml version="1.0" encoding="GBK"?> <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" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <context:load-time-weaver weaver-aspectj-weaving="autodetect" /> <context:component-scan base-package="com.shansun.multidemo"></context:component-scan></beans>?
?
再次运行我们的main方法,发现只输出了如下结果,切面没有起作用。
Run
?
看到了么,同一份代码、同一份配置,只需要在VM启动参数中稍加变化,即可实现同一个应用包在不同环境下可以自由选择使用使用AOP功能。文章开头提到的那个需求也就迎刃而解了。
?
这只是一种解决途径,相信大家会有更好的方案。如果有,请您告诉我。J
?
参考文档:
1、使用AspectJLTW(Load Time Weaving)
2、Spring LoadTimeWeaver 的那些事儿
3、在Spring应用中使用AspectJ
?
1 楼 littleJava 2011-08-22 Instrumentation 和 javassist根据定义的加载规则,就可以实现了,原理也是aop 2 楼 hyaci2011 2011-11-04 Aspectj 难道 就不能单独支持 LTW ?一定要和spring 整合在一起..很纠结呀..楼上的 解释一下 instrumentation ..我学习一下.