上文我们主要学习了Spring是如何获取xml配置文件并且将其转换成Document,我们知道xml文件是由各种标签组成,Spring需要将其解析成对应的配置信息。之前提到过Spring中的标签包括默认标签和自定义标签两种,而两种标签的用法以及解析方式存在着很大的不同,本文详细分析默认标签的解析过程。

  默认标签的解析是在parseDefaultElement函数中进行的,函数中的功能逻辑一目了然,分别对4种不同标签(import、alias、bean和 beans)做了不同的处理。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
// 对import标签的处理
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
// 对alias标签的处理
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
// 对bean标签的处理
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
// 对beans标签的处理
doRegisterBeanDefinitions(ele);
}
}

  在4种标签的解析中,对bean标签的解析最为复杂也最为重要,这是本文重点分析对象,如果能理解此标签的解析过程,其他标签的解析自然会迎刃而解。首先我们进入函数processBeanDefinition(ele, delegate):

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
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));
}
}

  大致的逻辑总结如下:

  1. 首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的实例bdHolder,经过这个方法后,bdHolder实例已经包含我们配置文件中配置的各种属性了,例如class、name、id、alias之类的属性;
  2. 当返回的bdHolder不为空的情况下若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析;
  3. 解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法;
  4. 最后发出响应事件,通知相关的监听器,这个bean已经加载完成了;

  下面会针对各个操作做进一步分析。

1. 解析BeanDefinition

  首先我们从元素解析及信息提取开始,也就是BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele),进入BeanDefinitionDelegate类的parseBeanDefinitionElement方法:

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
} public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
// 解析id属性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 解析name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 分割name属性
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) {
checkNameUniqueness(beanName, aliases, ele);
} AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
// 如果不存在beanName那么根据Spring中提供的命名规则为当前bean生成对应的beanName
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
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;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
} return null;
}

  以上便是对默认标签解析的全过程了。在开始对属性展开全面解析前,Spring又做了一些功能划分,主要如下:

  1. 提取元素中的id以及name属性;
  2. 进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中;
  3. 如果检测到bean没有指定beanName,那么使用默认规则为此Bean生成beanName;
  4. 将获取到的信息封装到BeanDefinitionHolder的实例中;

  我们进一步地查看步骤2中对标签其他属性的解析过程:

public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
// 解析class属性
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
} try {
String parent = null;
// 解析parent属性
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
// 创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 硬编码解析默认bean的各种属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 提取description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析元数据
parseMetaElements(ele, bd);
// 解析lookup-method属性
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析replaced-method属性
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析构造函数参数
parseConstructorArgElements(ele, bd);
// 解析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;
}

  到这里,bean标签的所有属性解析,不论常用的还是不常用的我们都看到了,尽管有些复杂的属性还需要进一步的解析。我们着重看一些复杂标签属性的解析。

1.1 创建用于属性承载的BeanDefinition

  BeanDefinition是一个接口,在Spring中有三个实现类:RootBeanDefinition、ChildBeanDefinition以及GenericBeanDefinition,均继承自AbstractBeanDefiniton,其中BeanDefinition是配置文件<bean>元素标签在容器中的内部表示形式。<bean>元素标签拥有class、scope、lazy-init等配置属性,BeanDefinition则提供了相应的beanClass、scope、lazyInit属性,BeanDefinition和<bean>中的属性是一一对应的。其中RootBeanDefinition是最常用的实现类,它对应一般性的<bean>元素标签, GenericBeanDefinition是自2.5版本以后新加入的bean文件配置属性定义类,是一站式服务类。

  在配置文件中可以定义父<bean>和子<bean>,父<bean>用RootBeanDefinition表示,而子<bean>用ChildBeanDefiniton表示,而没有父<bean>的<bean>就使用RootBeanDefinition表示。

  Spring通过BeanDefinition将配置文件中的<bean>配置信息转换为容器的内部表示,并将这些BeanDefiniton注册到BeanDefinitonRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息

  由此可知,要解析属性首先要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例。而代码createBeanDefinition(className,parent)的作用就是实现此功能:

