Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

Spring Boot学习笔记

5
Sep
2017

Spring Boot学习笔记

By Alex
/ in Java
/ tags Spring, 学习笔记
0 Comments
简介

Spring Boot是Spring的一个子项目,它让创建独立运行(通过java -jar)的、产品级别的Spring应用变得简单:

  1. 支持创建独立运行于JVM中的Spring程序
  2. 内嵌Tomcat、Jetty或者Undertow,不需要部署War包
  3. 简化Maven的POM配置
  4. 在任何可能的情况下,自动配置Spring
  5. 提供产品级特性,包括性能度量、健康检测、外部化配置
  6. 不需要任何代码生成或者XML配置
新特性
2.0
  1. 支持Java 9,至少要求Java 8
  2. 基于 Spring 5 构建,Spring 的新特性均可以在 Spring Boot 2.0 中使用
  3. 为各种组件的响应式编程提供了自动化配置,如:Reactive Spring Data、Reactive Spring Security 等
  4. 支持 Spring MVC 的非阻塞式替代方案 WebFlux 以及嵌入式 Netty Server
  5. 对 HTTP/2 的支持
  6. 更灵活的属性绑定API,不通过 @ConfigurationProperties注解就能实现配置内容读取和使用
  7. Spring Security 整合的简化
  8. Gradle 插件增强,要求Gradle版本4.4+
  9. Starter整合的第三方组件版本升级:
    1. Tomcat 升级至 9.0,Servlet版本4.0
    2. Jetty 升级至 9.4,Servlet版本3.1
    3. Flyway 升级至 5
    4. Hibernate 升级至 5.2
    5. Thymeleaf 升级至 3
起步

本章我们创建一个简单的基于Spring Boot的Web应用。

Maven依赖

首先设置父项目:

XML
1
2
3
4
5
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.6.RELEASE</version>
</parent> 

然后添加Web的Starter依赖:

XML
1
2
3
4
5
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.1.6.RELEASE</version>
</dependency>

只需要这一个依赖就可以了,开发Spring MVC项目所需要的繁杂依赖项不需要手工指定,均使用spring-boot-starter-web的传递依赖即可。

最后配置一下Maven插件,打包为可执行JAR:

XML
1
2
3
4
5
6
7
8
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build> 
HelloWorld
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package cc.gmem.study.spring;
 
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
 
@Controller
// 让Spring Boot猜测应该怎么样配置Spring(基于添加的依赖)
@EnableAutoConfiguration
public class SampleController {
 
    @RequestMapping( "/" )
    @ResponseBody
    String home() {
        return "Hello World!";
    }
 
    public static void main( String[] args ) throws Exception {
        System.getProperties().put( "server.port", 9090 );
        SpringApplication.run( SampleController.class, args );
    }
}

运行此程序,然后打开浏览器访问http://localhost:9090,即可访问。

安装
基于Maven
pom.xml
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<project>
    <!--
        继承基础配置:
        1、使用JDK 1.6作为默认编译级别
        2、使用UTF-8作为源码的编码方式
    -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.6.RELEASE</version>
    </parent>
    <!-- 可以定制的配置 -->
    <properties>
        <java.version>1.8</java.version>
        <!-- 如果有多个main函数入口,需要指定一个start-class才能进行可执行JAR打包 -->
        <start-class>cc.gmem.study.spring.boot.HelloApp</start-class>
    </properties>
    <!-- Web应用典型依赖,包括了对Spring MVC和Tomcat的依赖 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    <!-- 打包为可执行的JAR -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

如果parent构件已经存在,可以这样引入基础配置:

XML
1
2
3
4
5
6
7
8
9
10
11
<dependencyManagement>
     <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>1.5.6.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
基于Gradle
build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
plugins {
    id 'org.springframework.boot' version '1.5.6.RELEASE'
    id 'java'
}
 
 
jar {
    baseName = 'myproject'
    version =  '0.0.1-SNAPSHOT'
}
 
repositories {
    jcenter()
}
 
dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    testCompile("org.springframework.boot:spring-boot-starter-test")
}
Starters

所谓Starter,就是一个依赖描述符,你可以包含它们,避免手工逐个配置依赖项。上面我们已经使用了两种Starter。常用的Starter如下表:

