Spring源码学习系列第一篇

       相信对于从事java开发的工程师,没有人不晓得Spring,它替代了EJB,成为当今最流行的开发框架,特别是在互联网,特别是移动互联网当道的今天,模块化的微服务更是盛行,springBoot,spring cloud日渐成为新宠。

      从事软件开发数年,一直使用Spring框架,但是一直没有机会学习其源码,趁着工作闲暇之余,了解学习Spring源码,学习这些伟大的产品的设计方案和实现原理,当然阅读源码是一项比较费力的事情,记得Spring技术内幕的作者在序论中谈到这点,他说用了半年时间去详细的学习源码。今天就开始源码分析的第一篇:


      spring最为大家熟悉也是使用最多的就是他的IOC容器和AOP切面编程,对象间相互关系的维护交由spring来处理,大大减轻了工程师们的工作。对AOP使用最广泛的就是spring提供的声明式事务。

     今天开篇就从spring的IOC容器开始说起,BeanFactory,spring提供的最基础的容器接口,开发过程中使用的ApplicationContext上下文就是从beanFactory扩展出来的,并实现了其他相关的接口,来提供ioc容器的高级功能,比如国际化,资源加载等

    首先看一张关于spring提供的BeanFactory接口的类图,初略的了解下相关的类和接口:


  是不是有种眼花缭乱的感觉,不管你是不是,反正我看源码时,颇有种凌乱的感脚,其实,我们将这个类图整理分类成两条线,一条是BeanFactory,一条是ApplicationContext,BeanFactory线是不是已将整个类图的右上瓜分掉了,Application接口左边的几个接口为它提供了高级的功能,往下就是它的子类或子接口了,至于每个类或者接口都是干什么用的,暂时先不必理会,在分析源码的时候,会介绍的。

    那么既然学习源码,总要有个入口把,从那开始学习那,就从ContextLoaderListener类开始,因为他是启动spring容器的关键,我们一般使用spring,都会在web.xml中去配置它。

  首先看看此类的声明

 public class ContextLoaderListener extends ContextLoader implements  ServletContextListener

    因为实现了ServletContextListner,所以当web容器启动时候,会回掉监听器的contextInitialized(ServletContextEvent event)

  查看ContextLoaderListener中此方法的实现,发现它调用了 initWebApplicationContext(event.getServletContext());

  此方法是在基类ContextLoader中实现的,相关部分源码:

try {
   // Store context in local instance variable, to guarantee that
   // it is available on ServletContext shutdown.
   if (this.context == null) {
      //创建web上下文,实现在此方法中
      this.context = createWebApplicationContext(servletContext);
   }
   if (this.context instanceof ConfigurableWebApplicationContext) {
      ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
      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 ->
            // determine parent for root web application context, if any.
            ApplicationContext parent = loadParentContext(servletContext);
            cwac.setParent(parent);
         }
         //配置刷新根容器,这是spring能够启动的关键,下文分析
         configureAndRefreshWebApplicationContext(cwac, servletContext);
      }
   }
   //将创建好的上下文保存在ServletContext中
   servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

   ClassLoader ccl = Thread.currentThread().getContextClassLoader();
   if (ccl == ContextLoader.class.getClassLoader()) {
      currentContext = this.context;
   }
   else if (ccl != null) {
      currentContextPerThread.put(ccl, this.context);
   }

   if (logger.isDebugEnabled()) {
      logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
            WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
   }
   if (logger.isInfoEnabled()) {
      long elapsedTime = System.currentTimeMillis() - startTime;
      logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
   }

   return this.context;
}
 
关键看如何创建的根上下文:
 
 
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
   Class<?> contextClass = determineContextClass(sc); //决定用哪种容器
//选择好容器类型后判断是否是 ConfigurableWebApplicationContext同类或子类,不是则抛异常
   if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {       throw new ApplicationContextException("Custom context class [" + contextClass.getName() +             "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");    }
//实例化根容器交由工具类BeanUtils实现,    return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); }
determineContextClass方法是决定使用哪种容器类,看看是如何实现的:


 
 
