Spring Boot学习笔记
Spring Boot是Spring的一个子项目,它让创建独立运行(通过java -jar)的、产品级别的Spring应用变得简单:
- 支持创建独立运行于JVM中的Spring程序
- 内嵌Tomcat、Jetty或者Undertow,不需要部署War包
- 简化Maven的POM配置
- 在任何可能的情况下,自动配置Spring
- 提供产品级特性,包括性能度量、健康检测、外部化配置
- 不需要任何代码生成或者XML配置
- 支持Java 9,至少要求Java 8
- 基于 Spring 5 构建,Spring 的新特性均可以在 Spring Boot 2.0 中使用
- 为各种组件的响应式编程提供了自动化配置,如:Reactive Spring Data、Reactive Spring Security 等
- 支持 Spring MVC 的非阻塞式替代方案 WebFlux 以及嵌入式 Netty Server
- 对 HTTP/2 的支持
- 更灵活的属性绑定API,不通过 @ConfigurationProperties注解就能实现配置内容读取和使用
- Spring Security 整合的简化
- Gradle 插件增强,要求Gradle版本4.4+
- Starter整合的第三方组件版本升级:
- Tomcat 升级至 9.0,Servlet版本4.0
- Jetty 升级至 9.4,Servlet版本3.1
- Flyway 升级至 5
- Hibernate 升级至 5.2
- Thymeleaf 升级至 3
本章我们创建一个简单的基于Spring Boot的Web应用。
首先设置父项目:
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依赖:
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:
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> |
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,即可访问。
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构件已经存在,可以这样引入基础配置:
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> |
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") } |
所谓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 |
日志依赖 |
尽管你可以通过 SpringApplication.run()传递XML源,但是Spring Boot倾向于使用基于Java的配置。 即主要配置源应该是一个注解了 @Configuration的Java类,此类通常定义了main方法。
你可以使用 @Import注解引入额外的配置类。
你也可以使用 @ComponentScan自动扫描任何Spring组件,包括@ComponentScan本身。
如果要引入额外的XML配置,可以使用 @ImportResource注解。
Spring Boot的自动配置机制,尝试根据当前项目的依赖,自动判断如何配置Spring上下文、运行容器、数据库。例如,如果HSQLDB位于类路径下,Spring Boot会自动配置一个内存数据库。
要启用自动配置,可以在主配置类上添加 @EnableAutoConfiguration或者 @SpringBootApplication注解。
如果要查看哪些自动配置被启用,为何被启用,可以为应用程序传入参数 --debug。
你可以明确的禁用某种自动配置:
1 2 3 4 |
@Configuration @EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) public class Configuration { } |
注意:自动配置可以被渐进的替代。例如,当你手工配置了数据源后,Spring Boot就不会自动配置HSQLDB。
此注解等价于@Configuration, @EnableAutoConfiguration,@ComponentScan的组合。其中@ComponentScan为:
1 2 3 |
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) |
由于没有为@ComponentScan指定basePackage,因此默认扫描当前类所在包及其子包。
可以通过@ComponentScan来查找Bean,或者在配置类的方法中使用@Bean。@Component, @Service, @Repository, @Controller等注解都是支持的。
要为Bean注入字段,可以使用@Autowired,@Inject等注解。
很多IDE,包括Eclipse、IDEA都提供了对Spring Boot应用程序的支持。
通过命令行运行的示例:
1 |
java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n -jar boot-study.jar |
通过Maven运行的示例:
1 2 |
export MAVEN_OPTS=-Xmx1024m -XX:MaxPermSize=128M mvn spring-boot:run |
通过Gradle运行的示例:
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的情况下,模板自动重新载入。
大部分IDE支持代码热替换,因此你对类进行非结构性的改变时,构建后Java类会自动更新。
Spring Loaded允许方法签名改变后的类仍然能够重新载入。
spring-boot-devtools提供了自动的重启功能,其速度比JRebel、Spring Loaded慢,但是比冷启动快的多。
spring-boot-devtools的快速重启功能依赖于ClassLoader,打包好的第三方库,使用Base加载器加载,而正在开发的那些类则使用restart加载器加载。当快速重启发生后,之前的restart加载器被丢弃,新的restart加载器被创建。
此模块提供一些开发时特性,要启用DevTools,引入:
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在生产环境下会自动禁用,生产环境的判断依据是:
- 以java -jar启动Spring Boot应用
- 或者,使用了特殊的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 |
配置下面的属性以确定哪些JAR包由restart加载器加载:
1 2 3 4 |
# 下面的JAR由Base加载器加载 restart.exclude.companycommonlibs=/mycorp-common-[\\w-]+\.jar # 下面的JAR由restart加载器加载 restart.include.projectcommon=/mycorp-myproj-[\\w-]+\.jar |
DevTools包含了一个内嵌的LiveReload服务器,可以用来在资源发生变化时触发浏览器刷新。各大浏览器均有LiveReload扩展。
如果要禁用LiveReload服务器,配置:
1 |
spring.devtools.livereload.enabled=false |
此类提供了一些便捷方法,用于Spring应用的自举。最简单的用法:
1 |
SpringApplication.run(MyConfiguration.class, args); |
可以使用链式调用进行定制化:
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.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 | 启动失败后发布 |
SpringApplication会自动尝试创建合适的ApplicationContext实现类。默认的:
- 如果当前应用是Web应用,创建AnnotationConfigEmbeddedWebApplicationContext
- 否则创建AnnotationConfigApplicationContext
你可以调用setWebEnvironment()来强制指定是否Web应用。
你可以注入此类型,以访问传递给SpringApplication.run的参数。
Environment对象是配置信息的容器。
Spring Boot允许你把配置信息外部化,这样同一套代码就可以很容易的在不同环境下运行。
你可以使用YAML 、环境变量、命令行参数来指定配置。
使用 @Value注解可以直接注入配置参数,也可以通过Environment来访问,或者通过@ConfigurationProperties绑定到结构化对象。例如:
1 2 3 4 5 6 |
@Component public class Bean { @Value("${name}") private String name; } |
如果类路径下的application.properties文件中有name这个键,或者命令行指定了--name参数,则会被注入到上述Bean的属性中。
Spring使用PropertySource来定位配置属性,它会从以下位置寻找(优先级从高到低):
- 当使用DevTools时,DevTools全局设置,位于~/.spring-boot-devtools.properties
- 位于测试用例上的@TestPropertySource注解
- 位于测试用例上的@SpringBootTest#properties注解
- 命令行参数。例如 --server.port=9090
- 来自SPRING_APPLICATION_JSON中的属性,这是内联在环境变量或者系统属性中的一个JSON
- ServletConfig初始化参数
- ServletContext初始化参数
- 来自java:comp/env的JNDI属性
- 系统属性System.getProperties()
- OS环境变量
- RandomValuePropertySource中的random.*属性
- 位于应用JAR包外部的application-{profile}.properties文件(或者YAML),针对特定profile
- 位于应用JAR包内部的application-{profile}.properties文件(或者YAML),针对特定profile
- 位于应用JAR包外部的application.properties文件(或者YAML)
- 位于应用JAR包内部的application.properties文件(或者YAML)
- @Configuration类上的@PropertySource注解
- 缺省属性,来自SpringApplication.setDefaultProperties
其中application.properties的搜索位置包括(优先级从高到低):
- 当前目录的/config子目录
- 当前目录
- 类路径下的/config包
- 类路径根目录
如果你不想使用上述默认属性文件名称,可以指定命令行参数:
1 |
--spring.config.location=classpath:/default.properties,classpath:/override.properties |
你可以针对不同运行环境,为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项目属性:
1 2 3 |
spring.profiles.active=@environment@ app.encoding=@project.build.sourceEncoding@ app.java.version=@java.version@ |
配合Maven Profile:
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" |
Spring MVC框架允许你创建特殊的@Controller、@RestController以处理入站的HTTP请求,具体处理逻辑由注解了@RequestMapping的方法负责。
执行以下自动配置:
- 包含ContentNegotiatingViewResolver 、BeanNameViewResolver 这两个Bean
- 支持包括WebJars在内的静态资源
- 自动注册Converter、GenericConverter、Formatter
- 支持HttpMessageConverters
- 自动注册MessageCodesResolver
- 支持静态index.html
- 支持定制Favicon
- 自动使用ConfigurableWebBindingInitializer
如果你仅仅想添加额外的拦截器、formatters、view controllers,可以在WebMvcConfigurerAdapter的子类上加@Configuration注解,但是不使用@EnableWebMvc注解。
如果你希望使用自己实现的RequestMappingHandlerMapping、RequestMappingHandlerAdapter 、ExceptionHandlerExceptionResolver ,声明一个WebMvcRegistrationsAdapter,提供这些组件。
如果你希望对Spring MVC进行完全的控制,在主配置类上添加@EnableWebMvc注解。
如果需要自定义消息转换器,声明HttpMessageConverters类型的Bean即可:
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); } |
当基于JacksonJSON来编解码JSON时,你可能需要定制JsonSerializer、JsonDeserializer。
结合Spring Boot使用时,如果要注册定制的编解码器,需要在编解码器类(或者其外部类)上声明注解:
1 2 3 4 5 |
@JsonComponent public class JsonCodecs { public static class Serializer extends JsonSerializer {} public static class Deserializer extends JsonDeserializer {} } |
默认情况下,Spring Boot从类路径、ServletContext的根目录下寻找:
- /static
- /public
- /resources
- /META-INF/resources
这几个目录,并将其作为静态内容看待,你可以设置spring.resources.static-locations以修改静态目录。 这些静态内容默认映射URL为/**,你可以设置下面的属性以修改:
1 2 |
# 重新定位URL spring.mvc.static-path-pattern=/resources/** |
除了以目录方式组织的静态内容,Spring Boot还支持WebJars。任何/webjars/**目录下的JAR,如果符合WebJar格式,将会被作为静态资源使用。
你可以在静态文件根目录中放置一个favicon.ico文件。
Spring Boot支持FreeMarker、Groovy、Thymeleaf、Mustache这几种模板引擎的自动配置。
JSP也可以使用。
Spring Boot默认提供了一个/error映射,来处理所有错误,并且注册为Servlet容器的全局错误页面。此错误页面的默认行为:
- 对于浏览器客户端:以HTML格式显示一个页面。要定制此页面,可以配置一个映射到/error的View
- 对于机器客户端:返回一个JSON响应,包含错误码、详细错误信息。此JSON响应对应的Java类为ErrorAttributes,你可以注册一个ErrorAttributes的子类型Bean,改变响应内容
你还可以针对特定的控制器、异常类型,定制此JSON:
123456789101112131415161718192021// 针对和FooController在同一包中的所有控制器@ControllerAdvice(basePackageClasses = FooController.class)public class FooControllerAdvice extends ResponseEntityExceptionHandler {// 针对YourException@ExceptionHandler(YourException.class)@ResponseBodyResponseEntity<?> 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。
跨源资源共享(Cross-origin resource sharing)是被浏览器广泛实现的W3C规范,允许你指定一个灵活的策略,允许哪些跨Domain请求被发送。使用CORS可以避免使用IFRAME、JSONP等变通技术。
Spring MVC 4.2开始对CORS进行了开箱即用的支持。你可以在控制器方法上使用@CrossOrigin注解。或者配置全局性的CORS策略:
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编程模型创建REST端点,你可以使用Jersey、 Apache CXF等框架。要集成到Spring你只需要把它们的Servlet、Filter注册为@Bean。
Jersey 2.x对Spring集成提供了支持,Spring Boot可以进行自动配置,你需要依赖spring-boot-starter-jersey这个Starter并且注册ResourceConfig类型的Bean:
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。
添加Maven依赖:
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资源的接口:
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:
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服务了。
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相关配置,示例:
|
||
server.session.persistence | 是否进行会话的持久化 | ||
server.session.timeout | 会话过期时间 | ||
server.session.store-dir | 会话存放位置 | ||
server.session.cookie.* | Cookie相关配置 | ||
server.error.path | 错误页面配置 | ||
server.tomcat server.undertow |
容器特定的配置属性,这个页面包含所有可用属性 |
这是一种特化的WebApplicationContext,在自举时它会寻找一个EmbeddedServletContainerFactory实现类,通常是TomcatEmbeddedServletContainerFactory、JettyEmbeddedServletContainerFactory或者UndertowEmbeddedServletContainerFactory,以创建Servlet容器。
Spring Boot支持基于内嵌容器的自动化WebSockets配置,包含对spring-boot-starter-websocket的依赖即可。
要启用任务调度支持,只需要在应用程序入口添加 @EnableScheduling注解即可,之后你就可以在任何服务的方法上使用注解了:
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() { } |
引入依赖:
1 2 3 4 |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> |
在主程序上使用注解来启用缓存支持:
1 2 3 4 5 |
import org.springframework.cache.annotation.EnableCaching; @SpringBootApplication @EnableCaching public class CacheApplication {} |
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> |
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 |
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; } |
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 ); } |
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 ); } } |
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级别,这样可以看到更多有价值的日志。
Leave a Reply