Starter 说明
spring-boot-starter 核心Starter,包含了自动配置支持、日志、YAML
spring-boot-starter-activemq 支持基于ActiveMQ的JMS应用
spring-boot-starter-amqp 支持Spring AMQP、Rabbit MQ
spring-boot-starter-aop 支持Spring AOP和AspectJ
spring-boot-starter-cache 支持Spring缓存机制
spring-boot-starter-data-jpa 支持JPA + Hibernate
spring-boot-starter-data-mongodb 支持MongoDB
spring-boot-starter-data-redis 支持Redis
spring-boot-starter-jdbc 支持基于Tomcat的JDBC连接池
spring-boot-starter-jersey 基于JAX-RS / Jersey构建RESTful的Web应用
spring-boot-starter-test 支持JUnit、Hamcrest、Mockito
spring-boot-starter-web 支持Web应用,包括RESTful,使用Tomcat作为默认内嵌容器
spring-boot-starter-web-services 支持Spring WebService
spring-boot-starter-websocket 支持WebSocket
spring-boot-starter-jetty
spring-boot-starter-tomcat
spring-boot-starter-undertow
内嵌容器依赖
spring-boot-starter-log4j2
spring-boot-starter-logging
日志依赖
使用Spring Boot
配置类

尽管你可以通过 SpringApplication.run()传递XML源,但是Spring Boot倾向于使用基于Java的配置。 即主要配置源应该是一个注解了 @Configuration的Java类,此类通常定义了main方法。

额外配置类

你可以使用 @Import注解引入额外的配置类。

你也可以使用 @ComponentScan自动扫描任何Spring组件,包括@ComponentScan本身。

如果要引入额外的XML配置,可以使用 @ImportResource注解。

自动配置

Spring Boot的自动配置机制,尝试根据当前项目的依赖,自动判断如何配置Spring上下文、运行容器、数据库。例如,如果HSQLDB位于类路径下,Spring Boot会自动配置一个内存数据库。

要启用自动配置,可以在主配置类上添加 @EnableAutoConfiguration或者 @SpringBootApplication注解。

如果要查看哪些自动配置被启用,为何被启用,可以为应用程序传入参数 --debug。

你可以明确的禁用某种自动配置:

Java
1
2
3
4
@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class Configuration {
}

注意:自动配置可以被渐进的替代。例如,当你手工配置了数据源后,Spring Boot就不会自动配置HSQLDB。

@SpringBootApplication

此注解等价于@Configuration, @EnableAutoConfiguration,@ComponentScan的组合。其中@ComponentScan为:

Java
1
2
3
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

由于没有为@ComponentScan指定basePackage,因此默认扫描当前类所在包及其子包。

Bean和注入

可以通过@ComponentScan来查找Bean,或者在配置类的方法中使用@Bean。@Component, @Service, @Repository, @Controller等注解都是支持的。

要为Bean注入字段,可以使用@Autowired,@Inject等注解。

运行

很多IDE,包括Eclipse、IDEA都提供了对Spring Boot应用程序的支持。

通过命令行运行的示例:

Shell
1
java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n -jar boot-study.jar

通过Maven运行的示例:

Shell
1
2
export MAVEN_OPTS=-Xmx1024m -XX:MaxPermSize=128M
mvn spring-boot:run

通过Gradle运行的示例:

Shell
1
gradle bootRun
热部署

Spring Boot仅仅是简单的Java应用程序,JVM的热交换机制直接可以使用。但是JVM热交换有所限制,更加完整的解决方案是JRebel或者Spring Load。模块spring-boot-devtools也提供了快速的应用重启机制。

静态内容重新载入

有多种实现方式,推荐使用spring-boot-devtools模块,因为它提供了额外的开发时特性——快速重启、LiveReload。DevTools的工作方式是监控类路径变化,这意味着你需要触发项目构建,在IDEA中可以调用Make Project触发(或者开启自动构建)。静态内容的变化不会导致应用重启,但是会导致LiveReload。

模板重新载入

Spring Boot支持的模板技术大部分支持禁用缓存的功能,这样模板被修改后可以立即被感知到。使用spring-boot-devtools的情况下,模板自动重新载入。

Java类重新载入

