(五)Spring Boot官网文档学习


SpringApplication

它提供了一个方便的方法,通过 mian() 方法入口启动一个 Spring 项目,如下所示:

public static void main(String[] args) {
SpringApplication.run(MySpringConfiguration.class, args);
}

启动的时候,控制台输出如下欢迎页面:

  .   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.5.RELEASE)

这个图形是可以更改的,可以在类路径下面新建一个文件,名字必须是:banner.txt ,或者通过 spring.banner.location 属性配置,如果文件的编码不是 UTF8,可以通过 spring.banner.charset 属性设置编码;

这样该文件的内容就会被当做欢迎页面;

(五)Spring Boot官网文档学习

文件内容:

(五)Spring Boot官网文档学习

启动项目,欢迎页面:

Connected to the target VM, address: '127.0.0.1:56396', transport: 'socket'
a b
a b
a b
a b

此外,也可以选择图片当作欢迎页面,只是图片的名字必须是 banner ,还是将图片添加到类路径下面,或者通过 spring.banner.image.location 设置,这样图片就会被转为 ASCII 字符画 (博主,测试下不清楚,基本看不出原图片是啥);

关于欢迎页面,Spring 官网 给出了更多的设置,感兴趣,可以自己去看下,就不再介绍了;


SpringApplication 事件

除了平常的 Spring 事件,SpringApplication 还会发送一系列额外的应用事件;一些事件在 ApplicationContext 被创建之前发生,所以不能在 ApplicationContext 里面注册监听器,监听这些事件;

一般使用 SpringApplication.addListeners(…​) methodSpringApplicationBuilder.listeners(…​) 方法注册;

如果你想监听器被自动的注册,而不想去关心应用创建的方法,可以创建文件 META-INF/spring.factories 在里面引用监听器:

org.springframework.context.ApplicationListener=com.example.project.MyListener

相关的应用事件的顺序:

  1. ApplicationStartingEvent 在运行之前,监听器注册初始化以后,发生;
  2. ApplicationEnvironmentPreparedEvent 在上下文环境已经被使用之后,在上下文产生之前,发生;
  3. ApplicationPreparedEvent 在刷新之前,在加载 bean 之后,发生 ;
  4. ApplicationStartedEvent 在上下文刷新之后,在调用任何应用/命令行程序之前,发生;
  5. ApplicationReadyEvent 在调用任何应用/命令行程序之前,发生。它表示应用程序已经准备好为请求提供服务 ;
  6. ApplicationFailedEvent 在启动失败的时候,发生;

ApplicationContext 类型

SpringApplication 会试图去创建正确类型的 ApplicationContext

具体怎么确定正确类型的算法是很简单的e:

  1. 如果 SpringMvc 存在,则使用 AnnotationConfigServletWebServerApplicationContext
  2. 如果 SpringMvc 不存在,但是 Spring WebFlux 存在,则使用 AnnotationConfigReactiveWebServerApplicationContext
  3. 否则使用 AnnotationConfigApplicationContext

从中可以看出,SpringMvcSpring WebFlux 一起存在的时候,默认是按照 SpringMvc l来选取正确的类型的 ;

如果想更改,则自定义 SpringApplication 进行覆盖:

@SpringBootApplication
public class Application { public static void main(String[] args) {
// 自定义 SpringApplication
SpringApplication springApplication = new SpringApplication();
// 设置 ApplicationContext 类型
springApplication.setWebApplicationType(WebApplicationType); }
}

也可以调用 setApplicationContextClass() 方法,完全控制 ApplicationContext 类型;

如果使用 Junit 的时候,则把 ApplicationContext 的类型设置为 NONE

setWebApplicationType(WebApplicationType.NONE)

访问传递给 SpringApplication 的参数

我们获得传递 SpringApplication.run(...) 方法的参数,通过注入一个 org.springframework.boot.ApplicationArguments 对象;

