从web.xml开始以及延伸出来的问题-spring源码分析-IOC

本文从web.xml配置文件和注解理解出发,深入剖析Spring IOC的启动过程,包括资源定位、XML解析和BeanDefinition注册。接着详细阐述依赖注入的时机和实现,如AbstractBeanFactory的createBeanInstance和populateBean方法。最后讨论了自动依赖注入的优缺点,指出IOC容器如何管理对象间的依赖关系。
摘要由CSDN通过智能技术生成

目录

注解和xml配置文件的理解

spring IOC启动过程

依赖注入

 


1、小马哥源码解读Spring IOC原理

2、Spring BeanDefinition 生成所有bean的配置信息

3、Spring依赖注入源码分析

 

注解和xml配置文件的理解

容器启动时,先读取配置文件,如果发现配置文件里设置了扫描注解,则扫描注解生成每个bean的BeanDefinition放入BeanDefinition 容器。再继续读取xml,将配置的bean放入到BeanDefinition 容器中,这样bean定义容器就有了所有的BeanDefinition。
下一步就根据这些bean定义生成bean对象,并注入依赖的bean,如果依赖的bean没有,则继续生成,这样递归下去,知道所有的bean都生成结束。

spring IOC启动过程


构造一个IOC容器需要如下几个步骤:
第一步:资源的定位。所谓资源就是指Spring里众多的XML配置文件,要获取到配置文件里面的信息,首先是要找到它。
第二步:加载和解析资源文件。XML文件里面定义的一些节点,和Spring里面定义的数据结构不匹配,那么就需要按照Spring的解析规则将XML解析成Spring需要的。xml-->Document对象 -- >spring-bean规则定义的BeanDefinition
第三步:将解析完的数据结构注册到IOC容器中。Spring中内部的数据结构叫BeanDefinition。


经过以上三个步骤之后,IOC容器已经构造好,但是还是不能被直接使用。BeanDefinition只是配置文件里的配置在IOC中建立的一个映射,对于IOC容器来说最重要的依赖关系都还没有注入呢,相当于光有一个壳,内容还没有填充。
这里又有个Spring中重要的概念,叫做依赖注入。依赖的意思上文中有提到过吧,就是对象的引用关系嘛,注入是个动词,个人理解为设置,那依赖注入的意思不就是设置对象的引用关系了哇。
接下来这个过程是容器的实例化。容器的实例化只有一步,就是依赖注入。
完成IOC容器的构造和实例化之后,完整的IOC就建立好了。主线分析好了,接下来便可以顺藤摸瓜的学习源码了。

 

// IoC容器启动过程简析
public void TestDefaultListableBeanFactory(){
  
  // 获取资源
  ClassPathResource resource  = new ClassPathResource("spring.xml");
  // 创建IoC容器
  DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

  // 资源读取器
  XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);

  // 资源读取器解析资源文件 解析为BeanDefinition
  reader.loadBeanDefinitions(resource);

  MyBean bean = factory.getBean(MyBean.class);

  System.out.println(bean.toString());

}

// 定位:获取xml位置
// XmlWebApplicationContext
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
	String[] configLocations = getConfigLocations();
	if (configLocations != null) {
		for (String configLocation : configLocations) {
			reader.loadBeanDefinitions(configLocation);
		}
	}
}

/*
  加载解析:DefaultDocumentLoader 将xml解析为Document对象
  载入Bean定义资源文件的最后一步是将Bean定义资源转换为Document对象,该过程由documentLoader实现
  至此Spring IoC容器根据定位的Bean定义资源文件,将其加载读入并转换成为Document对象过程完成。
*/
// XmlBeanDefinitionReader
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
	try {
		// 将xml解析为Document对象
		Document doc = doLoadDocument(inputSource, resource);

		// 这里是启动对Bean定义解析的详细过程,该解析过程会用到Spring的Bean配置规则
		return registerBeanDefinitions(doc, resource);
	}
	catch (BeanDefinitionStoreException ex) {
		throw ex;
	}
}


/*
	Bean定义资源的载入解析分为以下两个过程:

	首先,通过调用XML解析器将Bean定义资源文件转换得到Document对象,但是这些Document对象并没有按照Spring的Bean规则进行解析。这一步是载入的过程

	其次,在完成通用的XML解析之后,按照Spring的Bean规则对Document对象进行解析。

	按照Spring的Bean规则对Document对象解析的过程是在接口BeanDefinitionDocumentReader的实现类DefaultBeanDefinitionDocumentReader中实现的。
*/