大部分IDE支持代码热替换,因此你对类进行非结构性的改变时,构建后Java类会自动更新。

Spring Loaded允许方法签名改变后的类仍然能够重新载入。

快速重启

spring-boot-devtools提供了自动的重启功能,其速度比JRebel、Spring Loaded慢,但是比冷启动快的多。

spring-boot-devtools的快速重启功能依赖于ClassLoader,打包好的第三方库,使用Base加载器加载,而正在开发的那些类则使用restart加载器加载。当快速重启发生后,之前的restart加载器被丢弃,新的restart加载器被创建。

DevTools

此模块提供一些开发时特性,要启用DevTools,引入:

XML
1
2
3
4
5
6
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <version>1.5.6.RELEASE</version>
    <optional>true</optional>
</dependency>

DevTools在生产环境下会自动禁用,生产环境的判断依据是:

  1. 以java -jar启动Spring Boot应用
  2. 或者,使用了特殊的ClassLoader 
默认属性

模板引擎具有缓存功能、Spring MVC也在HTTP头中添加了缓存字段。DevTools通过配置属性,禁用了这些功能。

自动重启

默认情况下,使用了DevTools的应用会在类路径发生变化时自动重启,注意模板、静态资源不会触发重启。具体来说,下列位置的资源不会触发重启:

/META-INF/maven, /META-INF/resources ,/resources ,/static ,/public和/templates

如果需要显式指定那些资源不会触发重启,配置下面的属性:

1
2
3
4
# 自己指定不触发的资源
spring.devtools.restart.exclude=static/**,public/**
# 在默认不触发资源目录列表的基础上,额外增加
spring.devtools.restart.additional-exclude=

如果需要额外指定会触发重启的资源,配置下面的属性:

1
spring.devtools.restart.additional-exclude=

如果你使用某种持续的自动编译、产生输出的IDE,并且期望在这些输出发生变化时,触发重启,配置下面的属性:

1
spring.devtools.restart.trigger-file= 

要禁用DevTools的重启功能,配置下面的属性:

1
spring.devtools.restart.enabled=false
restart加载器

配置下面的属性以确定哪些JAR包由restart加载器加载:

1
2
3
4
# 下面的JAR由Base加载器加载
restart.exclude.companycommonlibs=/mycorp-common-[\\w-]+\.jar
# 下面的JAR由restart加载器加载
restart.include.projectcommon=/mycorp-myproj-[\\w-]+\.jar
LiveReload

DevTools包含了一个内嵌的LiveReload服务器,可以用来在资源发生变化时触发浏览器刷新。各大浏览器均有LiveReload扩展。

如果要禁用LiveReload服务器,配置:

1
spring.devtools.livereload.enabled=false 
Spring Boot特性
SpringApplication

此类提供了一些便捷方法,用于Spring应用的自举。最简单的用法:

Java
1
SpringApplication.run(MyConfiguration.class, args);

可以使用链式调用进行定制化:

Java
1
2
3
4
5
new SpringApplicationBuilder()
        .sources(Parent.class)
        .child(Application.class)
        .bannerMode(Banner.Mode.OFF)
        .run(args); 
启动失败

启动后,控制台默认打印INFO级别的日志。

如果应用自举失败,已经注册的FailureAnalyzer会打印相关的信息,Spring Boot提供了很多FailureAnalyzer实现。 

如果FailureAnalyzer没有提供有价值的信息,你可以设置应用程序参数 --debug或者设置org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer的日志级别。

定制Banner

你可以在类路径下放置一个banner.txt文件,或者设置banner.location来指定任意文件。你还可以在类路径下添加banner.png|gif|jpg等图片,这些图片会被转换为ASCII(透明的PNG背景被转换为黑色),打印在文本Banner之前。

应用事件

除了ContextRefreshedEvent,SpringApplication会发布很多额外的事件:

事件 说明
ApplicationStartingEvent 开始启动时发布,此时仅仅注册了listeners、initializers,尚未进行其它处理
ApplicationEnvironmentPreparedEvent Spring context需要的Environment准备好之后调用,此时Context尚未创建
ApplicationPreparedEvent Bean定义已经全部加载,即将刷新Context
ApplicationReadyEvent Context刷新完毕,相关的回调已经被调用
ApplicationFailedEvent 启动失败后发布
Web环境

SpringApplication会自动尝试创建合适的ApplicationContext实现类。默认的:

  1. 如果当前应用是Web应用,创建AnnotationConfigEmbeddedWebApplicationContext
  2. 否则创建AnnotationConfigApplicationContext

你可以调用setWebEnvironment()来强制指定是否Web应用。

ApplicationArguments

你可以注入此类型,以访问传递给SpringApplication.run的参数。

配置外部化

Environment对象是配置信息的容器。

Spring Boot允许你把配置信息外部化,这样同一套代码就可以很容易的在不同环境下运行。

你可以使用YAML 、环境变量、命令行参数来指定配置。

使用 @Value注解可以直接注入配置参数,也可以通过Environment来访问,或者通过@ConfigurationProperties绑定到结构化对象。例如:

Java
1
2
3
4
5
6
@Component
public class Bean {
    @Value("${name}")
    private String name;
 
}

如果类路径下的application.properties文件中有name这个键,或者命令行指定了--name参数,则会被注入到上述Bean的属性中。

配置优先级

Spring使用PropertySource来定位配置属性,它会从以下位置寻找(优先级从高到低):

  1. 当使用DevTools时,DevTools全局设置,位于~/.spring-boot-devtools.properties
  2. 位于测试用例上的@TestPropertySource注解
  3. 位于测试用例上的@SpringBootTest#properties注解
  4. 命令行参数。例如 --server.port=9090
  5. 来自SPRING_APPLICATION_JSON中的属性,这是内联在环境变量或者系统属性中的一个JSON
  6. ServletConfig初始化参数
  7. ServletContext初始化参数
  8. 来自java:comp/env的JNDI属性
  9. 系统属性System.getProperties()
  10. OS环境变量
  11. RandomValuePropertySource中的random.*属性
  12. 位于应用JAR包外部的application-{profile}.properties文件(或者YAML),针对特定profile
  13. 位于应用JAR包内部的application-{profile}.properties文件(或者YAML),针对特定profile
  14. 位于应用JAR包外部的application.properties文件(或者YAML)
  15. 位于应用JAR包内部的application.properties文件(或者YAML)
  16. @Configuration类上的@PropertySource注解
  17. 缺省属性,来自SpringApplication.setDefaultProperties

其中application.properties的搜索位置包括(优先级从高到低):

  1. 当前目录的/config子目录
  2. 当前目录
  3. 类路径下的/config包
  4. 类路径根目录

如果你不想使用上述默认属性文件名称,可以指定命令行参数:

Shell
1
--spring.config.location=classpath:/default.properties,classpath:/override.properties
针对特定Profile的属性 

你可以针对不同运行环境,为Spring Boot应用定义多个Profile。Environment包含了若干内置Profile,且默认激活(如果你没有激活其它)default这个Profile。

针对特定Profile的属性定义在application-{profile}.properties文件中。

通过设置spring.profiles.active属性,可以改变当前活动Profile。例如:

1
spring.profiles.active=dev,hsqldb
属性占位符

在属性文件中,你可以通过占位符引用先前定义的属性:

1
2
app.name=MyApp
app.description=${app.name} is a Spring Boot application
Maven变量展开

你可以在属性文件中引用Maven项目属性:

Shell
1
2
3
spring.profiles.active=@environment@
app.encoding=@project.build.sourceEncoding@
app.java.version=@java.version@

配合Maven Profile:

XML
1
2
3
4
5
6
<profile>
    <id>pro</id>
    <properties>
        <environment>pro</environment>
    </properties>
</profile>

则运行 mvn package -Ppro后属性文件中的@environment@自动替换为pro

日志

Spring Boot支持使用Java Util Logging、Log4J2、Logback这几种日志实现。如果你使用Starters,则默认使用Logback。要定制日志配置,使用以下配置文件:

日志实现 配置文件
Logback logback-spring.xml, logback-spring.groovy, logback.xml 或者logback.groovy
Log4j2 log4j2-spring.xml 或者 log4j2.xml
Java Util Logging logging.properties

你可以使用Slf4J提供的日志API。

日志配置

可以在application.properties中配置对应的属性:

1
2
3
4
5
6
7
8
9
# 设置根日志级别
logging.level.root=DEBUG
# 设置日志级别
logging.level.cc.gmem=DEBUG
# 配置基于文件的日志
logging.file=${java.io.tmpdir}/application.log
# 配置日志输出格式
logging.pattern.console= "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"
logging.pattern.file= "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
Web应用支持

Spring MVC框架允许你创建特殊的@Controller、@RestController以处理入站的HTTP请求,具体处理逻辑由注解了@RequestMapping的方法负责。

自动配置

执行以下自动配置:

  1. 包含ContentNegotiatingViewResolver 、BeanNameViewResolver 这两个Bean
  2. 支持包括WebJars在内的静态资源
  3. 自动注册Converter、GenericConverter、Formatter
  4. 支持HttpMessageConverters
  5. 自动注册MessageCodesResolver
  6. 支持静态index.html
  7. 支持定制Favicon
  8. 自动使用ConfigurableWebBindingInitializer 

如果你仅仅想添加额外的拦截器、formatters、view controllers,可以在WebMvcConfigurerAdapter的子类上加@Configuration注解,但是不使用@EnableWebMvc注解。

如果你希望使用自己实现的RequestMappingHandlerMapping、RequestMappingHandlerAdapter 、ExceptionHandlerExceptionResolver ,声明一个WebMvcRegistrationsAdapter,提供这些组件。

如果你希望对Spring MVC进行完全的控制,在主配置类上添加@EnableWebMvc注解。

HttpMessageConverters

如果需要自定义消息转换器,声明HttpMessageConverters类型的Bean即可:

Java
1
2
3
4
5
6
7
8
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
// ...
@Bean
public HttpMessageConverters customConverters() {
    HttpMessageConverter<?> additional = ...
    HttpMessageConverter<?> another = ...
    return new HttpMessageConverters(additional, another);
}
定制JSON编解码器

当基于JacksonJSON来编解码JSON时,你可能需要定制JsonSerializer、JsonDeserializer。

结合Spring Boot使用时,如果要注册定制的编解码器,需要在编解码器类(或者其外部类)上声明注解:

Java
1
2
3
4
5
@JsonComponent
public class JsonCodecs {
    public static class Serializer extends JsonSerializer {}
    public static class Deserializer extends JsonDeserializer {}
}
静态内容

默认情况下,Spring Boot从类路径、ServletContext的根目录下寻找:

  1. /static
  2. /public
  3. /resources
  4. /META-INF/resources 

这几个目录,并将其作为静态内容看待,你可以设置spring.resources.static-locations以修改静态目录。 这些静态内容默认映射URL为/**,你可以设置下面的属性以修改:

Shell
1
2
# 重新定位URL
spring.mvc.static-path-pattern=/resources/** 

除了以目录方式组织的静态内容,Spring Boot还支持WebJars。任何/webjars/**目录下的JAR,如果符合WebJar格式,将会被作为静态资源使用。

Favicon

你可以在静态文件根目录中放置一个favicon.ico文件。

模板引擎支持

Spring Boot支持FreeMarker、Groovy、Thymeleaf、Mustache这几种模板引擎的自动配置。

JSP也可以使用。

错误处理

Spring Boot默认提供了一个/error映射,来处理所有错误,并且注册为Servlet容器的全局错误页面。此错误页面的默认行为:

  1. 对于浏览器客户端:以HTML格式显示一个页面。要定制此页面,可以配置一个映射到/error的View
  2. 对于机器客户端:返回一个JSON响应,包含错误码、详细错误信息。此JSON响应对应的Java类为ErrorAttributes,你可以注册一个ErrorAttributes的子类型Bean,改变响应内容
    你还可以针对特定的控制器、异常类型,定制此JSON:
    Java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // 针对和FooController在同一包中的所有控制器
    @ControllerAdvice(basePackageClasses = FooController.class)
    public class FooControllerAdvice extends ResponseEntityExceptionHandler {
        // 针对YourException
        @ExceptionHandler(YourException.class)
        @ResponseBody
        ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
            HttpStatus status = getStatus(request);
            // CustomErrorType的JSON展示会代替ErrorAttributes写到响应中
            return new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), status);
        }
     
        private HttpStatus getStatus(HttpServletRequest request) {
            Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
            if (statusCode == null) {
                return HttpStatus.INTERNAL_SERVER_ERROR;Media Source
            }
            return HttpStatus.valueOf(statusCode);
        }
     
    }Environment

要完成替换上述默认行为,可以注册一个ErrorController类型的Bean。

定制错误页

如果希望针对不同的HTTP状态码定制错误页面,添加一个文件到/error目录下。错误页面可以是静态HTML或者某种被支持的模板。 

例如,要对404定制错误页面,可以创建src/main/resources/public/error/404.html。要对所有5xx定制错误页面,可以在前面的目录中创建5xx.ftl。

CORS支持

跨源资源共享(Cross-origin resource sharing)是被浏览器广泛实现的W3C规范,允许你指定一个灵活的策略,允许哪些跨Domain请求被发送。使用CORS可以避免使用IFRAME、JSONP等变通技术。

Spring MVC 4.2开始对CORS进行了开箱即用的支持。你可以在控制器方法上使用@CrossOrigin注解。或者配置全局性的CORS策略:

Java
1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class MyConfiguration {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**");
            }
        };
    }
}
JAX-RS和Jersey

如果你喜欢基于JAX-RS编程模型创建REST端点,你可以使用Jersey、 Apache CXF等框架。要集成到Spring你只需要把它们的Servlet、Filter注册为@Bean。

Jersey 2.x对Spring集成提供了支持,Spring Boot可以进行自动配置,你需要依赖spring-boot-starter-jersey这个Starter并且注册ResourceConfig类型的Bean:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component
@ApplicationPath("/api")  // 默认情况下Jersey Servlet被映射到/*,此注解用于定制映射路径
public class JerseyConfig extends ResourceConfig {
    public JerseyConfig() {
        register(Endpoint.class);
    }
}
// 所有注册的Endpoint,必须是@Component,且包含HTTP资源注解(例如GET)
@Component
@Path("/hello")
public class Endpoint {
    @GET
    public String message() {
        return "Hello";
    }
}

要进行更定制化的配置,实现任意数量的ResourceConfigCustomizer类型的Bean。

默认情况下,Jersey在一个ServletRegistrationBean类型的@Bean中初始化其Servlet。此Servlet默认是延迟加载的,你可以配置属性spring.jersey.servlet.load-on-startup让其立即加载。 你也可以配置spring.jersey.type=filter,这样一个ServletFilter会代替上述Servlet。

JAX-RS和CXF

添加Maven依赖:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
  </dependency>
  <dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-spring-boot-starter-jaxrs</artifactId>
    <version>3.3.1</version>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
  <!-- 对JSON的支持 -->
  <dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-jaxrs</artifactId>
    <version>1.9.13</version>
  </dependency>
  <dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-xc</artifactId>
    <version>1.9.13</version>
  </dependency>
</dependencies>

RESTful资源接口示例,一般直接将Service层作为RESTful资源的接口:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.util.Collection;
 
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
 
import com.javacodegeeks.examples.jaxrs.model.Student;
 
// 将此类标识为根资源
@Path("students")
// 指定请求、响应的MIME类型
@Consumes("application/json;charset=UTF-8")
@Produces(MediaType.APPLICATION_JSON)
public interface StudentService {
  
  // 动词,此外支持@POST, @UPDATE,@DELETE
  @GET
  public Collection<Student> getAllStudents();
  
  @Path("{id}")
  @GET
  // 返回值类型javax.ws.rs.core.Response,是HTTP响应的抽象
  public Response getById(@PathParam("id") Long id);
 
}

在Spring属性中配置CXF:

Properties
1
2
3
4
5
# 默认/services
cxf.path=/studentservice
# 让CXF自动扫描带有JAX-RS注解的类
cxf.jaxrs.classes-scan=true
cxf.jaxrs.classes-scan-packages=cc.gmem.springboot.jaxrs

现在你可以通过URL:http://localhost:8080/studentservice/students来访问 StudentService暴露的RESTful服务了。

内嵌Servlet容器

Spring Boot支持内嵌的Tomcat、Jetty或者Undertow。通过合适的Starter你就可以得到自动化的配置。内嵌服务器默认监听端口8080。

当使用内嵌Servlet容器时,标准的Servlet组件(Servlet、Filter、Listener)既可以注册为Spring Beans,也可以自动被扫描。要启用自动扫描,使用@ServletComponentScan注解,@WebServlet、@WebFilter、@WebListener可以被自动扫描。

如果应用中仅仅有一个Servlet,它会默认被映射到 /,如果有多个Servlet,其Bean名称被作为URL前缀。Filter默认映射到/*。

如果自动化配置不能满足需要,你可以实现自己的ServletRegistrationBean、FilterRegistrationBean或者ServletListenerRegistrationBean。

要定制Servlet容器的运行参数,可以使用配置属性,通常定义在application.properties文件中。通用的配置属性包括:

Servlet容器属性 说明
server.port 监听端口
server.address 监听地址
server.ssl

SSL相关配置,示例:

1
2
3
4
5
6
7
8
# 密钥数据库的位置
server.ssl.key-store:classpath:keystore.jks
# 密钥数据库的密码
server.ssl.key-store-password:kurento
# 密钥数据库的类型
server.ssl.keyStoreType:JKS
# 使用的密钥
server.ssl.keyAlias:kurento-selfsigned
server.session.persistence 是否进行会话的持久化
server.session.timeout 会话过期时间
server.session.store-dir 会话存放位置
server.session.cookie.* Cookie相关配置
server.error.path 错误页面配置
server.tomcat
server.undertow
容器特定的配置属性,这个页面包含所有可用属性
EmbeddedWebApplicationContext

这是一种特化的WebApplicationContext,在自举时它会寻找一个EmbeddedServletContainerFactory实现类,通常是TomcatEmbeddedServletContainerFactory、JettyEmbeddedServletContainerFactory或者UndertowEmbeddedServletContainerFactory,以创建Servlet容器。

WebSockets支持

Spring Boot支持基于内嵌容器的自动化WebSockets配置,包含对spring-boot-starter-websocket的依赖即可。 

任务调度支持

要启用任务调度支持,只需要在应用程序入口添加 @EnableScheduling注解即可,之后你就可以在任何服务的方法上使用注解了:

Java
1
2
3
4
5
6
7
8
9
10
11
@EnableScheduling
public class App {}
 
 
// 每5秒调度执行一次
@Scheduled( fixedRate = 5000 )
@Scheduled( cron = "*/5 * * * * * *" )
// 每次执行完毕后,延迟5秒再次执行
@Scheduled( fixedDelay = 5000 )
public synchronized void cleanup() {
}
缓存支持