import org.springframework.boot.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*; @Component
public class MyBean { @Autowired
public MyBean(ApplicationArguments args) {
boolean debug = args.containsOption("debug");
List<String> files = args.getNonOptionArgs();
// if run with "--debug logfile.txt" debug=true, files=["logfile.txt"]
} }

Spring BootSpring环境中注册了一个 CommandLinePropertySource 对象,这样,我们就可以使用 @Value 注解,向应用程序注入参数 ;


ApplicationRunner or CommandLineRunner

如果需要在SpringApplication 启动后运行一些特殊代码,我们可以通过实现 ApplicationRunner or CommandLineRunner 这两个接口中的一个,它们有着相同的工作方式,并且都有一个方法 run( ) ,这个方法在 SpringApplication.run(…​) 执行完成之前,被调用;

其中 CommandLineRunner 接口,将应用程序的参数,当作一组字符串数组:

@Component
class MyBean implements CommandLineRunner {
@Override
public void run(String... args) throws Exception { }
}

ApplicationRunner 则使用上面说的 org.springframework.boot.ApplicationArguments 对象 :

@Component
class MyBean implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception { }
}

如果有多个 CommandLineRunner or ApplicationRunner 对象被定义,并且他们需要安装一定的顺序得到执行,我们需要实现额外的接口 org.springframework.core.Ordered ,或者实现 org.springframework.core.annotation.Order 接口 ;

@Component
class MyBean implements ApplicationRunner,Order { @Override
public void run(ApplicationArguments args) throws Exception { } @Override
public int value() {
return 1;
} @Override
public Class<? extends Annotation> annotationType() {
return null;
}
}

或者直接使用注解:

@Order(value = 1)
class MyBean implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception { }
}

ExitCodeGenerator应用

每个 SpringApplication 都注册了一个 钩子JVM 中,确保在关闭 JVM 的时候,SpringApplication 可以正常退出;

Bean 还可以实现一个接口,org.springframework.boot.ExitCodeGenerator ,可以在应用程序退出的时候,返回一个我们自定义的状态码;

具体就是在 SpringApplication.exit() 被调用的时候,就会返回我们实现的接口中的状态码,然后传给 System.exit() 当中虚拟机退出的状态码;

@SpringBootApplication
public class Application implements ExitCodeGenerator { public static void main(String[] args) {
// SpringApplication.run(Application.class, args);
System.exit(SpringApplication.exit(SpringApplication.run(Application.class, args))); } @Override
public int getExitCode() {
return 10086;
} }

还可以把 ExitCodeGenerator 接口通过异常类实现,这样触发异常的时候,我们就在异常的构造器里面写上:

System.exit(SpringApplication.exit(上下文)));

就可以返回我们自定义的状态码 ;


配置文件

Spring Boot 允许我们进行许多的外部配置,以保证我们在不同的环境下运行代码,不需要要改动代码,只需要改动配置;

配置的值,可以使用@Value 注入到 Bean 里面,或者通过 @ConfigurationProperties 绑定到结构化的对象中;

配置文件配置的属性有先后关系,有覆盖关系,Spring Boot 的配置文件先后关系如下:

优先级从高到低:

(五)Spring Boot官网文档学习

获取配置文件中配置的属性值,比如获取 name 属性:

import org.springframework.stereotype.*;
import org.springframework.beans.factory.annotation.*; @Component
public class MyBean {
// 获取name属性
@Value("${name}")
private String name; // ... }
  • 配置随机数

    my.secret=${random.value}
    my.number=${random.int}
    my.bignumber=${random.long}
    my.uuid=${random.uuid}
    my.number.less.than.ten=${random.int(10)}
    my.number.in.range=${random.int[1024,65536]}

    我们可以将我们需要的一些随机数,配置在这里,前面四个,在程序运行期间只会产生一个相同的值,即一个程序多个访问,返回同一个随机值,重启程序以后,就会重新产生一个随机值;