protected AbstractBeanDefinition createBeanDefinition(String className, String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
} public static AbstractBeanDefinition createBeanDefinition(
String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
// parentName可能为空
bd.setParentName(parentName);
if (className != null) {
// 如果classLoader不为空,则使用已传入的classLoader加载类对象,否则只是记录className
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}

1.2 解析各种属性

  当我们创建了bean信息的承载实例后,便可以开始各种属性解析了,这部分的主要逻辑在parseBeanDefinitionAttributes方法中:

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
BeanDefinition containingBean, AbstractBeanDefinition bd) {
// 解析scope属性
if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
// Spring 2.x "scope" attribute
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
// scope与singleton两个属性只能指定其中之一,不可以同时出现,否则Spring将会报出异常
error("Specify either 'scope' or 'singleton', not both", ele);
}
}
// 解析singleton属性
else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
// Spring 1.x "singleton" attribute
bd.setScope(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)) ?
BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);
}
else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
// 在嵌入beanDefinition情况下且没有单独指定scope属性则使用父类默认的属性
bd.setScope(containingBean.getScope());
}
// 解析abstract属性
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
// 解析lazy-init属性
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (DEFAULT_VALUE.equals(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
// 若没有设置或设置成其他字符都会被设置为false
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
// 解析autowire属性
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
// 解析dependency-check属性
String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);
bd.setDependencyCheck(getDependencyCheck(dependencyCheck));
// 解析depends-on属性
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
// 解析autowire-candidate属性
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
}
else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
}
// 解析primary属性
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
// 解析init-method属性
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
if (!"".equals(initMethodName)) {
bd.setInitMethodName(initMethodName);
}
}
else {
if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
}
// 解析destroy-method属性
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
if (!"".equals(destroyMethodName)) {
bd.setDestroyMethodName(destroyMethodName);
}
}
else {
if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
}
// 解析factory-method属性
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
// 解析factory-bean属性
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}

  我们可以清楚地看到Spring完成了对所有bean属性的解析,这些属性中有很多是我们经常使用的,也有些是我们不熟悉的,有兴趣可以查阅相关资料进一步了解。当然除了这部分属性解析,后面还有一些其他的属性解析(比如constructor-arg、property等),在此就不全部列出了。

2. AbstractBeanDefinition

  至此便完成了对XML文档到GenericBeanDefinition的转换,也就是说到这里,XML中所有的配置都可以在GenericBeanDefinition的实例类中找到对应的属性。

  GenericBeanDefinition只是子类实现,大部分的通用属性都保存在了AbstractBeanDefinition中,所以这里再次通过AbstractBeanDefinition的属性来回顾一下都解析了哪些对应的配置,以加深理解:

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
// 此处省略静态变量以及final常量 /** bean的作用范围,对应bean属性scope */
private String scope = SCOPE_DEFAULT; /** 是否是单例,来自bean属性scope */
private boolean singleton = true; /** 是否是原型,来自bean属性scope */
private boolean prototype = false; /** 是否是抽象,对应bean属性abstract */
private boolean abstractFlag = false; /** 是否延迟加载,对应bean属性lazy-init */
private boolean lazyInit = false; /** 自动注入模式,对应bean属性autowire */
private int autowireMode = AUTOWIRE_NO; /** 依赖检查,Spring 3.0后弃用这个属性 */
private int dependencyCheck = DEPENDENCY_CHECK_NONE; /** 用来表示一个bean的实例化依靠另一个bean先实例化,对应bean属性depend-on */
private String[] dependsOn; /** autowire-candidate属性设置为false,这样容器在查找自动装配对象时,
*将不考虑该bean,即它不会被考虑作为其他bean自动装配的候选
*者,但是该bean本身还是可以使用自动装配来注入其他bean的
*对应bean属性autowire-candidate */
private boolean autowireCandidate = true; /** 自动装配时当出现多个bean候选着时,将作为首选着,对应bean属性primary */
private boolean primary = false; /** */
private final Map<String, AutowireCandidateQualifier> qualifiers =
new LinkedHashMap<String, AutowireCandidateQualifier>(0); /** */
private boolean nonPublicAccessAllowed = true; /** */
private boolean lenientConstructorResolution = true; /** */
private ConstructorArgumentValues constructorArgumentValues; /** */
private MutablePropertyValues propertyValues; /** */
private MethodOverrides methodOverrides = new MethodOverrides(); /** */
private String factoryBeanName; /** */
private String factoryMethodName; /** */
private String initMethodName; /** */
private String destroyMethodName; /** */
private boolean enforceInitMethod = true; /** */
private boolean enforceDestroyMethod = true; /** */
private boolean synthetic = false; /** */
private int role = BeanDefinition.ROLE_APPLICATION; /** */
private String description; /** 这个bean定义的资源 */
private Resource resource;
}

