本文引自我的个人博客: sunmingshuai.coding.me
写在前面
在 ClassPathXmlApplicationContext源码解析一 中 我们用了很长的篇幅 介绍了加载上下文环境前的各种准备工作 spring是个世界级的项目框架 由精英团队打造 全世界的测试人员测试 即使如果读者感觉有点吃力 对突然冒出来的代码有点不习惯的话 也不要气馁 继续往下读 刚阅读源代码的时候 最怕纠结于一些琐碎单却不重要的事情
下面我们看一下parseBeanDefinitions(root, this.delegate)
方法
//对于`delegate` 我们暂时只要知道这个类中含有我们需要的一些environment,beanfactory等信息就可以了 当然他还提供一些解析方法
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
/**
* 如果是默认命名空间 也就是"http://www.springframework.org/schema/beans"这个空间下的元素 调用parseDefaultElement
* 否则调用parseCustomElement 例如aop context tx等标签的解析
* 也就是parseDefaultElement 解析import alias bean beans标签元素
* parseCustomElement 解析aop context tx 或者是用户自定义命名空间的元素
*/
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
//继续这个方法研究
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
我们这篇文章只看默认命名空间的解析 也就是http://www.springframework.org/schema/beans
这个空间下的定义的元素定义 对于非默认空间下的元素的解析的话 请看component-scan做了些什么 这篇文章讲解了component
空间下的元素解析
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//解析import元素标签 通过调用`loadBeanDefinitions`模块方法完成元素加载<import resource="url"/>
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//解析alias元素标签 <alias name="" alias="" />
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
//注册别名 说白点就是把name alias的对应关系加入到一个map中
processAliasRegistration(ele);
}
//解析bean元素标签 最核心 <bean >
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//解析beans元素标签 又调用了`doRegisterBeanDefinitions(element)`方法去解析<beans>
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
到达这个方法的时候 ele
代表的就是一个bean
标签元素了 例如
<bean id="" class="">
我们不必深入了解xml文档的解析 也是能看的懂的 当然如果了解解析的过程 是更好的了
我们可以很清晰的看到beans
空间下的元素标签有import
alias
bean
beans
我们当然是重点讲解bean
标签的解析了 继续跟踪代码
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//继续跟踪
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//处理非默认命名空间的元素 如果有的话 这里不继续跟踪下去了
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 也就是放到一个map中去 key为beanName value为BeanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
跟踪进BeanDefinitionParserDelegate.parseBeanDefinitionElement
方法
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//bean标签中的name其实会被划分成alias
List<String> aliases = new ArrayList<String>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
//beanName唯一性校验
checkNameUniqueness(beanName, aliases, ele);
}
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);//GenericBeanDefinition
if (beanDefinition != null) {
// 阅读源代码要做到有的放矢 像下面这样的代码就没有太大的必要去看了
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
//返回BeanDefinitionHolder
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
id
就是beanName
了 必须要唯一才行 其实parseBeanDefinitionElement
这个方法的目的就很明确了 就是要把bean
的配置读取并保存起来 保存到GenericBeanDefinition
中 例如我们常见的property
constructor-arg
等 无非就是构造合适的结构 然后读取数据并保存起来 供以后使用 虽然目的明确 但过程确实是很繁琐的 因为spring
支持多种复杂数据结构 例如list map等
我们就不再深入解读了 读者可以自己找几个解析方法阅读
/**
* 在这个方法中会遇到我们之前可能没有遇到的标签/属性元素 最好的解决办法就是先搜索这个元素/属性的用法,然后再看解析, 再看他们的应用处理等
* 我们这里不对这些不常用的元素/属性进行详解 这里主要就是解析出来并保存到BeanDefinitiion中
*/
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//GenericBeanDefinition 设置className parentName变量 parent属性目前只是作为一个普通属性放倒BeanDefinition中去了 后面加载单例的时候会用到
AbstractBeanDefinition bd = createBeanDefinition(className, parent);//
//解析元素属性 例如scope lazy-init factory-method等属性的解析
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//解析元素子元素开始
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//<meta key="" value="" /> 作为对bd的额外参数使用
parseMetaElements(ele, bd);
//<lookup-method name="" bean="" /> 插拔式设计 方法根据配置 动态返回不同的bean 感兴趣的可以自行搜索相关使用demo
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//<replaced-method name="" replacer=""/> 根据配置 替换/更改原来的方法逻辑 interesting 感兴趣的可以自行搜索相关使用demo
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//todo <constructor-arg index="i"></constructor-arg>
parseConstructorArgElements(ele, bd);
//<property name="" value=""/> or more complex property such as list,map 阅读源代码 我们要有的放矢
// 没必要把每一行都看懂 就像下面的解析构造参数 与 property一样 都是很复杂的 但我们没必要都读懂才行
parsePropertyElements(ele, bd);
//<qualifier />
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
生成beanDefinition
后 再用BeanDefinitionHolder
封装后返回注册 至此xml配置文件中的设置就转化为了java对象保存
我们在回到refresh()
方法 继续往下看prepareBeanFactory()
方法
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
//
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver());
// 我们知道xml文档中的都是字符串形式的值 但我们可能需要的是其他的类型 比如Float Date等
// 这里注册一些常用的属性编辑器 去解决类型转化问题
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
//BPP 作用是如果bean实现了Aware接口的话 自动调用`setXXX`方法
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
//忽略特殊类`ResourceLoaderAware` 等的依赖
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Detect a LoadTimeWeaver and prepare for weaving, if found.
// @AspectJ相关 不常用 后面会专门讲解Spring AOP
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
代码里面的注释应该是很详细了 我们来分析一个很有趣的事情
如果我们想在我们自己类中 获取如ApplicationContext
的话 我们一般会继承ApplicationContextAware 然后有个回调方法会让我们实现 例如setApplicationContext 那么为什么我们只要实现了这个接口 我们实现的回调方法就会调用呢 奥秘就在这句代码beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this))
BPP(BeanFactoryPostProcessor)是个很重要的知识点 我们这里大概了解一下
不管是单例模式还是原型模式 我们定义的class肯定是要实例化的 如果有定义初始化方法的话 还会调用初始化方法 对于一个类来说 实例化 初始化都是很重要的时间节点 我们的
class
又是托管给spring
进行管理的 那么spring
就有能力 有机会在这些时间节点前后添加一些处理方法spring
中代表这一类操作的接口是BeanPostProcessor
简称BPP 中文翻译是后置处理器 当然spring
中还有一种PostProcessor是容器级别的BeanFactoryPostProcessor
就是在容器加载前后做一些特殊的操作
在ApplicationContextAwareProcessor.postProcessBeforeInitialization(final Object bean, String beanName)
方法中 会调用invokeAwareInterfaces(bean)
方法
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(
new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
看起来很神秘的东西 它的实现是不是很一般 而且还感觉有点low 竟然没有魔法