Spring IoC 和DI
IoC和DI通俗地说就是避免new关键字,解耦和。 但是需要一种描述让容器知道要创建的对象与对象的关系,即配置(文件和注解等)。
问题
1.对象与对象的关系表示
可以通过XML、properties等语义化配置文件表示
2.描述对象关系的文件存放地址
classpath,filesystem,url,servletContext
3.不同配置文件对对象描述不一样,如标准,自定义声明,需要统一
在框架内部需要有一个统一的关于对象的定义,所有外部的描述都必须转换成统一的描述定义
4.对不同的配置文件进行解析?
不同配置文件语法不一样,需要有不同的解析器
总结一下:即该如何收集,管理不同对象之间的对应关系
Spring核心容器类图
在spring—beans模块下的org.springframework.beans包中
BeanFactory
其中XmlBeanFactory已经不推荐使用。
BeanFactory的子接口共有3个ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。其最终默认实现类都为DefaultListableBeanFactory。
众多子接口的作用
每个接口都有其使用场合,为了区分spring内部操作过程中对象的传递和转化,对对象的数据访问所作的限制。
- ListableBeanFactory:Bean可列表化
- HierarchicalBeanFactory:Bean有继承关系,每个Bean可能有父Bean
- AutowireCapableBeanFactory:定义Bean的自动装配规则
三个接口共同定义Bean的集合,它们之间的关系及行为。
BeanFactory接口源码
public interface BeanFactory {
/**
* @Author jtao
* @Date 2020/12/6 18:47
* @Description BeanFactory只对IoC容器的基本行为做了定义
*/
//对FactoryBean的转义定义,如果使用Bean的名字检索FactoryBean得到的是工程生产的Bean对象
//如果需要得到工厂本身需要转义字符
String FACTORY_BEAN_PREFIX = "&";
//根据Bean名字得到Ioc中的Bean实例
Object getBean(String name) throws BeansException;
//根据Bean名字和Class类型来得到Bean实例,增加了类型安全验证机制
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
//提供对Bean的检索,看看在IoC容器中是否有这个名字的Bean
boolean containsBean(String name);
//根据Bean名字得到Bean实例,同时判断这个Bean是不是单例,原型,类型是否匹配
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
//得到Bean实例的Class类型
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
//得到Bean的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name);
}
ApplicationContext
ApplicationContext是一个更高级的IoC容器,其与BeanFactory关系如下图
除了继承BeanFactory接口基本功能外其还定义了以下功能
1.支持信息源,可以实现国际化 MessageSource
2.访问资源 ResourcePatternResolver
3.支持应用事件 ApplicationEventPublisher
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
/**
* Return the unique id of this application context.
* @return the unique id of the context, or {@code null} if none
*/
@Nullable
String getId();
/**
* Return a name for the deployed application that this context belongs to.
* @return a name for the deployed application, or the empty String by default
*/
String getApplicationName();
/**
* Return a friendly name for this context.
* @return a display name for this context (never {@code null})
*/
String getDisplayName();
/**
* Return the timestamp when this context was first loaded.
* @return the timestamp (ms) when this context was first loaded
*/
long getStartupDate();
/**
* Return the parent context, or {@code null} if there is no parent
* and this is the root of the context hierarchy.
* @return the parent context, or {@code null} if there is no parent
*/
@Nullable
ApplicationContext getParent();
/**
* Expose AutowireCapableBeanFactory functionality for this context.
* <p>This is not typically used by application code, except for the purpose of
* initializing bean instances that live outside of the application context,
* applying the Spring bean lifecycle (fully or partly) to them.
* <p>Alternatively, the internal BeanFactory exposed by the
* {@link ConfigurableApplicationContext} interface offers access to the
* {@link AutowireCapableBeanFactory} interface too. The present method mainly
* serves as a convenient, specific facility on the ApplicationContext interface.
* <p><b>NOTE: As of 4.2, this method will consistently throw IllegalStateException
* after the application context has been closed.</b> In current Spring Framework
* versions, only refreshable application contexts behave that way; as of 4.2,
* all application context implementations will be required to comply.
* @return the AutowireCapableBeanFactory for this context
* @throws IllegalStateException if the context does not support the
* {@link AutowireCapableBeanFactory} interface, or does not hold an
* autowire-capable bean factory yet (e.g. if {@code refresh()} has
* never been called), or if the context has been closed already
* @see ConfigurableApplicationContext#refresh()
* @see ConfigurableApplicationContext#getBeanFactory()
*/
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
BeanDefinition
Bean对象在Spring实现中是以BeanDefinition来描述定义
BeanDefinitionReader
Bean的解析主要为对Spring配置文件进行解析。主要通过BeanDefinitionReader完成
Web Ioc容器初体验
在spring-webmvc模块中的org.springframework.web.servlet包中
DispatcherServlet的init()初始化方法在其父类HttpServletBean中
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
/**
* Logger available to subclasses
*/
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
private ConfigurableEnvironment environment;
private final Set<String> requiredProperties = new HashSet<>(4);
/**
* @Author jtao
* @Date 2020/12/6 22:10
* @Description 将配置参数映射到该servlet的bean属性上,并调用子类初始化。
* 如果bean属性无效(或缺少必需的属性),或者子类初始化失败,则抛出ServletException。
*/
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// 注入bean属性
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;
}
}
//具体的初始化步骤实现方法由其子类具体实现 例如FrameworkServlet.initServletBean()方法
initServletBean(); // 该类并未实现该方法。protected void initServletBean() throws ServletException { }
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
其中initServletBean(); 是关键。HttpServletBean并没有实现该方法,而是由其子类实现。
protected void initServletBean() throws ServletException {
}
FrameworkServlet重写了该方法
/**
* @Author jtao
* @Date 2020/12/6 22:16
* @Description {@link HttpServletBean}的重写方法,在设置任何bean属性之后调用。创建此Servlet的WebApplicationContext。
*/
@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 {
//具体的初始化方法 initWebApplicationContext
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");
}
}
其中分别调用的方法如下
/**
* @Author jtao
* @Date 2020/12/6 22:18
* @Description *初始化并发布此Servlet的WebApplicationContext。 * <p>代表{@link #createWebApplicationContext}用于实际创建上下文。可以在子类中覆盖。
* * @返回WebApplicationContext实例* @请参阅#FrameworkServlet(WebApplicationContext)* @请参阅#setContextClass * @请参阅#setContextConfigLocation
*/
protected WebApplicationContext initWebApplicationContext() {
//从ServletContext中获得父容器WebApplicationContext
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
//声明子容器 wac
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);
}
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);
}
//private boolean refreshEventReceived = false;
//true ,进行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);
}
//private boolean publishContext = true;
//true ,在getServletContext容器中添加
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;
}
/**
* @Author jtao
* @Date 2020/12/6 22:38
* @Description 使用{@link #setContextAttribute配置名称}从{@code ServletContext} *属性中检索{@code WebApplicationContext}。
* 在初始化(或调用)此Servlet之前,必须已经将{{code WebApplicationContext}加载并存储在{{code ServletContext}中。
* 子类可以重写此方法以提供不同的* {@code WebApplicationContext}检索策略。 * * @返回此servlet的WebApplicationContext,
* 如果找不到,则返回{@code null} * @see #getContextAttribute()
*/
@Nullable
protected WebApplicationContext findWebApplicationContext() {
String attrName = getContextAttribute();
if (attrName == null) {
return null;
}
WebApplicationContext wac =
WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
}
return wac;
}
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
return createWebApplicationContext((ApplicationContext) parent);
}
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
// public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
}
//public boolean isAssignableFrom(类<?> cls)确定由此类对象表示的类或接口是否与由指定的Class 类表示的类或接口相同或是超类或类接口。
//如果是,则返回true ; 否则返回false 。 如果此类对象表示基本类型,则如果指定的类参数正好是类对象,则此方法返回true ; 否则返回false 。
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
configureAndRefreshWebApplicationContext(wac);
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...
//String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":";
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);
//最终调用ConfigurableWebApplicationContext.refresh()方法
wac.refresh();
}
最终调用的是ConfigurableWebApplicationContext.refresh()方法,该方法继承自ConfigurableApplicationContext接口
/**
* @Author jtao
* @Date 2020/12/6 23:46
* @Description 加载或刷新配置的持久表示形式,可能是XML文件,属性文件或关系数据库架构。 <p>
* 由于这是一种启动方法,因此,如果失败,它应该销毁已创建的单例,以避免悬挂资源。换句话说,在调用该方法之后,应实例化所有单例或根本不实例化。
* @throws BeansException如果无法初始化bean工厂
* @throws IllegalStateException如果已经初始化并且不支持多次刷新尝试
*/
void refresh() throws BeansException, IllegalStateException;
接着是onRefresh方法,该方法在FrameworkServlet类是默认不执行的,但其子类DispatcherServlet重写了该方法
/**
* @Author jtao
* @Date 2020/12/6 23:52
* @Description 对于子类默认情况下不执行操作
*/
protected void onRefresh(ApplicationContext context) {
// For subclasses: do nothing by default.
}
/**
* This implementation calls {@link #initStrategies}.
*/
@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.
*/
/**
* @Author jtao
* @Date 2020/12/6 23:59
* @Description 初始化此servlet使用的策略对象。 可以在子类中重写,以初始化其他策略对象。
* SpringMVC的9大组件初始化
*
*/
protected void initStrategies(ApplicationContext context) {
//多文件上传组件
initMultipartResolver(context);
//初始化本地语言环境
initLocaleResolver(context);
//初始化模板处理器
initThemeResolver(context);
//初始化handerMapping
initHandlerMappings(context);
//初始化参数适配器
initHandlerAdapters(context);
//初始化异常拦截器
initHandlerExceptionResolvers(context);
//初始化视图预处理器
initRequestToViewNameTranslator(context);
//初始化视图转换器
initViewResolvers(context);
//初始化FlashMap管理器
initFlashMapManager(context);
}
总结
Web IoC容器初始化通过DispatcherServlet的父类HttpServletBean的 public final void init()方法初始化,该方法内部的initServletBean()负责具体的初始化方法,并且具体的初始化步骤实现方法由其子类具体实现,HttpServletBean并没有方法实现。FrameworkServlet重写了initServletBean()方法,在该方法内部调用initWebApplicationContext()继续调用onRefresh(),由其子类DispatcherServlet重写加载SpringMVC的九大组件,至此完毕!
基于XML的IoC容器初始化
IoC容器的初始化包括BeanDefinition的Resource定位、加载和注册!
以ApplicationContext为例。举例最常用的实现类XmlWebApplicationContext
private static final String PATH = "/org/springframework/context/support/";
private static final String FQ_SIMPLE_CONTEXT = PATH + "simpleContext.xml";
@Test
public void testSingleConfigLocation() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(FQ_SIMPLE_CONTEXT);
assertTrue(ctx.containsBean("someMessageSource"));
ctx.close();
}
/**
* *创建一个新的ClassPathXmlApplicationContext,从给定的XML文件中加载定义*并自动刷新上下文。
* * @param configLocation资源位置
* *如果上下文创建失败,则@throws BeansException */
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
/**
* @Author jtao
* @Date 2020/12/7 20:27
* @Description 使用给定的父级创建一个新的ClassPathXmlApplicationContext,从给定的XML文件中加载定义。
* @param configLocations资源位置数组
* @param refresh是否自动刷新上下文,加载所有bean定义并创建所有单例。或者,在进一步配置上下文之后,手动调用刷新。
* @param parent父上下文@throws BeansException如果上下文创建失败
* @see #refresh()
*/
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
其中super(parent)一直往上调用到AbstractApplicationContext的AbstractApplicationContext方法。refresh()也是调用父类AbstractApplicationContext的方法
static {
// Eagerly load the ContextClosedEvent class to avoid weird classloader issues
// on application shutdown in WebLogic 8.1. (Reported by Dustin Woods.)
//静态初始化代码块,在整个容器创建过程中只执行一次,避免应用程序在Weblogic 8.1关闭时出现类加载异常更为题
//加载IoC容器关闭事件类
ContextClosedEvent.class.getName();
}
/**
* Create a new AbstractApplicationContext with the given parent context.
* @param parent the parent context
*/
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
setParent(parent);
}
/**
* @Author jtao
* @Date 2020/12/7 20:39
* @Description 创建一个没有父级的新AbstractApplicationContext。
*/
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
/**
* @Author jtao
* @Date 2020/12/7 20:40
* @Description 设置此应用程序上下文的父级。 *
* 父{@linkplain ApplicationContext#getEnvironment()环境}是
* {@linkplain ConfigurableEnvironment#merge(ConfigurableEnvironment)合并}与
* (父)非{@code null}且此(子)应用上下文环境*它的环境是{@link ConfigurableEnvironment}的实例。
* @请参阅ConfigurableEnvironment#merge(ConfigurableEnvironment)
*/
@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类中的public AbstractApplicationContext构造方法中的this()调用了同类中的ResourcePatternResolver方法加载资源,如下图所示
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
setConfigLocations(configLocations);方法实现如下
//处理单个文件路径为一个字符串的情况
public void setConfigLocation(String location) {
//String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
//即多个资源文件路径之间用",; \t\n"分割,解析成数组
setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
}
/**
* Set the config locations for this application context.
* <p>If not set, the implementation may use a default as appropriate.
*/
//解析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;
}
}
最终调用AbstractApplicationContext.refresh();方法刷新。
@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.为容器的某些子类指定特殊的post事件处理器
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注册Post事件处理器
//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.取消刷新操作,重置容器的同步标识
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的生命周期管理提供条件,Spring IoC容器载入Bean配置信息从其子类容器的refreshBeanFactory()方法启动,所以整个refresh()方法中
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
以后的代码都是在注册容器的信息源和生命周期事件,载入就通过这句代码启动。
refresh()方法的主要作用是:在创建IoC容器前,如果已经有容器存在,需要把已有的容器销毁和关闭,以保证在refresh()方法之后使用的是新创建的IoC容器。它类似于对IoC容器的重启,在新创建的容器中对容器进行初始化,对Bean配置资源进行载入。
创建容器
//2.告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从子类的refreshBeanFactory()方法启动
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/**
* @Author jtao
* @Date 2020/12/8 18:01
* @Description 让子类刷新bean工厂
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
AbstractApplicationContext抽象类的抽象方法refreshBeanFactory由其子类实现如下图所示
AbstractRefreshableApplicationContext的refreshBeanFactory方法如下。其中还实现了序列化beanfactory
/**
* @Author jtao
* @Date 2020/12/8 18:09
* @Description 此实现对此上下文的基础bean工厂执行实际的刷新,关闭先前的bean工厂(如果有),并为上下文生命周期的下一阶段初始化一个新的bean工厂。
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
//检查是否有容器,如果有则销毁容器中的bean,关闭容器
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建IoC容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
//指定一个ID以进行序列化,如果需要的话,允许将该BeanFactory从该ID反序列化回BeanFactory对象。
beanFactory.setSerializationId(getId());
//对IoC容器进行定制化,设置启动参数、开启注解的自动装配等
customizeBeanFactory(beanFactory);
//调用载入Bean定义的方法,抽象方法,未实现,由其子类实现,委派模式
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
上述refreshBeanFactory代码先判断beanFactory是否存在,如果存在则先销毁Bean并关闭beanFactory,接着创建DefaultListableBeanFactory,并调用loadBeanDefinitions()方法装载Bean定义。loadBeanDefinitions(beanFactory)由AbstractXmlApplicationContext类实现
/**
* @Author jtao
* @Date 2020/12/8 23:09
* @Description 通过XmlBeanDefinitionReader加载bean定义
*/
@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);
}
/**
* @Author jtao
* @Date 2020/12/8 23:12
* @Description 初始化用于加载此上下文的bean *定义的bean定义读取器。默认实现为空。
* 可以在子类中覆盖,例如用于关闭XML验证*或使用其他XmlBeanDefinitionParser实现。
*/
protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
reader.setValidating(this.validating);
}
/**
* @Author jtao
* @Date 2020/12/8 23:14
* @Description 使用给定的XmlBeanDefinitionReader加载bean定义。
* bean工厂的生命周期由{@link #refreshBeanFactory}
* 方法处理;因此,该方法仅用于加载和/或注册Bean定义。
* xml Bean读取器加载Bean配置资源
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//在该类中protected Resource[] getConfigResources() {return null; }为null
//可以在子类中重写该方法
Resource[] configResources = getConfigResources();
if (configResources != null) {
//xml Bean读取器调用XmlBeanDefinitionReader父类AbstractBeanDefinitionReader读取定位的Bean配置资源
reader.loadBeanDefinitions(configResources);
}
//
//如果子类中获取的Bean配置资源定位为空,
//则获取ClasspathXmlApplicationContext构造方法中setConfigLocation方法设置的资源
String[] configLocations = getConfigLocations();
if (configLocations != null) {
//xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位的Bean配置资源
reader.loadBeanDefinitions(configLocations);
}
}
/**
* @Author jtao
* @Date 2020/12/8 23:41
* @Description 返回一个资源对象数组,引用此上下文应使用的XML bean定义文件。
* 默认实现返回{@code null}。子类可以重写
* 以提供预构建的Resource对象,而不是位置字符串。
*/
//这里又使用了一个委派模式,调用子类的获取Bean配置资源定位的方法
//该方法在ClassPathXmlApplicationContext中实现
@Nullable
protected Resource[] getConfigResources() {
return null;
}
以ClassPathXmlApplicationContext为例,getConfigResource()方法的返回值为null,因此程序执行read.loadBeanDefinitions(configLocations)分支。
分配路径处理策略
Comments | 0 条评论