3. 注册解析的BeanDefinition

  解析完成之后得到的beanDinition已经可以满足后续的使用要求了,唯一还剩下的工作就是注册,也就是processBeanDefinition函数中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext(). get())所完成的工作:

public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException { // Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}

  从上面的代码可以看出,解析好的beanDefinition都会被注册到BeanDefinitionRegistry类型的实例registry中,beanDefinition的注册分成了两部分:通过beanName的注册以及通过别名的注册。

3.1 通过beanName注册BeanDefinition

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// 注册前的最后一次校验,这里的校验不同于之前的XML文件校验
// 主要是对于AbstractBeanDefinition属性中的methodOverrides校验
// 校验methodOverrides是否与工厂方法并存或者methodOverrides对应的方法根本不存在
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
} BeanDefinition oldBeanDefinition;
// 因为beanDefinitionMap是全局变量,这里肯定会存在并发访问的情况
synchronized (this.beanDefinitionMap) {
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
// 处理注册已经注册的beanName情况
if (oldBeanDefinition != null) {
// 如果对应的BeanName已经注册并且在配置中配置了bean不允许被覆盖,则抛出异常
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
else {
// 记录beanName
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
// 注册beanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
}
// 重置所有beanName对应的缓存
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}

  如上,在对于bean的注册处理方式上,主要进行了如下几个步骤:

  1. 对AbstractBeanDefinition的校验。在解析XML文件的时候我们提过校验,但是此校验非彼校验,之前的校验是针对于XML格式的校验,而此时的校验则是对于AbstractBeanDefinition的methodOverrides属性的;
  2. 对beanName已经注册的情况的处理。如果设置了不允许bean的覆盖,会抛出异常,否则直接覆盖;
  3. 加入map缓存;
  4. 清除解析之前留下的对应 beanName的缓存;

3.2 通过别名注册BeanDefinition

public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
// 如果beanName与alias相同的话不记录alias,并删除对应的alias
if (alias.equals(name)) {
this.aliasMap.remove(alias);
}
else {
// 如果alias不允许被覆盖则抛出异常
if (!allowAliasOverriding()) {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null && !registeredName.equals(name)) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
}
// 当A->B存在时,若再次出现A->C->B时候则会抛出异常
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
}
}

  由以上代码中可以得知注册alias的步骤如下:

  1. alias与beanName相同情况处理。若alias与beanName名称相同则不需要处理并删除掉原有alias;
  2. alias覆盖处理。若aliasName已经使用并已经指向了另一beanName则需要根据用户的设置进行处理;
  3. alias循环检查。当A->B存在时,若再次出现A->C->B时候则会抛出异常;
  4. 注册alias;

4. 总结

  本文主要集中在分析从如何将默认xml标签解析成BeanDefinition到将其注册到容器中这一过程,至此Spring对配置的转化工作就完成了,后面就要开始Bean的获取这部分的逻辑的分析了。

