ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Spring 核心原理

2020-03-08 22:36:34  阅读:294  来源: 互联网

标签:容器 Spring wac Bean context 核心 原理 IOC


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有