1. 在启动程序的 jvm argument 中增加 spring-agent.jar 以获得 jvm 导出的 instrumentation
2. aspectj 拦截 domain object 的创建
3. 在 AnnotationBeanConfigurerAspect 中完成对 domain object 的注入
1. Add spring-agent.jar to jvm argument
如果是命令行启动, 使用 java -javaagent:#{your path}/spring-agent.jar MyProgram 命令启动程序, 如果是 ide, 在 jvm argument 中增加 -javaagent:#{your path}/spring-agent.jar 即可.
增加这个参数的目的就是获取 jvm 导出的 instrumentation 引用以便后续操作的进行, 打开 spring-agent.jar 的 META-INF/MENIFEST.MF 会发现其中一句 : Premain-Class: org.springframework.instrument.InstrumentationSavingAgent, 没错, 根据 instrumentation 规范, Premain-Class 就是用于处理 instrumentation 的入口, 事实上 spring-agent.jar 里也只有这一个 class, 打开代码会发现非常简单 :
public class InstrumentationSavingAgent {
private static volatile Instrumentation instrumentation;
/**
* Save the {@link Instrumentation} interface exposed by the JVM.
*/
public static void premain(String agentArgs, Instrumentation inst) {
instrumentation = inst;
}
}
在 premain 方法里将 instrumentation 保存到 static 引用中以便后续访问.
2. 配置 spring 支持 load time weaver
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);
}
}
@Configurable(autowire = Autowire.BY_TYPE)
public class User {
@Resource
// 或使用 @Autowired
private UserRepository userRepository;
public void save() {
userRepository.save(this);
}
}
4. 将 spring-agent.jar, spring-aspects.jar, aspectj-weaver.jar, aspectj-rt.jar 加入到 classpath 中, 运行期主要发生以下调用 :
- LoadTimeWeaverBeanDefinitionParser (spring.jar) // 解析配置
- -> AspectJWeavingEnabler (spring.jar) // 开启 aspectj weaving
- -> InstrumentationSavingAgent (spring-agent.jar) // 获取 instrumentation
- -> InstrumentationLoadTimeWeaver#addTransformer (spring.jar) // 增加 aspectj class transformer 到 instrumentation
- -> ClassPreProcessorAgentAdapter#transform (aspectj-weaver.jar) // aspectj 拦截 domain object 装载
- -> AnnotationBeanConfigurerAspect#configureBean (spring-aspects.jar) // spring 注入依赖到标注了 @Configurable 的对象中
注意前文中的 <context:annotation-config /> 并不是必须的, 如果不配置, userRepository 就不能用 annotation(@Resource 或 @Autowired) 注入而必须使用 set 方法.