Spring MVC bean 解析、注册、实例化流程源码剖析

130 阅读4分钟

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);
}