/*
 DefaultBeanDefinitionDocumentReader 默认的BeanDefinition文档读取类
 */
 protected void doRegisterBeanDefinitions(Element root) {
	String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
	if (StringUtils.hasText(profileSpec)) {
		Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
		String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
				profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
		if (!this.environment.acceptsProfiles(specifiedProfiles)) {
			return;
		}
	}

	// Any nested <beans> elements will cause recursion in this method. In
	// order to propagate and preserve <beans> default-* attributes correctly,
	// keep track of the current (parent) delegate, which may be null. Create
	// the new (child) delegate with a reference to the parent for fallback purposes,
	// then ultimately reset this.delegate back to its original (parent) reference.
	// this behavior emulates a stack of delegates without actually necessitating one.
	BeanDefinitionParserDelegate parent = this.delegate;
	this.delegate = createDelegate(this.readerContext, root, parent);
    // 解析前置操作
	preProcessXml(root);
	//使用Spring的Bean规则从Document的根元素开始进行Bean定义的Document对象
	parseBeanDefinitions(root, this.delegate);
	// 解析后置操作
	postProcessXml(root);

	this.delegate = parent;
}


/**
 * 处理BeanDefinition
   1、解析bean,将xml中<bean>解析未BeanDefinition 封装到BeanDefinitionHolder中:return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
   2、注册BeanDefinition到IoC容器当中
 */
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	// 12、BeanDefinition解析代理解析Bean定义资源文件中的<Bean>元素
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		try {
			// Register the final decorated instance.
			// 在其parseDefaultElement方法中完成对Document对象的解析后得到封装BeanDefinition的BeanDefinitionHold对象,
			// 然后调用BeanDefinitionReaderUtils的registerBeanDefinition方法向IoC容器注册解析的Bean,
			// BeanDefinitionReaderUtils的注册的源码如下:
			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));
	}
}

// 12、BeanDefinitionParserDelegate 代理解析Bean定义资源文件中的<Bean>元素
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
	String id = ele.getAttribute(ID_ATTRIBUTE);
	String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

	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 {
				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;
}

// 在其parseDefaultElement方法中完成对Document对象的解析后得到封装BeanDefinition的BeanDefinitionHold对象,
// 然后调用BeanDefinitionReaderUtils的registerBeanDefinition方法向IoC容器注册解析的Bean,
// BeanDefinitionReaderUtils的注册的源码如下:
public static void registerBeanDefinition(
		BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
		throws BeanDefinitionStoreException {

	// Register bean definition under primary name.
	String beanName = definitionHolder.getBeanName();
	// 当调用BeanDefinitionReaderUtils向IoC容器注册解析的BeanDefinition时,真正完成注册功能的是DefaultListableBeanFactory。
	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);
		}
	}
}

 

依赖注入


依赖注入,即在运行期由容器将依赖关系注入到组件之中

1、依赖注入发生的时间

当Spring IoC容器完成了Bean定义资源的定位、载入和解析注册以后,IoC容器中已经管理类Bean定义的相关数据,但是此时IoC容器还没有对所管理的Bean进行依赖注入,依赖注入在以下两种情况发生:

(1).用户第一次通过getBean方法向IoC容索要Bean时,IoC容器触发依赖注入。

(2).当用户在Bean定义资源中为<Bean>元素配置了lazy-init属性,即让容器在解析注册Bean定义时进行预实例化,触发依赖注入。

2、在BeanFactory中我们看到getBean(String…)函数,它的具体实现在AbstractBeanFactory中

依赖注入实现在以下两个方法中:

(1).createBeanInstance:生成Bean所包含的java对象实例。

(2).populateBean :对Bean属性的依赖注入进行处理。

 

分析:AbstractAutowireCapableBeanFactory类的populateBean方法

// 依赖注入的具体实现
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
		// 获取Bean的依赖属性
        PropertyValues pvs = mbd.getPropertyValues();

		if (bw == null) {
			if (!pvs.isEmpty()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
			}
			else {
				// Skip property population phase for null instance.
				return;
			}
		}

		// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
		// state of the bean before properties are set. This can be used, for example,
		// to support styles of field injection.
		boolean continueWithPropertyPopulation = true;

		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
						continueWithPropertyPopulation = false;
						break;
					}
				}
			}
		}

		if (!continueWithPropertyPopulation) {
			return;
		}

		if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

			// Add property values based on autowire by name if applicable.
			if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}

			// Add property values based on autowire by type if applicable.
			if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}

			pvs = newPvs;
		}

		boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
		boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);

		if (hasInstAwareBpps || needsDepCheck) {
			PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			if (hasInstAwareBpps) {
				for (BeanPostProcessor bp : getBeanPostProcessors()) {
					if (bp instanceof InstantiationAwareBeanPostProcessor) {
						InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
						pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
						if (pvs == null) {
							return;
						}
					}
				}
			}
			if (needsDepCheck) {
				checkDependencies(beanName, mbd, filteredPds, pvs);
			}
		}
        // 对属性进行处理
		applyPropertyValues(beanName, mbd, bw, pvs);
	}