protected Class<?> determineContextClass(ServletContext servletContext) {
//首先查询<context-param>中有没有配置contextClass参数,如果 配 置,则用配置的,若没有配置,则从默认策略中取
   String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);    if (contextClassName != null) {       try {          return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());       }       catch (ClassNotFoundException ex) {          throw new ApplicationContextException(                "Failed to load custom context class [" + contextClassName + "]", ex);       }    }
   else {
     //默认策略由spring提供的ContextLoader.properties配置,默认只配置一个XmlWebApplicationContext
      contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
      try {//加载类文件
         return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());       }       catch (ClassNotFoundException ex) {          throw new ApplicationContextException(                "Failed to load default context class [" + contextClassName + "]", ex);       }    }
}
开始刷新容器

 
 
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {    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
      //初始化参数设置了contextId的话,则将容器的id设置为配置的
      String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);       if (idParam != null) {          wac.setId(idParam);       }       else {          // Generate default id... 生成默认的id          wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +                ObjectUtils.getDisplayString(sc.getContextPath()));       }
   }
   //保存servlet上下文保存到root容器中
   wac.setServletContext(sc);
  //获取配置的spring配置文件路径参数contextConfigLocation
   String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);    if (configLocationParam != null) {       wac.setConfigLocation(configLocationParam);    }    // 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(sc, null);
   }
//定制ContextLoader创建的root容器,一般发生在refresh前,配置contextConfigLocation后
   customizeContext(sc, wac);
//开始刷新容器,springIOC容器最重要的开始
   wac.refresh(); }
customizeContext(sc,wac)方法是在容器创建完毕,允许我们对根容器作一些更改,一般项目上也不常用,这里还是可以看一下的,因为它
还是有点作用的

 
 
protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
  //对容器的定制功能都封装在determinContextInitiallizer方法中,下文分析如何定制
   List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =          determineContextInitializerClasses(servletContext);    if (initializerClasses.isEmpty()) {       // no ApplicationContextInitializers have been declared -> nothing to do       return;    }    Class<?> contextClass = applicationContext.getClass();    ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances =          new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>(); //如果有配置定制的类,则循环取,并加入到集合在中,    for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {       Class<?> initializerContextClass =             GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);       if (initializerContextClass != null) {          Assert.isAssignable(initializerContextClass, contextClass, String.format(                "Could not add context initializer [%s] as its generic parameter [%s] " +                "is not assignable from the type of application context used by this " +                "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),                contextClass.getName()));       }       initializerInstances.add(BeanUtils.instantiateClass(initializerClass));    }    ConfigurableEnvironment env = applicationContext.getEnvironment();    if (env instanceof ConfigurableWebEnvironment) {       ((ConfigurableWebEnvironment) env).initPropertySources(servletContext, null);    }    AnnotationAwareOrderComparator.sort(initializerInstances);
//调用ApplicationContextInitializer中德initialize方法,我们的定制功能都在这方法实现    for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {       initializer.initialize(applicationContext);    } }
从上述源码可以看出配置的必须是ApplicationContextInitializer的实现类,配置参数是contextInitializerClasses
 多个可以以","分割

 
 
例子:
web.xml中配置
<context-param>    <param-name>contextInitializerClasses</param-name>    <param-value>freestyle.ContextInitializerImp</param-value>
</context-param>
/**  * Created by 要 on 2017/2/21.  */ public class ContextInitializerImp implements ApplicationContextInitializer<XmlWebApplicationContext> {     @Override
    public void initialize(XmlWebApplicationContext applicationContext) {
//可以更新根容器的全局属性
        applicationContext.setAllowBeanDefinitionOverriding(false);
 // initPropertySources在定制容器时执行过,是没有必要执行的
        StandardServletEnvironment environment =
                              (StandardServletEnvironment) applicationContext.getEnvironment();
        environment.initPropertySources(applicationContext.getServletContext(),
                                        applicationContext.getServletConfig());
//设置活动的profile,这个一般用在配置多环境(开发,测试),其他没有配置为活动的则spring不会去加载
        environment.setActiveProfiles("dev");
    } }
根容器创建完毕,其实没有介绍太多东西,这篇中介绍的部分,一般开发中也很少用到,如定制根容器
 wac.refresh()才是springIOC容器实现的核心,那就下次一起学习吧!

   

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值