其他分享
首页 > 其他分享> > Spring 核心原理

Spring 核心原理

作者:互联网

Java Bean (java bean 是由Applet Bean演变而来)  ------------>  EJB(Enterprise java beans)--------->POJO(plain ordinary java Obect,简单的java bean)

 

Spring 简化开发的四个基本策略

1、基于POJO的轻量级和最小侵入性编程

2、通过依赖注入和面向接口松耦合

3、基于切面和惯性进行声明式编程

4、通过切面和模版减少样版式代码

 

AOP ,DI,IOC之间的关系

AOP(面向切面编程) 依赖IOC ,DI(依赖注入)依赖IOC(控制权反转)

 

 

Spring 核心模块
模块名称 主要功能
spring-core 依赖注入IOC与DI的最基本实现
spring-beans Bean工厂与Bean的装配
spring-context 定义基础的Spring的Context上下文(子容器)
spring-context-support 对spring ioc 容器的扩展支持,以及IOC子容器
spring-context-indexer Spring 的类管理组建和Classpath 扫描
spring-expression Spring 表达式语言
   
   
   
   
   

 


 

 

 

 

 

 

 

 

 

 

 

 

 

Spring 实现的基本思路

1、配置阶段

配置web.xml:Dispatcher Servlet

设定init-param:contextConfigLocation = classpath:application.xml

