一,Spring启动流程概述
Spring的IoC容器在实现控制反转和依赖注入的过程中,可以划分为两个阶段:
-
容器启动阶段
-
Bean实例化阶段
容器初始化
-
加载配置
-
分析配置信息
-
将Bean信息装配到BeanDefinition
-
将Bean信息注册到相应的BeanDefinitionRegistry
-
其他后续处理
容器实例化
-
根据策略实例化对象
-
装配依赖
-
Bean初始化前处理
-
对象初始化
-
对象其他处理
-
注册回调接口
二,Spring启动流程详解
BeanFactory后处理
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// ServletContextAwareProcessor中拿到应用上下文持有的servletContext引用和servletConfig引用
// 添加ServletContextAwareProcessor后处理器
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
// 在自动注入时忽略指定的依赖接口
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
// 向WebApplicationContext使用的BeanFactory注册Web相关作用域对象
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
// 注册和Environment有关的beans
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}
WebApplicationContextUtils
该类位于包`org.springframework.web.context.support`是一个使检索指定ServletContext的根WebApplicationContext的便捷工具类。它如下工具方法:
-
在Web容器启动过程中注册Web相关作用域Bean(request/session/globalSession/application)
-
在Web容器启动过程中注册相应类型的工厂Bean ,依赖注入的Bean时能访问到正确的对象(ServletRequest/ServletResponse/HttpSession/WebRequest)
-
在Web容器启动过程中注册Web相关环境Bean
-
在Web容器启动过程中初始化Servlet propertySources
-
在客户化Web视图或者MVC action中,使用该工具类可以很方便地在程序中访问Spring应用上下文(application context)。
自定义作用域
Scope也称作用域,在Soring Ioc容器指其创建的Bean对象对其他Bean对象的请求可见范围,是用来声明IoC容器中的对象应该处的限定场景或者说该对象的存活空间,即在IOC容器在对象进入相应的Scope之前,生成并装配这些对象,在该对象不再处于这些scope的限定之后,容器通常会销毁这些对象。
诸如WebApplicationContext之类的ApplicationContext可以注册相对于标准作用域(Singleton/Prototype)的特定环境Scope作用域,例如:“request”和“session”作用域。
Bean的Scope作用域范围同样是可扩展的:即可以定义自己的范围,甚至重新定义现有范围,但不能覆盖内置的Singleton和Prototype范围。
// 从scope中根据名字返回对象
Object get(String name, ObjectFactory objectFactory);
// 从Scope中删除该对象
Object remove(String name);
// 注册的Scope被销毁或Scope中的指定对象被销毁时执行回调
void registerDestructionCallback(String name, Runnable destructionCallback);
// 获取Scope的会话标识符
String getConversationId();
ConfigurableBeanFactory接口中允许使用自定义的作用域范围扩展BeanFactory的标准作用域“ Singleton”和“ Prototype”,并为新的作用域范围进行注册。在编写好自定义Scope之后,需要让Spring容器识别新的Scope作用域。
代码方式注册
以下方法是在Spring容器中注册新Scope的主要方法,该方法在ConfigurableBeanFactory接口中定义:
// 第一个参数是与该Scope的全局唯一名称。在Spring容器中,此名称是Singleton和Prototype
// 第二个参数是希望注册和使用的自定义Scope实例
void registerScope(String scopeName, Scope scope);
如果已经写好了自己的自定义`Scope`实现,并且已经将其进行了注册:
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);
然后就可以在配置文件中创建与自定义`Scope`规则对应的Bean定义:
<bean id="..." class="..." scope="thread"/>
配置方式注册
对于ApplicationContext来说,因为它可以自动识别并加载BeanFactoryPostProcessor,所以我们就可以直接在配置文件中,通过这个CustomScopeConfigurer注册来使新的Scope作用域范围生效
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="customerScope" value="xxx.xxx.scope.CustomerScope"/>
</map>
</property>
</bean>-->
<bean id="human" class="xxx.xxx.autowired.Human" scope="customerScope">
<!-- 让Bean符合自定义的作用域 -->
<!--<aop:scoped-proxy/>-->
</bean>
</beans>
测试类
public class TestScope {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("scope.xml");
// 代码方式注册新的Scope作用域
// ctx.getBeanFactory().registerScope("customerScope", new CustomerScope());
System.out.println(ctx.getBeanFactory().getBeanDefinition("human").getScope());
String scopes[] = ctx.getBeanFactory().getRegisteredScopeNames();
for (String scope : scopes) {
System.out.println(scope);
}
}
}