    后面两个,每次都产生不同的随机数;

  • 获取命令行属性

    默认,SpringApplication 将命令行参数(即以 -- 开头的参数:--server.port = 9000),转化为属性,添加到 Spring 环境中;

    前面贴的图,上面标识了各个配置的优先级,其中命令行参数配置在第四个,优先于生产中使用的配置文件,因此,可以使用 SpringApplication.setAddCommandLineProperties(false). 禁止SpringApplication 将命令行参数,转化为属性 ;

  • application.properties 文件

    SpringApplication 会从下面几个地方加载配置;

    1、项目根文件夹下面的 config 文件夹

    2、项目根文件夹下面

    3、classpath 路径下面的 config 文件夹

    4、classpath 路径下面

    优先级,从高到低 ;

    也可以使用 application.yml 做配置文件`;


多个配置文件

默认的配置文件名字:application.properties/yml ,我们可以按照 application-【profile】.properties/yml 格式新建许多的配置文件;

但是新建出来的配置文件,必须和 application.properties/yml 放在一个文件夹下面,application.properties/yml 可以看成是配置文件的入口,我们自定义的配置文件,还需要在这里面使用 spring.profiles.active 进行引用一下(多文件之间用逗号分开):

spring.profiles.active=自定义配置文件名字(不加后缀)

这里记住,我们自定义的配置文件,总是会覆盖掉 application.properties/yml 里面的同名属性的,也就是优先级高于 application.properties/yml,前提是使用 spring.profiles.active 启用了我们自定义的配置文件;

至于多个自定义文件之间的优先级,spring.profiles.active 谁最后引用,谁优先级最高;

最后,多个配置文件,由于存在覆盖现象,其实最后加载的属性,就跟一个大的配置文件一样;


配置文件中的占位符

可以使用 ${}

name = 12
name11=${name}1

YAML 配置文件

官网推荐我们使用 .yml 替代使用 .properties

YAMLJSON 的超集,更适合表达配置之间的层级关系;

如果想要使用 YAML ,需要添加它的依赖 SnakeYAML ,如果使用了 spring-boot-starter 启动器,则默认添加了该依赖 ;

  1. 加载 YAML 文件

    首先 Spring Boot 默认会主动加载 src/main/resource 路径下,名字为: application.ymlyml 文件 ;

    此外,我们的 yml 文件放在其他地方,Spring 框架本身也提供了两种方法加载 YAML 文件,一种是利用 YamlPropertiesFactoryBeanYAML 加载为一个 Properties 对象,这种方式,将会将所有属性都当作String处理;

    如果上面的 YamlPropertiesFactoryBean 将所有属性都当作字符串,不能满足你的要求,那就用 YamlMapFactoryBeanYAML 加载为一个 Map 对象 ;

    YAML- 表示为下标:

    my:
    servers:
    - dev.example.com
    - another.example.com // 上面的 yml 格式,被转换为下面的形式 ; my.servers[0]=dev.example.com
    my.servers[1]=another.example.com

    示例代码,使用 YamlPropertiesFactoryBean 加载:

     // 加载YML格式自定义配置文件
    @Bean
    public static PropertySourcesPlaceholderConfigurer properties() {
    PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
    YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
    // 如果加载多个文件,则传入 ClassPathResource 数组
    yaml.setResources(new ClassPathResource("config/application-a.yml"));//class引入
    configurer.setProperties(yaml.getObject());
    return configurer;
    }
  2. 获取 yml 配置文件的属性值

    当我们加载过 yml 文件以后,还可以直接将配置文件中的属性绑定到 List Set 集合里面,通过Spring Boot@ConfigurationProperties 注解;

    写好 prefix 属性,它表示你要加载属性的前缀,然后还要保证 属性名字和 配置文件里面的一样 ,再为要获取的属性,添加对应的 setter 方法;

    比如要获取上面 yml 文件中的 services

    @Component
    @ConfigurationProperties(prefix = "my")
    public class Config { public void setServers(List<String> servers) {
    this.servers = servers;
    } private List<String> servers = new ArrayList<String>(); public List<String> getServers() {
    return this.servers;
    } }

    就可以将 yml文件中的 services 属性,封装到 list 集合里面,这是一种获取配置文件属性的方法;

    还可以通过使用 @Value 注解,访问 yml 配置文件的属性,但是要写配置文件中的key 的全部名称 :

     @Value("${number.int.five}")
    private int num ;
  3. 全局化 yml 属性

    官网说可以使用 YamlPropertySourceLoader 来加载 yml 文件,然后添加到 Spring 环境中:

    package hello.config;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.env.EnvironmentPostProcessor;
    import org.springframework.boot.env.YamlPropertySourceLoader;
    import org.springframework.core.env.ConfigurableEnvironment;
    import org.springframework.core.env.PropertySource;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource; import java.io.IOException; public class EnvironmentPostProcessorExample implements EnvironmentPostProcessor { private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader(); @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
    SpringApplication application) {
    // 路径记得改为你自己的路径
    Resource path = new ClassPathResource("config/application.yml");
    PropertySource<?> propertySource = loadYaml(path);
    environment.getPropertySources().addLast(propertySource);
    } private PropertySource<?> loadYaml(Resource path) {
    if (!path.exists()) {
    throw new IllegalArgumentException("Resource " + path + " does not exist");
    }
    try {
    return this.loader.load("custom-resource", path).get(0);
    } catch (IOException ex) {
    throw new IllegalStateException(
    "Failed to load yaml configuration from " + path, ex);
    }
    } }

    然后还需要配置一个东西:

    (五)Spring Boot官网文档学习

    class path 路径下面,新建 /META-INF/spring.factories 文件,在里面注册下我们刚刚的类:

    org.springframework.boot.env.EnvironmentPostProcessor=hello.config.EnvironmentPostProcessorExample

    我没发现,和自己自定义加载有什么不同。没搞懂 Spring 环境是什么意思;

  4. 一个 yml 文件配置多个部分属性

    我们可以使用 spring.profiles 键,作为分割,在一个文件里面,配置多个场景属性:

    YAML 中使用 --- 分割不同部分;

    server:
    address: 192.168.1.100
    ---
    spring:
    profiles: development
    server:
    address: 127.0.0.1
    ---
    spring:
    profiles: production & eu-central
    server:
    address: 192.168.1.120

    比如上面的配置,当 profilesdevelopment 的时候,IP127.0.0.1;当 profilesproduction & eu-central 的时候,IP192.168.1.120;当 profiles 既不是 development,也不是 production & eu-central 的时候,IP192.168.1.100

    这里的 spring: profiles: 的值是个表达式,具体支持符号有 ()|& ,如果要混用 & | 必须使用 () ,比如 : production & us-east | eu-central 不是有效的表达式。它必须表达为 production & (us-east | eu-central)

    还可以配置一个默认的配置,如果项目启用以后没有特殊配置,则选用这个默认的,比如,配置一个默认米密码:

    server:
    port: 8000
    ---
    spring:
    profiles: default
    security:
    user:
    password: weak

    这里一定要写上 profiles: default,否则就不是默认配置了,就是妥妥的配置,选用其它配置,密码还会是 weak 的;

    profiles 不要产生二义性

  5. 缺点

    官网原文如下:

    24.7.4 YAML Shortcomings

    YAML files cannot be loaded by using the @PropertySource annotation. So, in the case that you need to load values that way, you need to use a properties file.

    说我们不能再使用 @PropertySource 注解,加载 YAML 文件了,@PropertySource 只能用来加载 .properties 文件了 ;


上一篇:UIWebView禁止点击后跳转


下一篇:【OpenStack】OpenStack系列17之OpenStack私有云设计一