// 对属性进行处理
applyPropertyValues(beanName, mbd, bw, pvs); -- > resolveValueIfNecessary(Object argName, Object value)

private Object resolveReference(Object argName, RuntimeBeanReference ref) {
		try {
			String refName = ref.getBeanName();
			refName = String.valueOf(evaluate(refName));
			if (ref.isToParent()) {
				if (this.beanFactory.getParentBeanFactory() == null) {
					throw new BeanCreationException(
							this.beanDefinition.getResourceDescription(), this.beanName,
							"Can't resolve reference to bean '" + refName +
							"' in parent factory: no parent factory available");
				}
				return this.beanFactory.getParentBeanFactory().getBean(refName);
			}
			else {
                // 获取引用的bean,注入依赖关系前,需要对依赖进行实例化
				Object bean = this.beanFactory.getBean(refName);
                // 真正注入依赖的方法
				this.beanFactory.registerDependentBean(refName, this.beanName);
				return bean;
			}
		}
		catch (BeansException ex) {
			throw new BeanCreationException(
					this.beanDefinition.getResourceDescription(), this.beanName,
					"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
		}
	}

方法中refName是当前Bean所依赖的Bean的名字,那么beanFactory获取到的就是当前Bean所依赖的Bean的实例。也就是说在对当前Bean进行依赖注入前要先获取依赖的Bean的实例,即对当前Bean进行依赖注入之前要先实例化依赖的Bean。 
获取到依赖的实例之后,还要维护当前Bean和依赖的Bean之间的依赖关系,需要将他们之间的依赖关系保存起来。保存依赖关系的实现在registerDependentBean方法中。 

  • dependentBeanMap 存储当前bean被哪些其他bean组件依赖;departmentUserService:[userController];
  • dependenciesForBeanMap 存储bean所依赖的其他bean组件;userController:[departmentUserService];
//为指定的Bean注入依赖的Bean  
   public void registerDependentBean(String beanName, String dependentBeanName) {  
       //处理Bean名称,将别名转换为规范的Bean名称  
       String canonicalName = canonicalName(beanName);  
       //多线程同步,保证容器内数据的一致性  
       //先从容器中:bean名称-->当前bean被依赖的bean  
       synchronized (this.dependentBeanMap) {  
           //获取给定名称Bean的所有依赖Bean名称  
           Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);  
           if (dependentBeans == null) {  
               //为Bean设置依赖Bean信息  
               dependentBeans = new LinkedHashSet<String>(8);  
               this.dependentBeanMap.put(canonicalName, dependentBeans);  
           }  
           //向容器中:bean名称-->当前bean被依赖的bean  
           //即,将Bean所依赖的Bean添加到容器的集合中  
           dependentBeans.add(dependentBeanName);  
       }  
         //从容器中:bean名称-->当前bean所依赖的bean
        //Bean的依赖Bean  
       synchronized (this.dependenciesForBeanMap) {  
           Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName);  
           if (dependenciesForBean == null) {  
               dependenciesForBean = new LinkedHashSet<String>(8);  
               this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);  
           }  
           //向容器中:bean名称-->当前bean所依赖的bean 
           //即,将Bean所依赖的Bean添加到容器的集合中  
           dependenciesForBean.add(canonicalName);  
       }  
    }

Spring IoC容器的autowiring属性自动依赖注入是一个很方便的特性,可以简化开发时的配置,但是凡是都有两面性,自动属性依赖注入也有不足,首先,Bean的依赖关系在配置文件中无法很清楚地看出来,对于维护造成一定困难。其次,由于自动依赖注入是Spring容器自动执行的,容器是不会智能判断的,如果配置不当,将会带来无法预料的后果,所以自动依赖注入特性在使用时还是综合考虑。

IOC容器的作用是管理对象之间的依赖关系,通过前面的分析,我们已知道Spring通过dependentBeanMap和dependenciesForBeanMap两个集合来管理依赖关系。
具体过程是:
a.遍历从BeanDefinition中获取到的属性集合,这些属性集合也就是当前Bean所依赖的集合,遍历 实例化这些属性(getBean())。
b.dependentBeanMap :然后再根据依赖属性的名称获取引用到该依赖属性实例的Bean的名称的集合,并将当前的beanName加入到集合中。
c.dependenciesForBeanMap :最后根据当前beanName获取当前Bean的依赖属性的名称的集合,并将依赖属性的名称加入到集合中。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值