Spring Boot 入门

Spring Boot 入门

官方地址
你是否已经厌烦了创建一个web项目
1)配置web.xml,加载spring和spring mvc

2)配置数据库连接、配置spring事务

3)配置加载配置文件的读取,开启注解

4)配置日志文件

...

配置完成之后部署tomcat 调试,每次新建都要重复现有步骤

但是如果使用Spring Boot 你可以start
image.png

点击你选择的依赖,这里我们选择web
image.png

导入我们的环境,我们看它的目录结构
image.png

直接main 函数启动:
我们直接看关键信息
image.png

上面分别告诉你,启动文件使用哪一个,如果没有设置则使用默认的,也就是application.properties
下面是web容器的端口,默认使用的是tomcat,当然也可以方便的更换成其他容器undertow等等,而这些操作只需要简单的更换pom文件和更新properties文件

image.png
这样激活的就是application-local.properties文件
image.png
方便调试不同的环境
image.png
这样就更换的了web容器
image.png
参数配置

剩下的使用就跟spring 差别不大了
image.png
image.png

因为没有了配置文件,我们看看针对spring mvc的一些MethodResolver小技巧 如何配置
本身boot 为我们提供了生命周期的口子:WebMvcConfigurationSupport
image.png
image.png
image.png
这样同样就完成了我们的参数解析,常用的扩展在此声明周期函数中都可以找到
image.png

具体的使用大家可以参考开头官方文档,使用非常详细了,接下来我们看看加载流程:
从开始:
image.png

首先经过代理之后最终看到其目的是为了实例化SpringApplication这个类
image.png

我们看构造函数中核心的几个步骤:
image.png

首先是deduceWebApplicationType,它的目的是为了明确应用的类型是标准类型还是web类型环境,
通过对应环境的核心类进行判断:
image.png

接着是:setInitializers
它会去调用getSpringFactoriesInstances,getSpringFactoriesInstances委托给getSpringFactoriesInstances 去调用SpringFactoriesLoader.loadFactoryNames(type, classLoader)
image.png
loadFactoryNames的作用是:读取META-INF/spring.factories里面的配置文件 明确要实例化的组件
image.png
读取完成之后,开始实例化
image.png

ApplicationContextInitializer根据类文档,这个接口的主要功能是:
在Spring上下文被刷新之前进行初始化的操作。典型地比如在Web应用中,注册Property Sources或者是激活Profiles。Property Sources比较好理解,就是配置文件。Profiles是Spring为了在不同环境下(如DEV,TEST,PRODUCTION等),加载不同的配置项而抽象出来的一个实体。

紧接着开始设置监听器:
image.png
与上面的步骤相同,读取配置文件:
image.png

ApplicationListener是spring的事件接口,事件一套属于spring的观察者模式,咱们写个小例子:
定义一个event
image.png

定义响应者,当登录成功之后,我们缓存账户信息
image.png

mock 当登录之后,我们发一个事件,这样我登录的验证逻辑和登录成功之后要做的事情是分开的
image.png

紧接着需要做的事情就是,找到主main函数
image.png

我们看下,找的方法,略屌:
image.png
运行时异常的栈桢中获取 吊炸天
这样SpringApplication的构造方法就执行完毕了

紧接着执行run方法:
image.png


// 运行run方法
public ConfigurableApplicationContext run(String... args) {
  // 计时工具
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();

    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

    // 设置java.awt.headless系统属性为true - 没有图形化界面
    configureHeadlessProperty();

    // KEY 1 - 获取SpringApplicationRunListeners
    SpringApplicationRunListeners listeners = getRunListeners(args);

    // 发出开始执行的事件
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);

        // KEY 2 - 根据SpringApplicationRunListeners以及参数来准备环境
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        configureIgnoreBeanInfo(environment);

        // 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
        Banner printedBanner = printBanner(environment);

        // KEY 3 - 创建Spring上下文
        context = createApplicationContext();

        // 准备异常报告器
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);

        // KEY 4 - Spring上下文前置处理
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);

        // KEY 5 - Spring上下文刷新
        refreshContext(context);

        // KEY 6 - Spring上下文后置处理
        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, exceptionReporters, ex);
        throw new IllegalStateException(ex);
    }
}

从getRunListeners来看也是执行上面的流程读取配置文件实例化:
image.png

SpringApplicationRunListeners它主要是负责发布SpringApplicationEvent事件的,它会利用一个内部的ApplicationEventMulticaster在上下文实际被刷新之前对事件进行处理。至于具体的应用场景,后面用到的时候再来分析;
接下来的步骤配置环境,这里都做了域隔离
image.png
image.png
记得最开始的spring.profiles.active么,就是在这决定如何操作的
image.png
打印控制台最开始的banner信息

createApplicationContext创建上下文

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            contextClass = Class.forName(this.webEnvironment
                    ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Unable create a default ApplicationContext, "
                            + "please specify an ApplicationContextClass",
                    ex);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}

// WEB应用的上下文类型
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";

上下文类图:
image.png

接下来做前置上文的操作:prepareContext

private void prepareContext(ConfigurableApplicationContext context,
        ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
    // 将环境和上下文关联起来
    context.setEnvironment(environment);

    // 为上下文配置Bean生成器以及资源加载器(如果它们非空)
    postProcessApplicationContext(context);

    // 调用初始化器
    applyInitializers(context);

    // 触发Spring Boot启动过程的contextPrepared事件
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }

    // 添加两个Spring Boot中的特殊单例Beans - springApplicationArguments以及springBootBanner
    context.getBeanFactory().registerSingleton("springApplicationArguments",
            applicationArguments);
    if (printedBanner != null) {
        context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }

    // 加载sources - 对于DemoApplication而言,这里的sources集合只包含了它一个class对象
    Set<Object> sources = getSources();
    Assert.notEmpty(sources, "Sources must not be empty");

    // 加载动作 - 构造BeanDefinitionLoader并完成Bean定义的加载
    load(context, sources.toArray(new Object[sources.size()]));

    // 触发Spring Boot启动过程的contextLoaded事件
    listeners.contextLoaded(context);
}

配置Bean生成器以及资源加载器

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    if (this.beanNameGenerator != null) {
        context.getBeanFactory().registerSingleton(
                AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
                this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        if (context instanceof GenericApplicationContext) {
            ((GenericApplicationContext) context)
                    .setResourceLoader(this.resourceLoader);
        }
        if (context instanceof DefaultResourceLoader) {
            ((DefaultResourceLoader) context)
                    .setClassLoader(this.resourceLoader.getClassLoader());
        }
    }
}

接下来是经典的refreshContext
image.png

还有最后的callRunners,它的作用是整个容器ready的时候去做一些事情,例如以前大家一定使用过
image.png
在具体的 业务域去初始化一些全局的东西,这样不是很好

可以监听容器事件的同时,可以使用runners
image.png
这就大致完成了初始化

原文地址:https://www.cnblogs.com/TestMa/p/10649052.html