引入依赖:

XML
1
2
3
4
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

在主程序上使用注解来启用缓存支持:

Java
1
2
3
4
5
import org.springframework.cache.annotation.EnableCaching;
 
@SpringBootApplication
@EnableCaching
public class CacheApplication {} 
常用样例
JPA和缓存
POM配置
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.6</version>
        </dependency>
 
        <!-- 使用Log4j2 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>RELEASE</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
            <version>RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-yaml</artifactId>
            <version>2.5.0</version>
        </dependency>
 
        <!-- Spring Boot 缓存支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
            <version>RELEASE</version>
        </dependency>
        <!-- Spring Boot data-jpa支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>RELEASE</version>
        </dependency>
    </dependencies>
</project>
application.properties
1
2
3
4
5
6
7
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
 
spring.datasource.url=jdbc:mysql://10.255.223.241:3306/digital
spring.datasource.username=digital
spring.datasource.password=digital
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
实体类
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package cc.gmem.study.sb.entity;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
 
@Entity
@Table( name = "media" )
public class Media {
 
    @Id
    private Long mediaId;
 
    @Column
    private Long saleId;
 
    @Column
    private String title;
 
    @Column( name = "is_bn" )
    private String isbn;
 
    @Column( name = "shelf_status" )
    private boolean onShelf;
}
存储层
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package cc.gmem.study.sb.repo;
 
 
import cc.gmem.study.sb.entity.Media;
import org.springframework.cache.annotation.*;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
 
