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

Spring源码分析(二)

作者: 程序员大黑鱼 | 来源:发表于2019-03-01 13:09 被阅读0次

Spring源码分析 二

基于Spring 5.1.5

1.initWebApplicationContext源码分析

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    ...
    //cwac.isActive()为什么是false呢?
    //初始化的时候容器还未刷新,容器的刷新是在第三步进行
    if (!cwac.isActive()) {
        if (cwac.getParent() == null) {
            ApplicationContext parent = this.loadParentContext(servletContext);
            cwac.setParent(parent);
        }

        this.configureAndRefreshWebApplicationContext(cwac, servletContext);
    }
    ...
}

2.cwac.isActive()源码分析

通过看下方代码可以看出只有当value==0时,cwac.isActive()才为false
刷新之后value的值会置为1,讲解在后边

public boolean isActive() {
    return this.active.get();
}

public final boolean get() {
    return value != 0;
}

3.configureAndRefreshWebApplicationContext源码分析

容器的configureAndRefreshWebApplicationContext:配置并且刷新,这一步是整个spring ioc的核心

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    String configLocationParam;
    //返回对象整体标识的字符串表示形式。
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
        configLocationParam = sc.getInitParameter("contextId");
        if (configLocationParam != null) {
            wac.setId(configLocationParam);
        } else {
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
        }
    }

    //它将servletContext这个上下文放到了wac中,
    //也就是说,在Spring容器中,有了servlet的上下文参数,那么我们可以在spring后续的容器操作中,使用servlet的配置等信息了。
    wac.setServletContext(sc);
    
    //读取web.xml中Spring配置文件路径
    configLocationParam = sc.getInitParameter("contextConfigLocation");
    if (configLocationParam != null) {
        //contextConfigLocation有可能有多个,通过StringTokenizer将给定字符串标记为字符串数组。
        //字符串数组放到了AbstractRefreshableConfigApplicationContext的一个成员数组configLocations中,
        //这里先mark一下,因为这里它放进去,是为了后续取出来去加载bean用的,所以记牢
        wac.setConfigLocation(configLocationParam);
    }

    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);
    }
    
    //这一步没看懂,定制什么
    this.customizeContext(sc, wac);
    //这个方法就要开始Spring容器的刷新了
    wac.refresh();
}

4.refresh()源码分析

public void refresh() throws BeansException, IllegalStateException {
    Object var1 = this.startupShutdownMonitor;
    synchronized(this.startupShutdownMonitor) {
        //preRefresh预刷新,其实就是把记录一下开始时间,打印一下日志,然后把servletConfig和servletContext放到spring容器的propertySources容器里面.
        this.prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        this.prepareBeanFactory(beanFactory);

        try {
            this.postProcessBeanFactory(beanFactory);
            this.invokeBeanFactoryPostProcessors(beanFactory);
            this.registerBeanPostProcessors(beanFactory);
            this.initMessageSource();
            this.initApplicationEventMulticaster();
            this.onRefresh();
            this.registerListeners();
            this.finishBeanFactoryInitialization(beanFactory);
            this.finishRefresh();
        } catch (BeansException var9) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
            }

            this.destroyBeans();
            this.cancelRefresh(var9);
            throw var9;
        } finally {
            this.resetCommonCaches();
        }

    }
}

prepareRefresh()源码分析:

protected void prepareRefresh() {
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);
    //修改active状态为true,可以认为它是容器的激活状态,既将第2步的value值置为1
    this.active.set(true);
    if (this.logger.isDebugEnabled()) {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Refreshing " + this);
        } else {
            this.logger.debug("Refreshing " + this.getDisplayName());
        }
    }
    //初始化propertySources这个容器
    //初始化propertySources,注意了,这个propertySources不是一堆propertySource啊,而是一个容器
    //spring里有两个概念,一个是propertySource,一个是propertySources,后者不是简单的表示一堆前者,而是表示一个存放前者的容器。
    //下方的源码分析中说明
    this.initPropertySources();
    
    // 验证所有的必须的属性。
    this.getEnvironment().validateRequiredProperties();
    if (this.earlyApplicationListeners == null) {
        this.earlyApplicationListeners = new LinkedHashSet(this.applicationListeners);
    } else {
        this.applicationListeners.clear();
        this.applicationListeners.addAll(this.earlyApplicationListeners);
    }
    
    this.earlyApplicationEvents = new LinkedHashSet();
}

PropertySource源码分析

从泛型和构造我们可以大致了解,propertySource,是一个用来存放property的keyValue的实体,构造表示,我拿任意类型的对象进来,都可以设置一个key,存在成员name上,然后把对象存放在成员source上,是不是很像一个Map.Entry<String,Object>对象呢。

public abstract class PropertySource<T> {
    protected final Log logger;
    protected final String name;
    protected final T source;

    public PropertySource(String name, T source) {
        this.logger = LogFactory.getLog(this.getClass());
        Assert.hasText(name, "Property source name must contain at least one character");
        Assert.notNull(source, "Property source must not be null");
        this.name = name;
        this.source = source;
    }
}

PropertySources源码分析

可以看到,他是propertySource的一个容器接口,具备迭代能力,有2个容器级别的方法,contains和get,这都是集合中常见的概念,它只有一个实现类,MutablePropertySources

public interface PropertySources extends Iterable<PropertySource<?>> {
    default Stream<PropertySource<?>> stream() {
        return StreamSupport.stream(this.spliterator(), false);
    }

    boolean contains(String var1);

    @Nullable
    PropertySource<?> get(String var1);
}

MutablePropertySources源码分析

它有一个propertySourceList,是用链表List来实现的,用于存放所有的propertySource,并且MutablePropertySources实现了contanins,get等集合方法,还有addFirst,addLast等添加对象的方法。在我们还没看到propertyResolver之前,先可以认为propertySource的检索都由容器自己完成,后续会看到一个全新的工具resolver,专门用于propertySources的检索,解析等相关工作

public class MutablePropertySources implements PropertySources {
    private final List<PropertySource<?>> propertySourceList;
    ...

再回到initPropertySources方法中

我们可以看到,initServletProperSources,顾名思义,就是初始化propertySources这个容器,那我们应该可以想到,它应该是往容器里塞点东西, 再看后面2个参数,一个是servletContext,一个servletConfig,那么我们知道了,它这个初始化,其实是想把servletContext,一个servletConfig这两个对象给存到容器的propertySources容器里去

protected void initPropertySources() {
    ConfigurableEnvironment env = this.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment)env).initPropertySources(this.servletContext, this.servletConfig);
    }

}

initPropertySources中的initServletPropertySources分析

我们在之前容器里已经有servletContext了,那么现在servletContext就被propertySources接管了

public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
    WebApplicationContextUtils.initServletPropertySources(this.getPropertySources(), servletContext, servletConfig);
}

public static void initServletPropertySources(MutablePropertySources sources, @Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
    Assert.notNull(sources, "'propertySources' must not be null");
    String name = "servletContextInitParams";
    if (servletContext != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
        sources.replace(name, new ServletContextPropertySource(name, servletContext));
    }

    name = "servletConfigInitParams";
    if (servletConfig != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
        sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
    }

}

到这里也就知道了原来preRefresh预刷新,其实就是把记录一下开始时间,打印一下日志,然后把servletConfig和servletContext放到spring容器的propertySources容器里面.

相关文章

网友评论

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

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