SpringBoot(1)

以SpringBoot官方文档为原型,阐述SpringBoot。仅供学习参考,如有错漏之处,望多包涵。

1.Spring Boot 入门

1.1 构建SpringBoot项目

构建springboot的方法有很多,如:maven\gradle\springboot-cli等。
介绍一下我常用的方法:
1、使用IDEA new->project



2、选择 spring Initializr(spring初始化),设置JDK,一路next



3、选择初始化导入的依赖,按需选择

4、finished,喝杯咖啡等IDEA下载依赖就好了。

1.2. 包结构规范

经典的包结构:

com
 +- example
     +- myapplication
         +- Application.java
         |
         +- customer
         |   +- Customer.java
         |   +- CustomerController.java
         |   +- CustomerService.java
         |   +- CustomerRepository.java
         |
         +- order
             +- Order.java
             +- OrderController.java
             +- OrderService.java
             +- OrderRepository.java

强烈推荐将主应用类放在其它类之上的根包中。如果您的主类在根包中,可以使用@SpringBootApplication 注解,它隐式定义了某些项目的包搜索的基准起点。当然,你也可以通过设置属性,自定义扫描包的范围。
提示

  • @SpringBootApplication = (默认属性)@Configuration + @EnableAutoConfiguration + @ComponentScan。

1.3. 配置类

1.3.1. 导入额外的配置类

SpringBoot不推荐使用XML配置,额外的配置类可以通过@Import导入,或者可以在@ComponentScan的扫描范围内,添加@Configuration类。

1.3.2. 禁用指定的自动配置类

SpringBoot有一套默认的自动配置类,如果你发现不需要指定的配置类,可以通过使用 @EnableAutoConfigurationexclude 属性来禁用它们。

@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class MyConfiguration {
}

如果类不在 classpath 下,您可以使用注解的excludeName 属性并指定完全类名。最后,您还可以通过 spring.autoconfigure.exclude property 控制要排除的自动配置类列表。

1.4.Spring Bean与依赖注入

如果您按照上述最佳实践(将应用类放在根包中)来组织代码,则可以添加无参的 @ComponentScan。所有应用组件(@Component、@Service、@Repository、@Controller 等)将自动注册为 Spring Bean。
如何获取Spring Bean?
通常使用@Autowired获取Bean。

@Component
public class DatabaseAccountController {

    @Autowired
    private RiskAssessService riskAssessService;

    // ...
}

如果说一个项目出现了相同名字的Bean,那么就要给其中一个设置另外的name,@Autowired结合@Qualifier获取。

@Component
public class DatabaseAccountController {

    @Autowired
    @Qualifier("RAssessService")
    private RiskAssessService riskAssessService;

    // ...
}

@Service("RAssessService")
public class RiskAssessService {
    // ...
}

也可以通过构造注入所需Bean。

@Service
public class RiskAssessService  {

   private final RiskAssessor riskAssessor;

   @Autowired
   public DatabaseAccountService(RiskAssessor riskAssessor) {
       this.riskAssessor = riskAssessor;
   }

   // ...
}

提示

  • 如果 bean 中只有一个构造方法,您可以忽略掉 @Autowired 注解。
  • 构造注入允许 riskAssessor 字段被修饰为 final,这表示以后它不能被更改

1.5.运行你的应用

在您使用IDEA或Eclipse,Maven构建项目的前提下,您可以通过开发工具轻松运行您的应用。您也可以通过命令行运行应用。例如:

$ java -jar target/myapplication-0.0.1-SNAPSHOT.jar

提示

  • target指向您项目的路径。
  • myapplication-0.0.1-SNAPSHOT.jar是您打包应用的名称。您可以通过mvn package命令行打包应用。

也可以在运行打包应用程序时开启远程调试支持。该功能允许您将调试器附加到打包的应用中。

$ java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n \
       -jar target/myapplication-0.0.1-SNAPSHOT.jar

使用maven插件。

mvn spring-boot:run    

1.6.热插拔

java应用热插拔,即JVM热插拔,应用更新不需要重启就能获得相应的效果。JVM 热插拔在可替换字节码方面有所限制。想要更完整的解决方案,可以使用 JRebel

1.7.spring-boot-devtools

详情参考开发者工具


2. Spring Boot的特性

2.1. 自定义SpringApplication

如果您想自定义SpringBoot应用的初始化配置,您可以创建本地实例定制化。例如,关闭banner:

public static void main(String[] args) {
    SpringApplication app = new SpringApplication(MySpringConfiguration.class);
    app.setBannerMode(Banner.Mode.OFF);
    app.run(args);
}