/**
* JpaRepository<EntityClass,IdType>,继承了:
*      PagingAndSortingRepository 支持分页和排序
*          CrudRepository 支持常规增删改查
*              Repository 标识接口
*/
 
/* 类级别的缓存公共设置 */
@CacheConfig( cacheNames = "media" )
public interface MediaRepository extends JpaRepository<Media, Long> {
 
    // 指定一个JPA查询
    @Query( "SELECT m FROM Media m WHERE m.isbn=:isbn AND m.onShelf=true" )
    /**
     * 将函数调用结果放入缓存,优先从缓存中查找,如果找不到才真正调用函数
     * key 指定SpEL表达式作为缓存键,p0表示第一个入参,result表示返回值
     * keyGenerator 不能和key同时使用,指定一个org.springframework.cache.interceptor.KeyGenerator实现类
     * sync 多个线程同时调用此函数时,是否串行化。当底层操作非常耗时时可以设置为true
     * condition 指定SpEL,仅当(在函数调用前的)求值结果为true时才会缓存
     * unless 指定SpEL,仅当(在函数调用后的)求值结果为false时才会缓存,可以引用result变量
     * cacheManager 指定使用的缓存管理器,@EnableCaching会自动注册可用的缓存管理器,查找缓存实现的顺序为:
     *      GenericJCache EhCache 2.x Hazelcast Infinispan Redis Guava Simple
     *      可以使用spring.cache.type强制指定
     * cacheResolver 指定使用的缓存解析器
     */
    @Cacheable( key = "#p0", sync = false, condition = "#p0.length()==13", unless = "#result == null" )
    Media findByIsbn( @Param( "isbn" ) String isbn );
 