Spring源码阅读笔记04:默认xml标签解析的更多相关文章

  1. Spring源码阅读笔记03:xml配置读取

    前面的文章介绍了IOC的概念,Spring提供的bean容器即是对这一思想的具体实现,在接下来的几篇文章会侧重于探究这一bean容器是如何实现的.在此之前,先用一段话概括一下bean容器的基本工作原理 ...

  2. Spring源码阅读笔记02:IOC基本概念

    上篇文章中我们介绍了准备Spring源码阅读环境的两种姿势,接下来,我们就要开始探寻这个著名框架背后的原理.Spring提供的最基本最底层的功能是bean容器,这其实是对IoC思想的应用,在学习Spr ...

  3. Spring源码阅读 之 配置的读取,解析

    在上文中我们已经知道了Spring如何从我们给定的位置加载到配置文件,并将文件包装成一个Resource对象.这篇文章我们将要探讨的就是,如何从这个Resouce对象中加载到我们的容器?加载到容器后又 ...

  4. Spring源码阅读笔记

    前言 作为一个Java开发者,工作了几年后,越发觉力有点不从心了,技术的世界实在是太过于辽阔了,接触的东西越多,越感到前所未有的恐慌. 每天捣鼓这个捣鼓那个,结果回过头来,才发现这个也不通,那个也不精 ...

  5. Spring源码阅读笔记01:源码阅读环境准备

    1. 写在前面 对于做Java开发的同学来说,Spring就像是一条绕不过去的路,但是大多数也只是停留在对Spring的简单使用层面上,对于其背后的原理所知不多也不愿深究,关于这个问题,我在平时的生活 ...

  6. Spring源码阅读笔记05:自定义xml标签解析

    在上篇文章中,提到了在Spring中存在默认标签与自定义标签两种,并且详细分析了默认标签的解析,本文就来分析自定义标签的解析,像Spring中的AOP就是通过自定义标签来进行配置的,这里也是为后面学习 ...

  7. spring源码阅读笔记06:bean加载之准备创建bean

    上文中我们学习了bean加载的整个过程,我们知道从spring容器中获取单例bean时会先从缓存尝试获取,如果缓存中不存在已经加载的单例bean就需要从头开始bean的创建,而bean的创建过程是非常 ...

  8. spring源码阅读笔记08:bean加载之创建bean

    上文从整体视角分析了bean创建的流程,分析了Spring在bean创建之前所做的一些准备工作,并且简单分析了一下bean创建的过程,接下来就要详细分析bean创建的各个流程了,这是一个比较复杂的过程 ...

  9. spring源码阅读笔记09:循环依赖

    前面的文章一直在研究Spring创建Bean的整个过程,创建一个bean是一个非常复杂的过程,而其中最难以理解的就是对循环依赖的处理,本文就来研究一下spring是如何处理循环依赖的. 1. 什么是循 ...

随机推荐

  1. getWeekDay TimeUtil

    package me.zhengjie.common.utils; import java.util.Calendar; import java.util.Date; /** * @author ji ...

  2. PAT甲级——1058 A+B in Hogwarts

    1058 A+B in Hogwarts If you are a fan of Harry Potter, you would know the world of magic has its own ...

  3. transcription-coupled repair|Germ|HK|TS|Mutation|四类变异

    生命组学-可遗传变异 GC content:碱基: GC content不同的植物对应的gene length,可看作上图的转置: 由GC content看出来碱基变异程度,可以找到对应碱基改变,所以 ...

  4. TCP\IP协议簇-各层主要协议帧格式

    本文只是对各协议的概要,详细请参考rfc文件. 官方下载地址:https://tools.ietf.org/rfc/index rfc中文:http://man.chinaunix.net/devel ...

  5. python之接口自动化测试框架

    梳理python+unittest接口自动化测试框架的思路: 1.确定目录: cases:存放测试用例的py文件:config:存放一些数据库,环境地址等固定不变的信息: core:核心的文件, ca ...

  6. 关联规则之Aprior算法

    关联规则挖掘在电商.零售.大气物理.生物医学已经有了广泛的应用,本篇文章将介绍一些基本知识和Aprori算法. 啤酒与尿布的故事已经成为了关联规则挖掘的经典案例,还有人专门出了一本书<啤酒与尿布 ...

  7. idea 创建项目没有web.xml文件,如何添加

    1.首先看下项目工程里面是否有WEB-INF文件夹,没有就创建一个 2.点击 file 选择 project structure 3.选择 facets,点击+号, 选择 web 4.弹出 弹框 选择 ...

  8. linux下使用过的命令总结(未整理完)

    1.常用命令不需解释 ls\cd\cp\mv\pwd\file\vi\vim\cat 2.getconf LONG_BIT 终端返回32表示操作系统32位,返回64表示操作系统64位. 3.ifcon ...

  9. git获取公钥和私钥以及常用的命令

    Git简单生成公钥和私钥的方法 Git安装完之后,需做最后一步配置.打开git bash,分别执行以下两句命令 git config --global user.name “用户名” git conf ...

  10. JavaScript类的写法(一)

    转自:http://segmentfault.com/a/1190000000725051 js类的基本含义 我们知道,在js中,是没有类的概念的.类的所有实例对象都从同一个原型对象上继承属性,因此, ...