或者,优雅的使用流式构建器API(Fluent Builder API),

new SpringApplicationBuilder()
        .sources(Parent.class)
        .child(Application.class)
        .bannerMode(Banner.Mode.OFF)
        .run(args);

也可以使用 application.properties 文件外部化配置。
关于配置选项的完整列表,请参阅 SpringApplication Javadoc
关于SpringApplicationBuilder详细信息,请参阅 SpringApplicationBuilder Javadoc

2.2. 自定义banner

banner文件是用来定义应用启动日志输出的。在 classpath 下添加一个 banner.txt 文件,或者将 spring.banner.location 属性指向该文件的位置来更改启动时打印的 banner。如果文件采用了非 UTF-8 编码,您可以设置 spring.banner.charset 来解决。除了文本文件,您还可以将 banner.gif、banner.jpg 或者 banner.png 图片文件添加到 classpath 下,或者设置 spring.banner.image.location 属性。指定的图片将会被转换成 ASCII 形式并打印在 banner 文本上方。

您可以在 banner.txt 文件中使用以下占位符:

变量 描述
${application.version} 您的应用版本号,声明在 MANIFEST.MF 中。例如,Implementation-Version: 1.0 将被打印为 1.0
${application.formatted-version} 您的应用版本号,声明在 MANIFEST.MF 中,格式化之后打印(用括号括起来,以 v 为前缀),例如 (v1.0)。
${spring-boot.version} 您使用的 Spring Boot 版本。例如 2.1.1.RELEASE.
${spring-boot.formatted-version} 您使用的 Spring Boot 版本格式化之后显示(用括号括起来,以 v 为前缀)。例如 (v2.1.1.RELEASE)。
${Ansi.NAME}(或 ${AnsiColor.NAME}${AnsiBackground.NAME}${AnsiStyle.NAME} 其中 NAME 是 ANSI 转义码的名称。有关详细信息,请参阅 AnsiPropertySource
${application.title} 您的应用标题,声明在 MANIFEST.MF 中,例如 Implementation-Title: MyApp 打印为 MyApp

注意

  • 如果您想以编程的方式生成 banner,可以使用 SpringApplication.setBanner(​...) 方法。使用 org.springframework.boot.Banner 接口并实现自己的 printBanner() 方法。打印的 banner 被注册名为 springBootBanner 的单例 bean。
  • 您还可以使用 spring.main.banner-mode 属性来确定是否必须在 System.out(console)上打印 banner,还是使用日志记录器(log)或者都不打印(off)。YAML 将 off 映射为 false,因此如果要禁用应用程序 banner,请确保属性添加引号。
spring:
    main:
        banner-mode: "off"

2.3. 应用事件与监听器

Spring Framework事件都继承了抽象类 org.springframework.context.ApplicationEvent,诸如:ContextRefreshedEvent等。
注意

ApplicationContext 创建之前,实际上触发了一些事件,因此您不能像 @Bean 一样注册监听器。您可以通过 SpringApplication.addListeners(​...) 或者 SpringApplicationBuilder.listeners(...​) 方法注册它们。如果您希望无论应用使用何种创建方式都能自动注册这些监听器,您都可以将 META-INF/spring.factories 文件添加到项目中,并使用 org.springframework.context.ApplicationListener 属性键指向您的监听器。比如:org.springframework.context.ApplicationListener=com.example.project.MyListener

当您运行应用时,应用程序事件将按照以下顺序发送:

  1. 在开始应用开始运行但还没有进行任何处理时(除了注册监听器和初始化器[initializer]),将发送 ApplicationStartingEvent。
  2. 当 Environment 被上下文使用,但是在上下文创建之前,将发送 ApplicationEnvironmentPreparedEvent。
  3. 在开始刷新之前,bean 定义被加载之后发送 ApplicationPreparedEvent。
  4. 在上下文刷新之后且所有的应用和命令行运行器(command-line runner)被调用之前发送 ApplicationStartedEvent。
  5. 在应用程序和命令行运行器(command-line runner)被调用之后,将发出 ApplicationReadyEvent,该事件用于通知应用已经准备处理请求。
  6. 如果启动时发生异常,将发送 ApplicationFailedEvent。

应用程序事件发送使用了 Spring Framework 的事件发布机制。该部分机制确保在子上下文中发布给监听器的事件也会发布给所有祖先上下文中的监听器。因此,如果您的应用程序使用有层级结构的 SpringApplication 实例,则监听器可能会收到同种类型应用程序事件的多个实例。

为了让监听器能够区分其上下文事件和后代上下文事件,您应该注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。可以通过实现 ApplicationContextAware 来注入上下文,如果监听器是 bean,则使用 @Autowired 注入上下文。

提示

您可能不会经常使用应用程序事件,但了解他们的存在还是很有必要的。在框架内部,Spring Boot 使用这些事件来处理各种任务。

2.4.外部化配置

为了适配多环境的部署。Spring Boot可以通过properties 文件、YAML 文件、环境变量或者命令行参数来外部化配置。可以使用 @Value 注解将属性值直接注入到 bean 中,可通过 Spring 的 Environment 访问,或者通过 @ConfigurationProperties 绑定到结构化对象。

Spring Boot使用了PropertySource指令,由于智能覆盖默认值。属性值将按照以下顺序加载:

  1. 在您的主目录(当 devtools 被激活,则为 ~/.spring-boot-devtools.properties )中的 Devtools 全局设置属性。
  2. 在测试中使用到的 @TestPropertySource 注解。
  3. 在测试中使用到的 properties 属性,可以是 @SpringBootTest 和用于测试应用程序某部分的测试注解。
  4. 命令行参数。
  5. 来自 SPRING_APPLICATION_JSON 的属性(嵌入在环境变量或者系统属性【system propert】中的内联 JSON)。
  6. ServletConfig 初始化参数。
  7. ServletContext 初始化参数。
  8. 来自 java:comp/env 的 JNDI 属性。
  9. Java 系统属性(System.getProperties())。
  10. 操作系统环境变量。
  11. 只有 random.* 属性的 RandomValuePropertySource
  12. 在已打包的 jar 外部的指定 profile 的应用属性文件(application-{profile}.properties 和 YAML 变量)。
  13. 在已打包的 jar 内部的指定 profile 的应用属性文件(application-{profile}.properties 和 YAML 变量)。
  14. 在已打包的 jar 外部的应用属性文件(application.properties 和 YAML 变量)。
  15. 在已打包的 jar 内部的应用属性文件(application.properties 和 YAML 变量)。
  16. @Configuration 类上的 @PropertySource 注解。
  17. 默认属性(使用 SpringApplication.setDefaultProperties 指定)。

2.4.1. 配置随机值

RandomValuePropertySource 对于随机值注入非常有用(比如在保密场景或者测试用例中)。它可以产生 integer、long、uuid 和 string。如下示例:

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]}