    @Override
    /**
     * 调用此函数时导致缓存清除
     * allEntries 清除所有缓存
     * beforeInvocation 在调用函数前还是后执行清除
     */
    @CacheEvict( key = "#p0", allEntries = false, beforeInvocation = false )
    void deleteById( Long mediaId );
 
    @Override
    // 下面的注解总是调用函数并缓存其结果
    @CachePut( key = "#p0.isbn" )
    // 可以同时存储多个缓存
    @Caching(
        put = {
            // 可以引用result
            @CachePut(value = "media", key = "#result.username", condition = "#result != null"),
            @CachePut(value = "media", key = "#result.id", condition = "#result != null")
        }
    )
    Media save( Media media );
}
服务层
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package cc.gmem.study.sb.service;
 
 
import cc.gmem.study.sb.entity.Media;
import cc.gmem.study.sb.repo.MediaRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import javax.transaction.Transactional;
 
@Service
@Transactional
public class MediaService {
 
    @Autowired
    private MediaRepository repo;
 
    public Media findUserByIsbn( String isbn ) {
        return repo.findByIsbn( isbn );
    }
}
入口点
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package cc.gmem.study.sb;
 
import cc.gmem.study.sb.entity.Media;
import cc.gmem.study.sb.service.MediaService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ApplicationContext;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
 