设定url-pattern:/*

配置Annotation:@Controller  @Service  @Autowrited   @RequsetMapping

 

2、初始化阶段

调用init()方法:加载配置文件

IOC初始化容器:Map<String,Object>

扫描相关的类:scan-package="com.tealala"

IOC-->创建实例化并保存到容器:通过反射机制将类实例化放入IOC容器中

DI---->进行DI操作:扫描IOC容器中的实例,给没有赋值的属性自动赋值

MVC-->初始化HandlerMapping:将一个URL和一个Method进行一对一的关联映射Map<String,Method>

 

3、运行阶段

调用doPost()/doGet() :web容器调用doPost/doGet方法,获得request/response对象

匹配HandlerMapping:从request对象中获得用户输入的url,找到其对应的Method

反射调用method.invoker(): 利用反射调用方法并返回结果

response.getWrite().write():将结果输出到浏览器

 

 

IOC(Inversion of Control)控制反转:将代码中需要实现的对象创建、依赖的代码,反转给容器来帮忙实现。

DI(Dependency Injection)依赖注入:就是对象是被动接受依赖类而不是自己主动去找,换句话说就是指对象不是从容器中查找它依赖的类,而是在容器实例化对象的时候主动将它的依赖的类注入给它。

 

对象与对象之间的关系怎么标识:xml/properties

描述对象关系的文件存放在哪里:classpath/network/filesystem/servletContext

如何统一配置文件的标准:BeanDefinition

如何对不同的配置文件进行解析:策略模式

 

BeanFactory:负责定义容器

BeanDefinition:负责存储配置信息

BeanDefinitionReader:负责读取配置信息

 

spring IOC 容器初始化三部曲:

定位(定位配置文件和扫描相关的注解)

加载(将配置信息载入到内存中)

注册(根据载入的信息,将对象初始化到IOC容器中)

 

Spring 核心容器类图

1、BeanFactory

Spring bean 的创建是典型的工厂模式,这一系列的Bean工厂,也即IOC容器为开发者管理对象间的依赖关系提供了很多的便利和基础服务,在Spring中有许多的IOC容器的实现供用户选择和使用,相互关系如下:

其中,BeanFactory作为最顶层的一个接口类,它定义了IOC容器的基本功能规范,BeanFactory有三个重要的子类:ListableBeanFactory、HierachicalBeanFactory和AutowireCapableBeanFactory。最终的默认实现类是DefaultListableBeanFactory,它实现了所有的接口。

不同的接口都有它的使用场景,主要是为了区分在Spring内部在操作的过程中对象的传递和转换过程时,对对象的数据访问所做的限制。例如:ListableBeanFactory  接口表示这些Bean是可列表化的,而HierachicalBeanFactory表示的是这些Bean是有继承关系的,AutowireCapableBeanFactory接口定义Bean的自动装配规则。这三个接口共同定义了Bean的集合、Bean之间的关系,以及Bean的行为。

要知道工厂是如何产生对象的,我们需要看具体的IOC容器实现,Spring提供了许多的IOC容器的实现。例如:GenericApplicationContext,ClasspathXmlApplicationContext等。

 

ApplicationContext是Spring提供的一个高级的IOC容器,它除了能够提供IOC容器的基本功能外,还为用户提供了附加的服务。从ApplicationContext实现的接口可以看出具有以下几个特点:

1、支持信息源,可以实现国际化(实现了MessageSource接口)

2、访问资源(实现ResourcePatternResolver接口)

3、支持应用事件(实现ApplictionEventPublisher接口)

 

2、BeanDefinition

SpringIOC容器管理了我们定义的各种Bean对象及其相互的关系,Bean对象在Spring实现中是以BeanDefinition来描述的,其继承关系如下:

 

3、BeanDefinitionReader

Bean的解析过程非常的复杂,功能被细分,扩展较多,必须要保证有足够的灵活性,以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析。这个解析过程主要通过BeanDefinitionReader来完成。其类结构图如下:

 

WebIOC容器初体验:

DispatcherServlet中,最为重要的就是init方法,但是在DispatcherServlet中并没有找到init方法,经过追索在其父类的HttpServletBean 中找到了init()方法:如下:

@Override
	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// Set bean properties from init parameters.
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				//定位资源
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				//加载配置信息
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				initBeanWrapper(bw);
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}

		// Let subclasses do whatever initialization they like.
		initServletBean();

		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}

 在init()方法中,我们找到了初始化容器的逻辑其实就是在initServletBean()方法中,而这个方法在FrameworkServlet类中进行了实现,

@Override
	protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
		if (this.logger.isInfoEnabled()) {
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {

			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
		catch (ServletException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}
		catch (RuntimeException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}

		if (this.logger.isInfoEnabled()) {
			long elapsedTime = System.currentTimeMillis() - startTime;
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
					elapsedTime + " ms");
		}
	}

 

此中方法initWebApplicationContext()

protected WebApplicationContext initWebApplicationContext() {

		//先从ServletContext中获得父容器WebAppliationContext
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		//声明子容器
		WebApplicationContext wac = null;

		//建立父、子容器之间的关联关系
		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
					//这个方法里面调用了AbatractApplication的refresh()方法
					//模板方法,规定IOC初始化基本流程
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		//先去ServletContext中查找Web容器的引用是否存在,并创建好默认的空IOC容器
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			wac = findWebApplicationContext();
		}
		//给上一步创建好的IOC容器赋值
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}

		//触发onRefresh方法
		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			onRefresh(wac);
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
						"' as ServletContext attribute with name [" + attrName + "]");
			}
		}

		return wac;
	}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			if (this.contextId != null) {
				wac.setId(this.contextId);
			}
			else {
				// Generate default id...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
			}
		}

		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

		// The wac environment's #initPropertySources will be called in any case when the context
		// is refreshed; do it eagerly here to ensure servlet property sources are in place for
		// use in any post-processing or initialization that occurs below prior to #refresh
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
		}

		postProcessWebApplicationContext(wac);
		applyInitializers(wac);
		wac.refresh();
	}

configureAndRefreshWebApplicationContext方法中的refresh()方法是真正启动IOC容器的入口,容器初始化之后,调用了DispathcerServlet中的onrefresh()方法,onfresh()方法中有调用了initStrategies方法初始化SpringMVC的九大组件,如下:

@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	/**
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
	//初始化策略
	protected void initStrategies(ApplicationContext context) {
		//多文件上传的组件
		initMultipartResolver(context);
		//初始化本地语言环境
		initLocaleResolver(context);
		//初始化模板处理器
		initThemeResolver(context);
		//handlerMapping
		initHandlerMappings(context);
		//初始化参数适配器
		initHandlerAdapters(context);
		//初始化异常拦截器
		initHandlerExceptionResolvers(context);
		//初始化视图预处理器
		initRequestToViewNameTranslator(context);
		//初始化视图转换器
		initViewResolvers(context);
		//
		initFlashMapManager(context);
	}

 

基于XML的IOC容器初始化:

IOC容器的初始化包括BeanDefinition的Resource定位、加载和注册这三个基本的过程。现在就以ApplicationContext为例,

ApplicationContext允许上下文嵌套,通过保持父上下文可以维持一个上下体系,对于Bean的查找可以在这个上下文体系中发生,首先检查当前上下文,其次就是父上下文,逐级向上,这样为不同的Spring应用提供了一个共享的Bean定义环境。

1、寻找入口

ClassPathXmlApplicationContext,通过mian()方法启动:

ApplicationContext app = new ClassPathXmlApplicationContext("application.xml");

调用其构造方法:

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
		this(configLocations, refresh, null);
	}


public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}

类似ClassPathXmlApplicationContext的还有AnnotationApplicationContext、FileSystemXmlApplicationContext、XmlWebApplicationContext等都继承自父级容器AbstractApplicationContext,主要用到了装饰器模式和策略模式,最终调用了refresh()方法;

 

 

2、获得配置路径

在创建ClassPathXmlApplicationContext容器的时候,构造方法中主要有两项重要的工作:

1、调用父容器的构造方法super(parent)为容器设置好Bean资源加载器。

2、调用父类AbstractRefreshableConfigApplicationConext的setConfigLocations(configLocations)方法设置Bean配置信息的定位路径。通过上面的类图结构,ClassPathApplicationContext的继承体系,发现其父类的AbstractApplicationContext中初始化IOC容器所做的主要源码如下:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
//静态初始化块,在整个容器创建过程中只执行一次
	static {
		// Eagerly load the ContextClosedEvent class to avoid weird classloader issues
		// on application shutdown in WebLogic 8.1. (Reported by Dustin Woods.)
		//为了避免应用程序在Weblogic8.1关闭时出现类加载异常加载问题,加载IoC容
		//器关闭事件(ContextClosedEvent)类
		ContextClosedEvent.class.getName();
	}

/**
	 * Create a new AbstractApplicationContext with no parent.
	 */
	public AbstractApplicationContext() {
		this.resourcePatternResolver = getResourcePatternResolver();
	}

	/**
	 * Create a new AbstractApplicationContext with the given parent context.
	 * @param parent the parent context
	 */
	public AbstractApplicationContext(@Nullable ApplicationContext parent) {
		this();
		setParent(parent);
	}

