Spring Boot是Spring的一个子项目,它让创建独立运行(通过java -jar)的、产品级别的Spring应用变得简单:
本章我们创建一个简单的基于Spring Boot的Web应用。
首先设置父项目:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
然后添加Web的Starter依赖:
<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:
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
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,即可访问。
<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构件已经存在,可以这样引入基础配置:
<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>
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。
你可以明确的禁用某种自动配置:
@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class Configuration {
}
注意:自动配置可以被渐进的替代。例如,当你手工配置了数据源后,Spring Boot就不会自动配置HSQLDB。
此注解等价于@Configuration, @EnableAutoConfiguration,@ComponentScan的组合。其中@ComponentScan为:
@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应用程序的支持。
通过命令行运行的示例:
java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n -jar boot-study.jar
通过Maven运行的示例:
export MAVEN_OPTS=-Xmx1024m -XX:MaxPermSize=128M mvn spring-boot:run
通过Gradle运行的示例:
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,引入:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>1.5.6.RELEASE</version>
<optional>true</optional>
</dependency>
DevTools在生产环境下会自动禁用,生产环境的判断依据是:
模板引擎具有缓存功能、Spring MVC也在HTTP头中添加了缓存字段。DevTools通过配置属性,禁用了这些功能。
默认情况下,使用了DevTools的应用会在类路径发生变化时自动重启,注意模板、静态资源不会触发重启。具体来说,下列位置的资源不会触发重启:
/META-INF/maven, /META-INF/resources ,/resources ,/static ,/public和/templates
如果需要显式指定那些资源不会触发重启,配置下面的属性:
# 自己指定不触发的资源 spring.devtools.restart.exclude=static/**,public/** # 在默认不触发资源目录列表的基础上,额外增加 spring.devtools.restart.additional-exclude=
如果需要额外指定会触发重启的资源,配置下面的属性:
spring.devtools.restart.additional-exclude=
如果你使用某种持续的自动编译、产生输出的IDE,并且期望在这些输出发生变化时,触发重启,配置下面的属性:
spring.devtools.restart.trigger-file=
要禁用DevTools的重启功能,配置下面的属性:
spring.devtools.restart.enabled=false
配置下面的属性以确定哪些JAR包由restart加载器加载:
# 下面的JAR由Base加载器加载 restart.exclude.companycommonlibs=/mycorp-common-[\\w-]+\.jar # 下面的JAR由restart加载器加载 restart.include.projectcommon=/mycorp-myproj-[\\w-]+\.jar
DevTools包含了一个内嵌的LiveReload服务器,可以用来在资源发生变化时触发浏览器刷新。各大浏览器均有LiveReload扩展。
如果要禁用LiveReload服务器,配置:
spring.devtools.livereload.enabled=false
此类提供了一些便捷方法,用于Spring应用的自举。最简单的用法:
SpringApplication.run(MyConfiguration.class, args);
可以使用链式调用进行定制化:
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实现类。默认的:
你可以调用setWebEnvironment()来强制指定是否Web应用。
你可以注入此类型,以访问传递给SpringApplication.run的参数。
Environment对象是配置信息的容器。
Spring Boot允许你把配置信息外部化,这样同一套代码就可以很容易的在不同环境下运行。
你可以使用YAML 、环境变量、命令行参数来指定配置。
使用@Value注解可以直接注入配置参数,也可以通过Environment来访问,或者通过@ConfigurationProperties绑定到结构化对象。例如:
@Component
public class Bean {
@Value("${name}")
private String name;
}
如果类路径下的application.properties文件中有name这个键,或者命令行指定了--name参数,则会被注入到上述Bean的属性中。
Spring使用PropertySource来定位配置属性,它会从以下位置寻找(优先级从高到低):
其中application.properties的搜索位置包括(优先级从高到低):
如果你不想使用上述默认属性文件名称,可以指定命令行参数:
--spring.config.location=classpath:/default.properties,classpath:/override.properties
你可以针对不同运行环境,为Spring Boot应用定义多个Profile。Environment包含了若干内置Profile,且默认激活(如果你没有激活其它)default这个Profile。
针对特定Profile的属性定义在application-{profile}.properties文件中。
通过设置spring.profiles.active属性,可以改变当前活动Profile。例如:
spring.profiles.active=dev,hsqldb
在属性文件中,你可以通过占位符引用先前定义的属性:
app.name=MyApp
app.description=${app.name} is a Spring Boot application
你可以在属性文件中引用Maven项目属性:
spring.profiles.active=@environment@ app.encoding=@project.build.sourceEncoding@ app.java.version=@java.version@
配合Maven Profile:
<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中配置对应的属性:
# 设置根日志级别
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的方法负责。
执行以下自动配置:
如果你仅仅想添加额外的拦截器、formatters、view controllers,可以在WebMvcConfigurerAdapter的子类上加@Configuration注解,但是不使用@EnableWebMvc注解。
如果你希望使用自己实现的RequestMappingHandlerMapping、RequestMappingHandlerAdapter 、ExceptionHandlerExceptionResolver ,声明一个WebMvcRegistrationsAdapter,提供这些组件。
如果你希望对Spring MVC进行完全的控制,在主配置类上添加@EnableWebMvc注解。
如果需要自定义消息转换器,声明HttpMessageConverters类型的Bean即可:
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使用时,如果要注册定制的编解码器,需要在编解码器类(或者其外部类)上声明注解:
@JsonComponent
public class JsonCodecs {
public static class Serializer extends JsonSerializer {}
public static class Deserializer extends JsonDeserializer {}
}
默认情况下,Spring Boot从类路径、ServletContext的根目录下寻找:
这几个目录,并将其作为静态内容看待,你可以设置spring.resources.static-locations以修改静态目录。 这些静态内容默认映射URL为/**,你可以设置下面的属性以修改:
# 重新定位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容器的全局错误页面。此错误页面的默认行为:
// 针对和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。
跨源资源共享(Cross-origin resource sharing)是被浏览器广泛实现的W3C规范,允许你指定一个灵活的策略,允许哪些跨Domain请求被发送。使用CORS可以避免使用IFRAME、JSONP等变通技术。
Spring MVC 4.2开始对CORS进行了开箱即用的支持。你可以在控制器方法上使用@CrossOrigin注解。或者配置全局性的CORS策略:
@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:
@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依赖:
<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资源的接口:
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:
# 默认/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.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 |
容器特定的配置属性,这个页面包含所有可用属性 |
这是一种特化的WebApplicationContext,在自举时它会寻找一个EmbeddedServletContainerFactory实现类,通常是TomcatEmbeddedServletContainerFactory、JettyEmbeddedServletContainerFactory或者UndertowEmbeddedServletContainerFactory,以创建Servlet容器。
Spring Boot支持基于内嵌容器的自动化WebSockets配置,包含对spring-boot-starter-websocket的依赖即可。
要启用任务调度支持,只需要在应用程序入口添加@EnableScheduling注解即可,之后你就可以在任何服务的方法上使用注解了:
@EnableScheduling
public class App {}
// 每5秒调度执行一次
@Scheduled( fixedRate = 5000 )
@Scheduled( cron = "*/5 * * * * * *" )
// 每次执行完毕后,延迟5秒再次执行
@Scheduled( fixedDelay = 5000 )
public synchronized void cleanup() {
}
引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
在主程序上使用注解来启用缓存支持:
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching
public class CacheApplication {}
<?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>
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
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;
}
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 );
}
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 );
}
}
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 to cloud Cancel reply