2019-01-15

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。

目录:

1 学习SpirngBoot的准备知识点
  • 1.1 关于AbstractApplicationContext类以及其子类的一些解读。
        1.1.1 AbstractApplicationContext类信息注释的解读,从类注释开始学习spring。
         AbstractApplicationContext类注释中,@see的关键类/接口BeanFactoryPostProcessor ,BeanPostProcessor ,ApplicationListener ,ApplicationEventMulticaster ,SimpleApplicationEventMulticaster ,DefaultResourceLoader 的解读。

  • 1.2 AbstractApplicationContext类的子类,常用的spring-xxx.xml方式启动和springBoot无xml方式选用的ApplicationContext子类,以及这两个子类的差异。

  • 1.3 spring注解,@Bean @ComponentScan @Configuration @ConditionalOnXXXX @Import @EnableXXX @PropertySource 的学习

2 SpringBoot的启动
  • 2.1 通过war包启动和通过jar包启动的共通点和差异点。

  • 2.2 SpringBoot中设计的SpringApplication类的作用,SpringApplication源码解读,以及在AbstractApplicationContext.refresh()前后做了一些什么事情

  • 2.3 AbstractApplicationContext.refresh() 实现IOC容器源码解读,YY一下spring设计的一些精髓。


  • 1.1 从类AbstractApplicationContext注释开始学习spring。看AbstractApplicationContext的源码的注释上,有几个@see 的类,这个类都是在applicitonContext IOC容器启动中几个比较重要的类,首先查看下这几个类接口的定义,后面在解读源码时讲spring是怎么去实现这些接口的。关于spring钩子的详细解读参考链接
  • 1.1.1 Aware接口族
  • 1.1.2 InitializingBean接口和DisposableBean接口
  • 1.1.3 ImportBeanDefinitionRegistrar接口
  • 1.1.4 BeanPostProcessor接口和BeanFactoryPostProcessor接口
  • 1.1.5 BeanDefinitionRegistryPostProcessor 接口
  • 1.1.6 FactoryBean接口
  • 1.1.7 ApplicationListener
以下为AbstractApplicationContext,BeanPostProcessor,BeanFactoryPostProcessor源码,以及官方注释:
/**
 * Abstract implementation of the {@link org.springframework.context.ApplicationContext}
 * interface. Doesn't mandate the type of storage used for configuration; simply
 * implements common context functionality. Uses the Template Method design pattern,
 * requiring concrete subclasses to implement abstract methods.
 *
 * <p>In contrast to a plain BeanFactory, an ApplicationContext is supposed
 * to detect special beans defined in its internal bean factory:
 * Therefore, this class automatically registers
 * {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessors},
 * {@link org.springframework.beans.factory.config.BeanPostProcessor BeanPostProcessors}
 * and {@link org.springframework.context.ApplicationListener ApplicationListeners}
 * which are defined as beans in the context.
 *
 * <p>A {@link org.springframework.context.MessageSource} may also be supplied
 * as a bean in the context, with the name "messageSource"; otherwise, message
 * resolution is delegated to the parent context. Furthermore, a multicaster
 * for application events can be supplied as "applicationEventMulticaster" bean
 * of type {@link org.springframework.context.event.ApplicationEventMulticaster}
 * in the context; otherwise, a default multicaster of type
 * {@link org.springframework.context.event.SimpleApplicationEventMulticaster} will be used.
 *
 * <p>Implements resource loading through extending
 * {@link org.springframework.core.io.DefaultResourceLoader}.
 * Consequently treats non-URL resource paths as class path resources
 * (supporting full class path resource names that include the package path,
 * e.g. "mypackage/myresource.dat"), unless the {@link #getResourceByPath}
 * method is overwritten in a subclass.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Mark Fisher
 * @author Stephane Nicoll
 * @since January 21, 2001
 * @see #refreshBeanFactory
 * @see #getBeanFactory
 * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor
 * @see org.springframework.beans.factory.config.BeanPostProcessor
 * @see org.springframework.context.event.ApplicationEventMulticaster
 * @see org.springframework.context.ApplicationListener
 * @see org.springframework.context.MessageSource
 */
public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext, DisposableBean {
}

Bean工厂的后置处理器接口,可以管理我们的bean工厂内所有的beandefinition(未实例化)数据
在AbstractApplictionContext.refresh()方法中,通过invokeBeanFactoryPostProcessors(beanFactory);调用。

public interface BeanFactoryPostProcessor {

