• 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
    这就大致完成了初始化

  • 相关阅读:
    关于多态
    关于lock锁
    wait()和notify()
    多线程之间的通讯
    多线程的异步请求模式
    合理配置线程池
    自定义线程池
    Curl的毫秒超时的一个”Bug”
    Nginx正确记录post日志的方法
    NGINX的奇淫技巧 —— 5. NGINX实现金盾防火墙的功能(防CC)
  • 原文地址:https://www.cnblogs.com/TestMa/p/10649052.html
Copyright © 2020-2023  润新知