//获取一个Spring Source的加载器用于读入Spring Bean定义资源文件
	protected ResourcePatternResolver getResourcePatternResolver() {
		//AbstractApplicationContext继承DefaultResourceLoader,因此也是一个资源加载器
		//Spring资源加载器,其getResource(String location)方法用于载入资源
		return new PathMatchingResourcePatternResolver(this);
	}


@Override
	public void setParent(@Nullable ApplicationContext parent) {
		this.parent = parent;
		if (parent != null) {
			Environment parentEnvironment = parent.getEnvironment();
			if (parentEnvironment instanceof ConfigurableEnvironment) {
				getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
			}
		}
	}



}

AbstractApplicationContext的默认构造方法中调用PathMatchingResourcePatternResolver的构造方法创建Spring资源加载器:

public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
		Assert.notNull(resourceLoader, "ResourceLoader must not be null");
		//设置Spring的资源加载器
		this.resourceLoader = resourceLoader;
	}

在设置完容器的资源加载器之后,接下来ClassPathXmlApplicationContext执行setConfigLocations()方法通过调用其父类AbstractRefreshableConfigApplicationContext的方法进行对Bean配置信息的定位:

//解析Bean定义资源文件的路径,处理多个资源文件字符串数组
	public void setConfigLocations(@Nullable String... locations) {
		if (locations != null) {
			Assert.noNullElements(locations, "Config locations must not be null");
			this.configLocations = new String[locations.length];
			for (int i = 0; i < locations.length; i++) {
				// resolvePath为同一个类中将字符串解析为路径的方法
				this.configLocations[i] = resolvePath(locations[i]).trim();
			}
		}
		else {
			this.configLocations = null;
		}
	}

通过方法可变形参列表可以看出,我们既可以使用一个字符串来配置多个Spring Bean 的配置信息,也可以使用字符串数组,多个资源文件路径之间可以使用",;\t\n"等分离。

ClassPathResource res = new ClassPathResource(new String[]{"aa.xml","bbb.xml"});

至此,SpringIOC容器在初始化时将配置的Bean配置信息定位为Spring封装到了Resource

 

3、开始启动

SpringIOC容器对Bean配置资源的载入是从refresh()函数开始,refresh()是一个模版方法,规定了IOC容器启动的流程,有些逻辑交给了子类去实现,它对Bean配置资源进行载入ClassPathXmlApplicationContext通过调用其父类AbstractApplicationContext的refresh()方法启动整个IOC容器对Bean定义的载入过程,逻辑处理如下:

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			//1、调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			//2、告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从
			//子类的refreshBeanFactory()方法启动
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			//3、为BeanFactory配置容器特性,例如类加载器、事件处理器等
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				//4、为容器的某些子类指定特殊的BeanPost事件处理器
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				//5、调用所有注册的BeanFactoryPostProcessor的Bean
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				//6、为BeanFactory注册BeanPost事件处理器.
				//BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				//7、初始化信息源,和国际化相关.
				initMessageSource();

				// Initialize event multicaster for this context.
				//8、初始化容器事件传播器.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				//9、调用子类的某些特殊Bean初始化方法
				onRefresh();

				// Check for listener beans and register them.
				//10、为事件传播器注册事件监听器.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				//11、初始化所有剩余的单例Bean
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				//12、初始化容器的生命周期事件处理器,并发布容器的生命周期事件
				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.
				//13、销毁已创建的Bean
				destroyBeans();

				// Reset 'active' flag.
				//14、取消refresh操作,重置容器的同步标识。
				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...
				//15、重设公共缓存
				resetCommonCaches();
			}
		}
	}

