AOP静态代理使用示例见Spring的LoadTimeWeaver(代码织入)

Instrumentation使用示例见java.lang.instrument使用

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

AspectJ所做的事

在Spring中的静态AOP直接使用了AspectJ提供的方法,而AspectJ又是在Instrument基础上进行的封装。就以上面的两个使用示例来看,至少在AspectJ中会有如下功能。

(1)读取META-INF/aop.xml。

(2)将aop.xml中定义的增强器通过自定义的ClassFileTransformer织入对应的类中。

这都是AspectJ所做的事情,并不在我们讨论的范畴,Spring是直接使用AspectJ,也就是将动态代理的任务直接委托给了AspectJ,那么,Spring怎么嵌入AspectJ的呢?从配置文件入手。

标签解析入口

spring.handlers

http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler

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函数开始的,而经过父类的封装,LoadTimeWeaverBeanDefinitionParser类的核心实现被转移到了doParse函数中

    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
if (isAspectJWeavingEnabled(element.getAttribute(ASPECTJ_WEAVING_ATTRIBUTE), parserContext)) {
RootBeanDefinition weavingEnablerDef = new RootBeanDefinition();
weavingEnablerDef.setBeanClassName(ASPECTJ_WEAVING_ENABLER_CLASS_NAME);
parserContext.getReaderContext().registerWithGeneratedName(weavingEnablerDef); if (isBeanConfigurerAspectEnabled(parserContext.getReaderContext().getBeanClassLoader())) {
new SpringConfiguredBeanDefinitionParser().parse(element, parserContext);
}
}
}

其实之前在分析动态AOP也就是在分析配置中已经提到了自定义配置的解析流程,对于的解析无非是以标签作为标志,进而进行相关处理类的注册,那么对于自定义标签其实是起到了同样的作用。上面函数的核心作用其实就是注册一个对于ApectJ处理的类org.Springframework.context.weaving.AspectJWeavingEnabler,它的注册流程总结起来如下。

(1)是否开启AspectJ。

<context:load-time-weaver aspectj-weaving="autodetect" />

这个标签中还有一个属性aspectj-weaving,这个属性有3个备选值,on、off和autodetect,默认为autodetect,也就是说,如果我们只是使用了,那么Spring会帮助我们检测是否可以使用AspectJ功能,而检测的依据便是文件META-INF/aop.xml是否存在,看看在Spring中的实现方式。

    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(AspectJWeavingEnabler.ASPECTJ_AOP_XML_RESOURCE) != null);
}
}

(2)将org.Springframework.context.weaving.AspectJWeavingEnabler封装在BeanDefinition中注册。

当通过AspectJ功能验证后便可以进行AspectJWeavingEnabler的注册了,注册的方式很简单,无非是将类路径注册在新初始化的RootBeanDefinition中,在RootBeanDefinition的获取时会转换成对应的class。(weavingEnablerDef.setBeanClassName(ASPECTJ_WEAVING_ENABLER_CLASS_NAME);)尽管在init方法中注册了AspectJWeavingEnabler,但是对于标签本身Spring也会以bean的形式保存,也就是当Spring解析到标签的时候也会产生一个bean,而这个bean中的信息是什么呢?

在LoadTimeWeaverBeanDefinitionParser类中有这样的函数:

private static final String WEAVER_CLASS_ATTRIBUTE = "weaver-class";
private static final String ASPECTJ_WEAVING_ATTRIBUTE = "aspectj-weaving";
private static final String DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME =
"org.springframework.context.weaving.DefaultContextLoadTimeWeaver";
private static final String ASPECTJ_WEAVING_ENABLER_CLASS_NAME =
"org.springframework.context.weaving.AspectJWeavingEnabler";
@Override
protected String getBeanClassName(Element element) {
if (element.hasAttribute(WEAVER_CLASS_ATTRIBUTE)) {
return element.getAttribute(WEAVER_CLASS_ATTRIBUTE);
}
return DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME;
}
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) {
return ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME;//loadTimeWeaver
}

其中,可以看到:

WEAVER_CLASS_ATTRIBUTE="weaver-class"

DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME ="org.Springframework.context.weaving.DefaultContextLoadTimeWeaver";

ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME=”loadTimeWeaver”

凭以上的信息我们至少可以推断,当Spring在读取到自定义标签后会产生一个bean,而这个bean的id为loadTimeWeaver,class为org.Springframework.context.weaving.DefaultContextLoadTimeWeaver,也就是完成了DefaultContextLoadTimeWeaver类的注册。

完成了以上的注册功能后,并不意味这在Spring中就可以使用AspectJ了,因为我们还有一个很重要的步骤忽略了,就是LoadTimeWeaverAwareProcessor的注册。在AbstractApplicationContext中的prepareBeanFactory函数中有这样一段代码:

if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {//loadTimeWeaver
  beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
  // Set a temporary ClassLoader for type matching.
  beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader (beanFactory.getBeanClassLoader()));
}

在AbstractApplicationContext中的prepareBeanFactory函数是在容器初始化时候调用的,也就是说只有注册了LoadTimeWeaverAwareProcessor才会激活整个AspectJ的功能。

总结:

在解析load-time-weaver标签时,从getBeanClassName方法中可以看到,如果没有指定weaver-class属性,会自动给容器中注入一个org.springframework.context.weaving.DefaultContextLoadTimeWeaver类型的bean,从resolveId方法中看到,该bean的名称为loadTimeWeaver。在doParse方法中,还会注册一个类型为org.springframework.context.weaving.AspectJWeavingEnabler的匿名bean。

从此可以看出下面两段配置完全是等价的:

<bean id="loadTimeWeaver"
class="org.springframework.context.weaving.DefaultContextLoadTimeWeaver"></bean>
<bean class="org.springframework.context.weaving.AspectJWeavingEnabler"></bean>
<context:load-time-weaver aspectj-weaving="autodetect" />

<context:load-time-weaver aspectj-weaving="autodetect" />

最新文章

  1. [Java 基础]控制语句
  2. 10款.net 图形插件
  3. AngularJs开发环境搭建
  4. C#复习①
  5. Leetcode ReorderList
  6. sqlite以及python的应用
  7. Android 自带图标库 android.R.drawable
  8. 在Tomcat中配置数据源
  9. 2014图灵技术图书最受欢迎TOP15
  10. 149. Max Points on a Line
  11. vim 中Ctags的安装和使用
  12. #define和typedef在windows上的应用
  13. Feign get接口传输对象引发一场追寻
  14. jmeter 写正则表达式
  15. iOS-野指针与僵尸对象
  16. 腾讯云CentOS7.4服务器添加swap分区
  17. Python—字典的操作
  18. Tesseract-OCR的简单使用与训练
  19. 利用官方的vue-cli脚手架来搭建Vue集成开发环境
  20. 《mysql必知必会》学习_第11章_20180801_欢

热门文章

  1. 【leetcode】Binary Tree Level Order Traversal I &amp; II
  2. python 获取控制台输入
  3. SQLServer自定义函数简单演示
  4. SQL Server之存储过程基础知
  5. BeatifulSoup文档地址
  6. 关于KVC、KVO
  7. Swift - 代码创建单例
  8. [转] C++的STL库,vector sort排序时间复杂度 及常见容器比较
  9. 20145206实验四《Android开发基础》
  10. MVC缓存02,使用数据层缓存,添加或修改时让缓存失效