@SpringBootApplication
// 启用Spring基于注解的缓存管理
@EnableCaching
// 从何处扫描JPA实体类
@EntityScan( basePackages = { "cc.gmem.study.sb.entity" } )
// 从何处查找Repository接口的子接口,并自动生成实现类
@EnableJpaRepositories( basePackages = "cc.gmem.study.sb.repo" )
public class JPACacheApplication {
 
    private static final Logger LOGGER = LoggerFactory.getLogger( JPACacheApplication.class );
 
    public static void main( String[] args ) {
        ApplicationContext ctx = SpringApplication.run( JPACacheApplication.class, args );
        MediaService ms = ctx.getBean( MediaService.class );
        Media m = ms.findUserByIsbn( "9787547227961" );
        LOGGER.debug( m.getTitle() );
        m = ms.findUserByIsbn( "9787547227961" );
        LOGGER.debug( m.getTitle() );
    }
}
常见问题
关于日志

org.springframework.boot.diagnostics应该调整到debug级别, org.springframework应该调整到warn级别,这样可以看到更多有价值的日志。

← SockJS知识集锦
实时通信协议族 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • Spring对JMS的支持
  • Spring知识集锦
  • 基于JavaConfig方式的Spring+Hibernate集成
  • Spring对WebSocket的支持
  • Spring与Quartz的任务调度比较

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • Bazel学习笔记 38 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2