    /**
     * Modify the application context's internal bean factory after its standard
     * initialization. All bean definitions will have been loaded, but no beans
     * will have been instantiated yet. This allows for overriding or adding
     * properties even to eager-initializing beans.
     * @param beanFactory the bean factory used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

/**
 * Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for
 * the registration of further bean definitions <i>before</i> regular
 * BeanFactoryPostProcessor detection kicks in. In particular,
 * BeanDefinitionRegistryPostProcessor may register further bean definitions
 * which in turn define BeanFactoryPostProcessor instances.
 *
 * @author Juergen Hoeller
 * @since 3.0.1
 * @see org.springframework.context.annotation.ConfigurationClassPostProcessor
 */
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

    /**
     * Modify the application context's internal bean definition registry after its
     * standard initialization. All regular bean definitions will have been loaded,
     * but no beans will have been instantiated yet. This allows for adding further
     * bean definitions before the next post-processing phase kicks in.
     * @param registry the bean definition registry used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

在AbstractApplictionContext.refresh()方法中,通过registerBeanPostProcessors注册加载类信息,通过finishBeanFactoryInitialization(beanFactory)实例化执行BeanPostProcessor的相关操作

Bean的后置处理器接口
public interface BeanPostProcessor {

    /**
     * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
     * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
     * or a custom init-method). The bean will already be populated with property values.
     * The returned bean instance may be a wrapper around the original.
     * @param bean the new bean instance
     * @param beanName the name of the bean
     * @return the bean instance to use, either the original or a wrapped one;
     * if {@code null}, no subsequent BeanPostProcessors will be invoked
     * @throws org.springframework.beans.BeansException in case of errors
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
     */
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

    /**
     * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
     * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
     * or a custom init-method). The bean will already be populated with property values.
     * The returned bean instance may be a wrapper around the original.
     * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
     * instance and the objects created by the FactoryBean (as of Spring 2.0). The
     * post-processor can decide whether to apply to either the FactoryBean or created
     * objects or both through corresponding {@code bean instanceof FactoryBean} checks.
     * <p>This callback will also be invoked after a short-circuiting triggered by a
     * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
     * in contrast to all other BeanPostProcessor callbacks.
     * @param bean the new bean instance
     * @param beanName the name of the bean
     * @return the bean instance to use, either the original or a wrapped one;
     * if {@code null}, no subsequent BeanPostProcessors will be invoked
     * @throws org.springframework.beans.BeansException in case of errors
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
     * @see org.springframework.beans.factory.FactoryBean
     */
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}
  • 1.2 AbstractApplicationContext子类信息,我们SpringBoot的web环境用到的是AnnotationConfigEmbeddedWebApplicationContext子类,非web环境用到的是AnnotationConfigApplicationContext子类。而传统通过spring-xxx.xml方式启动的web环境,用到的是ClassPathXmlApplicationContext.有兴趣的可以自行点击AbstractApplicationContext.refresh()方法里面的各个方法,查询这两个子类在实现上的差异。


    image.png

    image.png
SpringBoot正式部分
  • 2.0 war方式启动写法和jar方式启动写法
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class})
public class WarApplication extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        Map<String, Object> properties = new HashMap<>();
        return builder.properties(properties).sources(this.getClass());
    }
}
@SpringBootApplication
@MapperScan(basePackages = "com.xxx.xxx.mapper")
@ServletComponentScan(basePackages = "com.xxx.xxxx")
public class Application extends WebMvcConfigurerAdapter {
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

