首页 > 数据库 > 数据库开发技术 > Spring之IoC容器

前言
Spring’s Inversion of Control (IoC) container 简称IoC容器,也称为dependency injection (DI) 依赖注入,是Spring最核心的关键技术之一。
一、对Spring Ioc的理解
Spring IoC是对Bean的全生命周期管理的容器,包括bean创建,依赖注入,销毁。他是Spring技术生态的底座,所有的Spring技术在Ioc容器之上进行扩展实现。
beans和context包是ioc容器的基础,BeanFactory支持配置任意类型的对象。ApplicationContext是BeanFactory的子接口,它更易于集成aop,支持国际化,事件发布,以及应用层特定的上下文,如用于web应用程序的WebApplicationContext。
二、Spring IoC需要提供的功能
1、 获取不同来源bean的资源
2、 加载读取bean资源
3、 解析资源bean的定义信息
4、 根据bean的定义信息进行实例化,并存储
三、Spring IoC启动流程
在AbstractApplicationContext类中,Spring使用了模版模式来将Spring Ioc容器初始化流程进行了定义串连。
public abstract class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext {// ...public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}}// ...}
详细流程说明:
首先是资源定位:ioc容器需要统一创建对象,创建对象的类信息,属性信息需要从不同配置文件中解析出来,所以需要查找类的配置文件。这就是资源定位。
spring设计了如下接口和类用来定位资源路径,通过一个字符串路径获取资源加载器。字符串路径可以是类路径下的文件,网络Url,文件系统等等。其中PathMatchingResourcePatternResolver通过持有ResourceLoader的引用,可以根据不同的资源加载器加载不同位置的资源。

//资源加载处理器 **此处使用了策略模式** 持有了ResourceResolver的引用,根据不同类型的ResourceResolver来实现加载不同位置的资源public class PathMatchingResourcePatternResolver implements ResourcePatternResolver {//通过构造函数持有ResourceLoader引用public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {Assert.notNull(resourceLoader, "ResourceLoader must not be null");this.resourceLoader = resourceLoader;}//通过不同的ResourceLoader来加载资源public Resource[] getResources(String locationPattern) throws IOException {Assert.notNull(locationPattern, "Location pattern must not be null");if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {// a class path resource (multiple resources for same name possible)if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {// a class path resource patternreturn findPathMatchingResources(locationPattern);}else {// all class path resources with the given namereturn findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));}}else {// Generally only look for a pattern after a prefix here,// and on Tomcat only after the "*/" separator for its "war:" protocol.int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :locationPattern.indexOf(':') + 1);if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {// a file patternreturn findPathMatchingResources(locationPattern);}else {// a single resource with the given namereturn new Resource[] {getResourceLoader().getResource(locationPattern)};}}}}
不同资源类继承图

第2点是资源加载,读取到了资源文件,需要将资源文件转换成BeanDefinition,
1、先获取类信息资源字节流对象
2、加载资源字节流对象成org.w3c.dom.Document对象,在Spring中通过jdk的SAX的xml解析技术,类图如下

3、在DefaultBeanDefinitionDocumentReader类中,读取Document对象里所有xml节点,委派给BeanDefinitionParserDelegate类进行解析,使用了委派设计模式
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {protected void doRegisterBeanDefinitions(Element root) {BeanDefinitionParserDelegate parent = this.delegate;this.delegate = createDelegate(getReaderContext(), root, parent);// ....//解析前处理preProcessXml(root);//解析bean定义parseBeanDefinitions(root, this.delegate);//解析后处理postProcessXml(root);//解析this.delegate = parent;}}
4、parseBeanDefinitions()方法进行具体解析
先解析bean标签属性,如name,id,class,参数等等,然后构造GenericBeanDefinition对象,并填充属性,这就是bean实例化的原始配方。解析主要有两种情况:
第一种是spring默认支持的标签。
//构造GenericBeanDefinition定义public static AbstractBeanDefinition createBeanDefinition(String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {GenericBeanDefinition bd = new GenericBeanDefinition();bd.setParentName(parentName);if (className != null) {if (classLoader != null) {bd.setBeanClass(ClassUtils.forName(className, classLoader));}else {bd.setBeanClassName(className);}}return bd;}
第二种是应用程序自定义的标签
对于应用程序自定义的标签,Spring支持灵活定制,方便集成其他组件到spring中。用户可以将自己的解析器注册器放到META-INF/spring.handlers文件中,同时用户还需要自定义xsd文件和META-INF/spring.schemas文件,在tx模块和dubbo中都用到了该扩展点。在加载自定义标签的时候,spring将会加载到具体解析器。具体解析过程中,通过org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement()方法进行管理解析流程。委派给NamespaceHandlerSupport进行解析,NamespaceHandlerSupport类持有了所有的解析器,由它委派给具体的解析器进行解析。
下面是自定义标签解析器相关类图。

解析标签流程图

5、第5步,将生成的GenericBeanDefinition对象,注册到容器中
public abstract class BeanDefinitionReaderUtils {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 alias : aliases) {registry.registerAlias(beanName, alias);}}}}
注册beandefinition具体过程包括,校验,判断是否可以覆盖,保存。
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {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 {((AbstractBeanDefinition) beanDefinition).validate();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);}}BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);if (existingDefinition != null) {if (!isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);}else if (existingDefinition.getRole() < beanDefinition.getRole()) {// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTUREif (logger.isInfoEnabled()) {logger.info("Overriding user-defined bean definition for bean '" + beanName +"' with a framework-generated bean definition: replacing [" +existingDefinition + "] with [" + beanDefinition + "]");}}else if (!beanDefinition.equals(existingDefinition)) {if (logger.isDebugEnabled()) {logger.debug("Overriding bean definition for bean '" + beanName +"' with a different definition: replacing [" + existingDefinition +"] with [" + beanDefinition + "]");}}else {if (logger.isTraceEnabled()) {logger.trace("Overriding bean definition for bean '" + beanName +"' with an equivalent definition: replacing [" + existingDefinition +"] with [" + beanDefinition + "]");}}this.beanDefinitionMap.put(beanName, beanDefinition);}else {if (hasBeanCreationStarted()) {// Cannot modify startup-time collection elements anymore (for stable iteration)synchronized (this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;removeManualSingletonName(beanName);}}else {// Still in startup registration phasethis.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);removeManualSingletonName(beanName);}this.frozenBeanDefinitionNames = null;}if (existingDefinition != null || containsSingleton(beanName)) {resetBeanDefinition(beanName);}}}
6、看的上面,beanDefinition就解析好了。
接下来spring初始化了一系列bean,这些bean比较特殊,他是Spring的bean工厂级别的,比如Environment,ClassLoader, post-processors
public abstract class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext {/*** Configure the factory's standard context characteristics,* such as the context's ClassLoader and post-processors.* @param beanFactory the BeanFactory to configure*/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(beanFactory.getBeanClassLoader()));beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));// Configure the bean factory with context callbacks.//这个地方是注册容器本身的ApplicationContextAwareProcessor,他的作用是为实现了**Aware接口的bean进行回调赋值。beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));beanFactory.ignoreDependencyInterface(EnvironmentAware.class);beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);beanFactory.ignoreDependencyInterface(MessageSourceAware.class);beanFactory.ignoreDependencyInterface(ApplicationContextAware.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);// Register early post-processor for detecting inner beans as ApplicationListeners. 检测实现了ApplicationListener接口的bean,并存储下来beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));// Detect a LoadTimeWeaver and prepare for weaving, if found.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());}}}
7、上一步初始化了内置的bean,接下来执行postProcessBeanFactory(beanFactory);方法方便不同容器对BeanFactory进行定制。
8、调用invokeBeanFactoryPostProcessors(beanFactory);执行所有的BeanFactoryPostProcessor。对beandefinition进行修改,这个是spring提供的扩展点,用来自定义修改beandefinition的。比如mybatis(将mapper的class统一修改成MapperFactoryBean,将mapper的class作为它的构造参数),dubbo,都使用这个技术来和spring进行集成。
9、实例化并注册所有bean的后置处理器,用于拦截bean创建。Register bean processors that intercep registerBeanPostProcessors(beanFactory);
10、initMessageSource(); 国际化支持
11、initApplicationEventMulticaster(); 初始化事件多播器
12、onRefresh(); 子类使用,初始化其他资源
13、registerListeners(); 注册事件监听器
在spring容器中,提供了一种Event机制,实现Spring容器里的Bean实例之间进行通信。
14、finishBeanFactoryInitialization(beanFactory); 初始化bean,并完成依赖注入
15、finishRefresh();完成容器初始化函数
16、最后回收资源,清除缓存,结束初始化
总结
Spring IoC容器启动流程是非常复杂的,里面不仅涉及到解析内置的bean资源,而且涉及到解析第三方框架定制化的bean资源,但是Spring统一了第三方对接姿势,这是非常巧妙的设计之一,因为这个扩展点让Spring被称为万能胶和粘合剂,他可以让其他框架无缝集成。
原文作者:服务端技术栈;如有侵权,请联系管理员删除。