Spring MVC bean 解析、注册、实例化流程源码剖析
Spring Bean 解析注册
Spring bean的加载主要分为以下6步:
####(1)读取XML配置文件
####(2)XML文件解析为document文档
####(3)解析bean
####(4)注册bean
####(5)实例化bean
####(6)获取bean
(1)读取XML配置文件
查看源码第一步是找到程序入口,再以入口为突破口,一步步进行源码跟踪。
Java Web应用中的入口就是web.xml。
在web.xml找到ContextLoaderListener ,此Listener负责初始化Spring IOC。
contextConfigLocation参数设置了bean定义文件地址。
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring.xml</param-value>
</context-param>
ContextLoaderListener作用就是负责启动和关闭Spring root WebApplicationContext。
具体WebApplicationContext是什么?开始看源码。
package org.springframework.web.context;
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
//servletContext初始化时候调用/负责初始化WebApplicationContext
public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext();
}
//servletContext销毁时候调用/负责销毁WebApplicationContext
public void contextDestroyed(ServletContextEvent event) {
this.closeWebApplicationContext(event.getServletContext());
}
}
继续看initWebApplicationContext函数。
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//初始化Spring容器时如果发现servlet 容器中已存在根Spring容根器则抛出异常,证明rootWebApplicationContext只能有一个。
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
if (this.context == null) {
//1.创建webApplicationContext实例
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
//2.配置WebApplicationContext
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
//把生成的webApplicationContext 设置为root webApplicationContext。
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
return this.context;
}
在上面的代码中主要有两个功能:
(1)创建WebApplicationContext实例。
(2)配置生成WebApplicationContext实例。
1.1创建WebApplicationContext实例
进入CreateWebAPPlicationContext函数
//1.创建webApplicationContext实例
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
//得到ContextClass类,默认实例化的是 XmlWebApplicationContext类
Class<?> contextClass = determineContextClass(sc);
//实例化Context类
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
进入determineContextClass函数
protected Class<?> determineContextClass(ServletContext servletContext) {
// 此处CONTEXT_CLASS_PARAM = "contextClass"
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
//若设置了contextClass则使用定义好的ContextClass。
//通过指定的classloader加载对应的类
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
else {
//此处获取的是在Spring源码中ContextLoader.properties中配置的org.springframework.web.context.support.XmlWebApplicationContext类。
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
//.class.getName()方法:
* String.class.getName()
* returns "java.lang.String"
* byte.class.getName()
* returns "byte"
* (new Object[3]).getClass().getName()
* returns "[Ljava.lang.Object;"
* (new int[3][4][5][6][7][8][9]).getClass().getName()
* returns "[[[[[[[I"
}
1.2 配置Web ApplicationContext
进入configureAndReFreshWebApplicaitonContext函数
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
//webapplicationContext设置servletContext.
wac.setServletContext(sc);
// 此处CONFIG_LOCATION_PARAM = "contextConfigLocation",
//即读即取web.xm中配设置的contextConfigLocation参数值,获得spring bean的配置文件.
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
//webApplicationContext设置配置文件路径设。
wac.setConfigLocation(configLocationParam);
}
//开始处理bean
wac.refresh();
}
(2)XML文件解析为document文档
上面wac变量声明为ConfigurableWebApplicationContext类型,ConfigurableWebApplicationContext又继承了WebApplicationContext。
WebApplication Context有很多实现类。 但从上面determineContextClass得知此处wac实际上是XmlWebApplicationContext类,因此进入XmlWebApplication类查看其继承的refresh()方法。
沿方法调用栈一层层看下去。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//获取beanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 实例化所有声明为非懒加载的单例bean
finishBeanFactoryInitialization(beanFactory);
}
}
获取beanFactory。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//初始化beanFactory
refreshBeanFactory();
return beanFactory;
}
beanFactory初始化。
@Override
protected final void refreshBeanFactory() throws BeansException {
DefaultListableBeanFactory beanFactory = createBeanFactory();
//加载bean定义
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
加载bean。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//创建XmlBeanDefinitionReader实例来解析XML配置文件
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
initBeanDefinitionReader(beanDefinitionReader);
//解析XML配置文件中的bean。
loadBeanDefinitions(beanDefinitionReader);
}
最终将XML文件解析成Document文档对象。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
//XML配置文件解析到Document实例中
Document doc = doLoadDocument(inputSource, resource);
//注册bean
return registerBeanDefinitions(doc, resource);
}
(3)解析bean
上一步完成了XML文件的解析工作,接下来将XML中定义的bean注册到webApplicationContext,继续跟踪函数。
跟踪registerBeanDefinition函数,此函数将bean信息保存到到webApplicationContext的beanDefinitionMap变量中,该变量为map类型,保存Spring 容器中所有的bean定义。
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
//把bean信息保存到beanDefinitionMap中
this.beanDefinitionMap.put(beanName, beanDefinition);
//把beanName 保存到List 类型的beanDefinitionNames属性中
this.beanDefinitionNames.add(beanName);
}
(5)实例化bean
Spring 实例化bean的时机有两个。
一个是容器启动时候,另一个是真正调用的时候。
如果bean声明为scope=singleton且lazy-init=false,则容器启动时候就实例化该bean(Spring 默认就是此行为)。否则在调用时候再进行实例化。
相信用过Spring的同学们都知道以上概念,但是为什么呢?
继续从源码角度进行分析,回到之前XmlWebApplication的refresh()方法。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//生成beanFactory,
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 实例化所有声明为非懒加载的单例bean
finishBeanFactoryInitialization(beanFactory);
}
可以看到获得beanFactory后调用了 finishBeanFactoryInitialization()方法,继续跟踪此方法。
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 初始化非懒加载的单例bean
beanFactory.preInstantiateSingletons();
}
预先实例化单例类逻辑。
public void preInstantiateSingletons() throws BeansException {
// 获取所有注册的bean
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
// 遍历bean
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
//如果bean是单例且非懒加载,则获取实例
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
getBean(beanName);
}
}
}
判断哪种动态代理方式实例化bean。
public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
//使用JDK动态代理
if (bd.getMethodOverrides().isEmpty()) {
return BeanUtils.instantiateClass(constructorToUse);
}
else {
//使用CGLIB动态代理
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
不管哪种方式最终都是通过反射的形式完成了bean的实例化。
public static <T> T instantiateClass(Constructor<T> ctor, Object... args)
ReflectionUtils.makeAccessible(ctor);
return ctor.newInstance(args);
}