Spring源码分析(一)

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包下的东西

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352