2.4.2. 访问命令行属性

默认情况下,SpringApplication 将所有命令行选项参数(即以 -- 开头的参数,比如 --server.port=9000)转换为属性,并将它们添加到 Spring Environment 中。如之前所述,命令行属性始终优先于其他属性源。

如果您不希望将命令行属性添加到 Environment,可以使用 SpringApplication.setAddCommandLineProperties(false)来禁用它们。

2.4.3. 应用程序属性文件

SpringApplication 从以下位置的 application.properties 文件中加载属性(properties),并将它们添加到 Spring Environment 中:

  1. 当前目录/config 子目录
  2. 当前目录
  3. classpath 上的 /config 包
  4. classpath 根路径
    列表按序号优先级排序,序号越小,优先级越高。
    用于多环境配置,如下图:



    如果您不喜欢 application.properties 作为配置文件名,则可以通过指定 spring.config.name 环境属性来切换到另一个文件名。您还可以使用 spring.config.location 环境属性来引用一个显式位置(以逗号分隔的目录位置或文件路径列表)。以下示例展示了如何指定其他文件名:

$ java -jar myproject.jar --spring.config.name=myproject

以下示例展示了如何指定两个位置:

$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

2.4.3. 属性文件中的占位符

application.properties 中的值在使用时通过现有的 Environment 进行过滤,因此您可以返回之前定义的值(例如,从系统属性)。

app.name=MyApp
app.description=${app.name} is a Spring Boot application

2.4.4. 使用 YAML 代替属性文件

YAML 是 JSON 的超集,是一个可用于指定层级配置数据的便捷格式。只要在 classpath 上有 SnakeYAML库,SpringApplication 类就会自动支持 YAML 作为属性文件(properties)的替代。
注意

如果使用 starter,则 spring-boot-starter 会自动提供 SnakeYAML。
通常YAML文件后缀名用 .yml。

2.4.4.1. 加载YAML

例如以下文件:

environments:
  dev:
    url: http://dev.example.com
    name: Developer Setup
my:
  servers:
    - dev.example.com
    - another.example.com

转换为以下属性(properties):