refresh()方法主要为IOC容器Bean的生命周期管理提供条件,SpringIOC容器载入Bean配置信息从其子类容器的refreshBeanFactory()方法启动,所以整个refresh()中

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

这句话以后代码都是注册容器的信息源和生命周期事件。

refresh()方法的主要的作用是:在 创建IOC容器之间,如果已经有容器存在了,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是建立起来的IOC容器。它类似于对IOC容器的重启,在新建立好的容器中对容器进行初始化,对Bean配置资源进行载入。

 

4、创建容器

obtainFreshBeanFactory()方法中调用了子容器的refreshBeanFactory()方法,启动容器载入Bean配置信息的过程如下:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		//这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,具体实现调用子类容器的refreshBeanFactory()方法
		refreshBeanFactory();
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}

AbstractApplicationContext类中只抽象的定义了refreshBeanFactory()方法,容器真正调用的是其子类AbstractRefreshableApplicationContext实现的refreshBeanFactory()方法,如下:

@Override
	protected final void refreshBeanFactory() throws BeansException {
		//如果已经有容器,销毁容器中的bean,关闭容器
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			//创建IOC容器
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			//对IOC容器进行定制化,如设置启动参数,开启注解的自动装配等
			customizeBeanFactory(beanFactory);
			//调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

该方法中先判断BeanFactory是否存在,如果存在容器销毁容器中的bean,关闭容器,然后创建DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)装载bean定义。

 

5、载入配置路径

AbstractRefreshableApplicatioonContext中只定义了抽象的loadBeanDefinitions方法,容器真正调用的是其子类AbstractXmlApplicationContext对该方法的实现,AbstractXmlApplicationContext的主要源码如下:

loadBeanDefinition依然是抽象的方法,需要子类进行实现,

public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {

	private boolean validating = true;


@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		//创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容器使用该读取器读取Bean定义资源
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		//为Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext的
		//祖先父类AbstractApplicationContext继承DefaultResourceLoader,因此,容器本身也是一个资源加载器
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		//为Bean读取器设置SAX xml解析器
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		//当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制
		initBeanDefinitionReader(beanDefinitionReader);
		//Bean读取器真正实现加载的方法
		loadBeanDefinitions(beanDefinitionReader);
	}


protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
		reader.setValidating(this.validating);
	}


//Xml Bean读取器加载Bean定义资源
	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		//获取Bean定义资源的定位
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			//Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位
			//的Bean定义资源
			reader.loadBeanDefinitions(configResources);
		}
		//如果子类中获取的Bean定义资源定位为空,则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			//Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位
			//的Bean定义资源
			reader.loadBeanDefinitions(configLocations);
		}
	}


//这里又使用了一个委托模式,调用子类的获取Bean定义资源定位的方法
	//该方法在ClassPathXmlApplicationContext中进行实现,对于我们
	//举例分析源码的FileSystemXmlApplicationContext没有使用该方法
	@Nullable
	protected Resource[] getConfigResources() {
		return null;
	}



}

以XmlBean读取器的其中一种策略XmlBeanDefinitionReader为例。XmlBeanDefinitionReader调用父类AbstractBeanDefinitionReader的reader.loadBeanDefinitions()方法读取Bean配置资源。由于我们使用ClassPathXmlApplicationContext作为例子进行分析,因此getConfigResources的返回值为null,因此程序执行reader.loadBeanDefinitions(configLocations)分支。

 

6、分配路径处理策略

在XmlBeanDefinitionReader的抽象父类AbstractBeanDefinitionReader中定义载入过程。

AbstractBeanDefinitionReader的loadBeanDefinitions()方法如下:

//重载方法,调用下面的loadBeanDefinitions(String, Set<Resource>);方法
	@Override
	public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(location, null);
	}

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
		//获取在IoC容器初始化过程中设置的资源加载器
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
				//将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源
				//加载多个指定位置的Bean定义资源文件
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				//委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
				int loadCount = loadBeanDefinitions(resources);
				if (actualResources != null) {
					for (Resource resource : resources) {
						actualResources.add(resource);
					}
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
				}
				return loadCount;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			// Can only load single resources by absolute URL.
			//将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源
			//加载单个指定位置的Bean定义资源文件
			Resource resource = resourceLoader.getResource(location);
			//委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
			int loadCount = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
			}
			return loadCount;
		}
	}

 

 

Spring ioc 运行时时序图

Annotation IOC 初始化全过程

定位Bean扫描路径------------>读取元数据------------>解析------------->注册bean

 

 

 

 

 

 

标签:容器,Spring,wac,Bean,context,核心,原理,IOC
来源: https://blog.csdn.net/tealala/article/details/103660965