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-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