environments.dev.url=http://dev.example.com
environments.dev.name=Developer Setup
my.servers[0]=dev.example.com
my.servers[1]=another.example.com

YAML以行缩进的形式表示层级关系。

2.4.4.2. 多profile YAML文档

spring.profiles key 在单个文件中指定多个特定 profile 的 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

在前面示例中,如果 development profile 处于激活状态,则 server.address 属性得值为 127.0.0.1。 同样,如果 production 和 eu-central profile 处于激活状态,则 server.address 属性的值为 192.168.1.120。如果未激活 development、production 或 eu-central profile,则该属性的值为 192.168.1.100。
注意

profile 表达式允许表达更复杂的 profile 逻辑,例如 production & (eu-central | eu-west)。有关详细信息,请查阅参考指南

2.4.4.3. YAML 的缺点

无法使用 @PropertySource 注解加载 YAML 文件。因此,如果您需要以这种方式加载值,请使用属性文件(properties)。

2.4.5. 类型安全的配置属性

使用 @Value("${property}") 注解来注入配置属性有时会很麻烦,特别是如果您使用了多个属性或者您的数据本质上是分层结构。Spring Boot 提供了另一种使用属性的方法,该方法使用强类型的 bean 来管理和验证应用程序的配置,如下所示:

@ConfigurationProperties("acme")
public class AcmeProperties {

    private boolean enabled;

    private InetAddress remoteAddress;

    private final Security security = new Security();

    public boolean isEnabled() { ... }

    public void setEnabled(boolean enabled) { ... }

    public InetAddress getRemoteAddress() { ... }

    public void setRemoteAddress(InetAddress remoteAddress) { ... }

    public Security getSecurity() { ... }

    public static class Security {

        private String username;

        private String password;

        private List<String> roles = new ArrayList<>(Collections.singleton("USER"));

        public String getUsername() { ... }

        public void setUsername(String username) { ... }

        public String getPassword() { ... }

        public void setPassword(String password) { ... }

        public List<String> getRoles() { ... }

        public void setRoles(List<String> roles) { ... }

    }
}

前面的 POJO 定义了以下属性:

  • acme.enabled,默认值为 false
  • acme.remote-address,可以从 String 强制转换的类型。
  • acme.security.username,内嵌一个 security 对象,其名称由属性名称决定。特别是,返回类型根本没有使用,可能是 SecurityProperties。
  • acme.security.password
  • acme.security.roles,String 集合。

@Value@ConfigurationProperties 之间的差异

功能 @ConfigurationProperties @Value
宽松绑定
元数据支持
SpEL 表达式

同时,不要忘了注册属性类,如下所示:

@Configuration
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}

Bean方式配置属性,与外部YAML配置,有异曲同工之妙。

# application.yml

acme:
    remote-address: 192.168.1.1
    security:
        username: admin
        roles:
          - USER
          - ADMIN

要使用@ConfigurationProperties bean,您可以使用与其他 bean 相同的方式注入它们,如下所示:

@Service
public class MyService {
  
    @Autowired
    private final AcmeProperties properties;

    //...  
}

提示

使用 @ConfigurationProperties 还可以生成元数据文件,IDE 可以通过这些文件来为您自己的 key 提供自动完成功能。有关详细信息,请参阅附录 B:配置元数据

2.4.6. 宽松绑定

Spring Boot 使用一些宽松的规则将 Environment 属性绑定到 @ConfigurationProperties bean,因此 Environment 属性名不需要和 bean 属性名精确匹配。常见的示例包括使用了 - 符号分割的环境属性(例如,context-path 绑定到 contextPath)和大写环境属性(例如,PORT 绑定到 port)。
注意

注解的 prefix 值必须是 kebab (短横线命名)风格(小写并用 - 分隔,例如 acme.my-project.person)。

表2.4.6.1 每种属性源(property source)的宽松绑定规则

属性源 简单类型 列表集合类型
properties 文件 驼峰式、短横线式或下划线式 标准列表语法使用 [] 或逗号分隔值
YAML 文件 驼峰式、短横线式或者下划线式 标准 YAML 列表语法或者逗号分隔值
环境变量 大写并且以下划线作为定界符,_ 不能放在属性名之间使用 数字值两边使用下划线连接,例如 MY_ACME_1_OTHER = my.acme[1].other
系统属性 驼峰式、短横线式或者下划线式 标准列表语法使用 [] 或逗号分隔值

提示

Spring Boot建议,属性尽可能以kebab (短横线命名)风格存储,比如 my.property-name=acme

未完待续~