  • 2.1 war包方式启动,基于servlet3.0的扩展。通过实现ServletContainerInitializer的onStartup方法,SpringServletContainerInitializer调用springboot的SpringBootServletInitializer类的onStartup方法,==>调用createRootApplicationContext方法==》运行SpringAppliction的run方法,
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
    public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
            throws ServletException {
      for (WebApplicationInitializer initializer : initializers) {
            initializer.onStartup(servletContext);
        }
      }
}
    public void onStartup(ServletContext servletContext) throws ServletException {
        // Logger initialization is deferred in case a ordered
        // LogServletContextInitializer is being used
        this.logger = LogFactory.getLog(getClass());
        WebApplicationContext rootAppContext = createRootApplicationContext(
                servletContext);
        if (rootAppContext != null) {
            servletContext.addListener(new ContextLoaderListener(rootAppContext) {
                @Override
                public void contextInitialized(ServletContextEvent event) {
                    // no-op because the application context is already initialized
                }
            });
        }
        else {
            this.logger.debug("No ContextLoaderListener registered, as "
                    + "createRootApplicationContext() did not "
                    + "return an application context");
        
    protected WebApplicationContext createRootApplicationContext(
            ServletContext servletContext) {
/*
这儿通过此方法中的new SpringApplication(sources),调用initialize(),
初始实例化了spring自带的一些Initializers和Listeners保存在SpringApplication的list中
(具体实现原理通过:SpringFactoriesLoader.loadFactoryNames(type, classLoader)加载所有
jar包META-INF下的spring.factories里面指定type的是实现类),详细见下图A1 图A2:
*/
        SpringApplicationBuilder builder = createSpringApplicationBuilder(){
};
        StandardServletEnvironment environment = new StandardServletEnvironment();
        environment.initPropertySources(servletContext, null);
        builder.environment(environment);
        builder.main(getClass());
        ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
        if (parent != null) {
            this.logger.info("Root context already created (using as parent).");
            servletContext.setAttribute(
                    WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
            builder.initializers(new ParentContextApplicationContextInitializer(parent));
        }
        builder.initializers(
                new ServletContextApplicationContextInitializer(servletContext));
/*
重点:这个地方设置了SpirngBoot的AbstrctApplictionContext的实例化子类类型,而
通过jar方式启动,这个为null,SpringApplication在new ApplicationContext的时候
,用的SpirngApplication.DEFAULT_WEB_CONTEXT_CLASS作为默认值。
*/
builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
/*
重点:这个地方设置了一个source类信息,就是@SpringBootApplication标注的主类。
这个在后面refresh()方法中会讲到,工厂后置处理器中就是通过
这个SpringAppliction中的两个类为起始,扫描加载注册了整个工程中的类信息。
*/
        builder = configure(builder);
        SpringApplication application = builder.build();
        if (application.getSources().isEmpty() && AnnotationUtils
                .findAnnotation(getClass(), Configuration.class) != null) {
            application.getSources().add(getClass());
        }
        Assert.state(!application.getSources().isEmpty(),
                "No SpringApplication sources have been defined. Either override the "
                        + "configure method or add an @Configuration annotation");
        // Ensure error pages are registered
        if (this.registerErrorPageFilter) {
//重点:这个地方额外增加了一个source类型信息
            application.getSources().add(ErrorPageFilterConfiguration.class);
        }
        return run(application);
    }

图A1
image.png

图A2
image.png
2.2 接下来看一下SpringApplication最重要的一个方法:
/**
     * Run the Spring application, creating and refreshing a new
     * {@link ApplicationContext}.
     * @param args the application arguments (usually passed from a Java main method)
     * @return a running {@link ApplicationContext}
     */
    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();构造一个任务执行观察器
        stopWatch.start();开始执行,记录开始时间
        ConfigurableApplicationContext context = null;
        FailureAnalyzers analyzers = null;
        configureHeadlessProperty();
        //获得EventPublishingRunListener实例(图B0)
        SpringApplicationRunListeners listeners = getRunListeners(args);
        /*通过EventPublishingRunListener的 ApplicationStartedEvent
(this.application, this.args)springBoot启动事件,之前初始化加载的多个监听器都会监听此事件,
执行listener.onApplicationEvent(event)方法。
        */
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            //准备环境,加载spring的properties或者yml配置文件key-value到environment
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            Banner printedBanner = printBanner(environment);
            /*1 创建ApplicationContext,这里面的contextClass类型为之前SpringApplication设置的值,
2 AnnotationConfigEmbeddedWebApplicationContext,这个类的构建方法会调用
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)加载一部分
内置beanFactoryPostProcessor,其中包含refresh()方法中最开始第一个调用的beanFactoryPostProcessor,这个在后面invokeBeanFactoryPostProcessors中会讲到。
            */
            context = createApplicationContext();
//这儿加载的是一些spring异常包装类,见下图B1
            analyzers = new FailureAnalyzers(context);
/*
1 设置environment
2  调用之前SpringApplication初始化中加载的ApplicationContextInitializer.initialize()
包括加载ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor
SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor
ConfigFileApplicationListener$PropertySourceOrderingPostProcessor
3注册之前SpringApplication前面设置source的类信息。
load(context, sources.toArray(new Object[sources.size()]));
4 发布ApplicationPreparedEvent(this.application, this.args, context)事件
*/
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            listeners.finished(context, null);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            return context;
        }
        catch (Throwable ex) {
            handleRunFailure(context, listeners, analyzers, ex);
            throw new IllegalStateException(ex);
        }
    }

图B0
image.png

图B1

image.png

AbstractApplicationContext最重要的一个方法
因这个方法相关篇幅较多,网上的资料也特别多,就不再这个地方重新写一遍。
参考:SpringBoot源码分析之Spring容器的refresh过程
Spring源码finishBeanFactoryInitialization和getBean

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

SpirngBoot Jar方式启动,内嵌jetty或者tomcat容器实现方法

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

推荐阅读更多精彩内容