美文网首页
Spring源码分析(一)

Spring源码分析(一)

作者: 程序员大黑鱼 | 来源:发表于2019-02-28 22:29 被阅读0次

    Spring源码分析 一

    基于Spring 5.1.5

    前言:要分析Spring源码,首先得知道Spring是怎么被应用到web项目中的

    1.Web项目的启动加载顺序

    1、启动一个WEB项目的时候,WEB容器会去读取它的配置文件web.xml,读取<context-param>和<listener>两个结点。
    2、紧急着,容创建一个ServletContext(servlet上下文),这个web项目的所有部分都将共享这个上下文。
    3、容器将<context-param>转换为键值对,并交给servletContext。
    4、容器创建<listener>中的类实例,创建监听器。
    5、容器创建<filter>中的类实例,创建过滤器。

    最终得出的结论是:ServletContext-> listener ->filter -> servlet

    2.web.xml中Spring的配置

    <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring/applicationContext.xml</param-value>
    </context-param>
    <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    

    3.初始化源码分析

    ContextLoaderListener源码分析

    public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
        public ContextLoaderListener() {
        }
    
        public ContextLoaderListener(WebApplicationContext context) {
            super(context);
        }
    
        public void contextInitialized(ServletContextEvent event) {
            //ContextLoaderListener继承了ContextLoader类,
            //真正的创建在操作,是在ContextLoader的initWebApplicationContext方法中完成。
            this.initWebApplicationContext(event.getServletContext());
        }
    
        public void contextDestroyed(ServletContextEvent event) {
            this.closeWebApplicationContext(event.getServletContext());
            ContextCleanupListener.cleanupAttributes(event.getServletContext());
        }
    }
    

    initWebApplicationContext源码分析

    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        //保证只有一个WebApplicationContext
        //从ServletContext查找WebApplicationContext实例,如果已经存在,则抛出异常
        //典型异常:在web.xml中配置了不止一个ContextLoaderListener,造成多次初始化
        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!");
        } else {
            servletContext.log("Initializing Spring root WebApplicationContext");
            Log logger = LogFactory.getLog(ContextLoader.class);
            if (logger.isInfoEnabled()) {
                logger.info("Root WebApplicationContext: initialization started");
            }
    
            long startTime = System.currentTimeMillis();
    
            try {
                if (this.context == null) {
                    //创建WebApplicationContext实例,其实这里创建的是它的子类ConfigurableWebApplicationContext
                    this.context = this.createWebApplicationContext(servletContext);
                }
    
                if (this.context instanceof ConfigurableWebApplicationContext) {
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
                    if (!cwac.isActive()) {
                        if (cwac.getParent() == null) {
                            ApplicationContext parent = this.loadParentContext(servletContext);
                            cwac.setParent(parent);
                        }
    
                        this.configureAndRefreshWebApplicationContext(cwac, 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.isInfoEnabled()) {
                    long elapsedTime = System.currentTimeMillis() - startTime;
                    logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
                }
    
                return this.context;
            } catch (Error | RuntimeException var8) {
                logger.error("Context initialization failed", var8);
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);
                throw var8;
            }
        }
    }
    

    createWebApplicationContext源码分析

    protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
        //既然要创建一个context,首先需要确定context是由什么类定义
        Class<?> contextClass = this.determineContextClass(sc);
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        } else {
            return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
        }
    }
    

    determineContextClass源码分析

    protected Class<?> determineContextClass(ServletContext servletContext) {
        String contextClassName = servletContext.getInitParameter("contextClass");
        if (contextClassName != null) {
            //如果在web.xml中配置了contextClass,则直接读取返回context定义类
            try {
                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
            } catch (ClassNotFoundException var4) {
                throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4);
            }
        } else {
            //如果没有定义的话,去defaultStrategies里去取,WebApplicationContext的类名作为key
            //默认是XmlWebApplicationContext
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
    
            try {
                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            } catch (ClassNotFoundException var5) {
                throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5);
            }
        }
    }
    

    那么defaultStrategies里的值哪来的呢?

    static {
        try {
            //读取ContextLoader.properties文件
            //文件位置在下图中
            ClassPathResource resource = new ClassPathResource("ContextLoader.properties", ContextLoader.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        } catch (IOException var1) {
            throw new IllegalStateException("Could not load 'ContextLoader.properties': " + var1.getMessage());
        }
    
        currentContextPerThread = new ConcurrentHashMap(1);
    }
    

    ContextLoader.properties文件位置

    ContextLoader.properties.png

    ContextLoader.properties文件内容

    # Default WebApplicationContext implementation class for ContextLoader.
    # Used as fallback when no explicit context implementation has been specified as context-param.
    # Not meant to be customized by application developers.
    
    org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
    

    接着回到createWebApplicationContext源码分析

    protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
        //既然要创建一个context,首先需要确定context是由什么类定义
        //spring的web工程会首先取用户在web.xml里配置的context的类,如果用户配置了,就采用配置的,如果用户没有配置,就采用默认的xmlWebApplicationContext
        Class<?> contextClass = this.determineContextClass(sc);
        //spring要求,必须是可配置的,也就是configurable.只有是Configurable,才能进行后续的配置操作,至于Configurable的具体含义,我们后面会了解到。
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        } else {
            //这个方法是用xmlWebApplicationContext的类作为参数,调用它的无参构造,实例一个对象,也就是我们期待已久的spring容器对象,至此,spring容器创建了
            return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
        }
    }
    

    至此Spring容器就创建了!

    接下来在(Spring源码分析(二))里,我们去学习填充容器的过程,包括它是怎么配置的,还有最重要的,它是怎么刷新的,“刷新”这个动作就是将spring的xml配置变成bean,这个是ioc的核心,会涉及到一系列 beans包和core包,还有io包下的东西

    相关文章

      网友评论

          本文标题:Spring源码分析(一)

          本文链接:https://www.haomeiwen.com/subject/fkkluqtx.html