Spring Boot基础

基本概念

技术 特性 适用场景 说明
Spring Boot - 官网
Spring Boot Actuator 监控
- 与Prometheus结合
- Grafana
-

基础概念

启动器

面向开发的启动器

启动器 说明
spring-boot-starter 这是Spring Boot的核心启动器,包含了自动配置、日志和YAML。
spring-boot-starter-actuator 帮助监控和管理应用。
spring-boot-starter-amqp 通过spring-rabbit来支持AMQP协议(Advanced Message Queuing Protocol)。
spring-boot-starter-aop 支持面向方面的编程即AOP,包括spring-aop和AspectJ。
spring-boot-starter-artemis 通过Apache Artemis支持JMS的API(Java Message Service API)。
spring-boot-starter-batch 支持Spring Batch,包括HSQLDB数据库。
spring-boot-starter-cache 支持Spring的Cache抽象。
spring-boot-starter-cloud-connectors 支持Spring Cloud Connectors,简化了在像Cloud Foundry或Heroku这样的云平台上连接服务。
spring-boot-starter-data-elasticsearch 支持ElasticSearch搜索和分析引擎,包括spring-data-elasticsearch。
spring-boot-starter-data-gemfire 支持GemFire分布式数据存储,包括spring-data-gemfire。
spring-boot-starter-data-jpa 支持JPA(Java Persistence API),包括spring-data-jpa、spring-orm、Hibernate。
spring-boot-starter-data-mongodb 支持MongoDB数据,包括spring-data-mongodb。
spring-boot-starter-data-rest 通过spring-data-rest-webmvc,支持通过REST暴露Spring Data数据仓库。
spring-boot-starter-data-solr 支持Apache Solr搜索平台,包括spring-data-solr。
spring-boot-starter-freemarker 支持FreeMarker模板引擎。
spring-boot-starter-groovy-templates 支持Groovy模板引擎。
spring-boot-starter-hateoas 通过spring-hateoas支持基于HATEOAS的RESTful Web服务。
spring-boot-starter-hornetq 通过HornetQ支持JMS。
spring-boot-starter-integration 支持通用的spring-integration模块。
spring-boot-starter-jdbc 支持JDBC数据库。
spring-boot-starter-jersey 支持Jersey RESTful Web服务框架。
spring-boot-starter-jta-atomikos 通过Atomikos支持JTA分布式事务处理。
spring-boot-starter-jta-bitronix 通过Bitronix支持JTA分布式事务处理。
spring-boot-starter-mail 支持javax.mail模块。
spring-boot-starter-mobile 支持spring-mobile。
spring-boot-starter-mustache 支持Mustache模板引擎。
spring-boot-starter-redis 支持Redis键值存储数据库,包括spring-redis。
spring-boot-starter-security 支持spring-security。
spring-boot-starter-social-facebook 支持spring-social-facebook
spring-boot-starter-social-linkedin 支持spring-social-linkedin
spring-boot-starter-social-twitter 支持pring-social-twitter
spring-boot-starter-test 支持常规的测试依赖,包括JUnit、Hamcrest、Mockito以及spring-test模块。
spring-boot-starter-thymeleaf 支持Thymeleaf模板引擎,包括与Spring的集成。
spring-boot-starter-velocity 支持Velocity模板引擎。
spring-boot-starter-web 支持全栈式Web开发,包括Tomcat和spring-webmvc。
spring-boot-starter-websocket 支持WebSocket开发。
spring-boot-starter-ws 支持Spring Web Services。

面向生产环境的启动器

启动器 说明
spring-boot-starter-actuator 增加了面向产品上线相关的功能,比如测量和监控。
spring-boot-starter-remote-shell 增加了远程ssh shell的支持。

面向替换技术的启动器

启动器 说明
spring-boot-starter-jetty 引入了Jetty HTTP引擎(用于替换Tomcat)。
spring-boot-starter-log4j 支持Log4J日志框架。
spring-boot-starter-logging 引入了Spring Boot默认的日志框架Logback。
spring-boot-starter-tomcat 引入了Spring Boot默认的HTTP引擎Tomcat。
spring-boot-starter-undertow 引入了Undertow HTTP引擎(用于替换Tomcat)。

注解

注解 说明
@Configuration 注解的类(要在扫描的包路径中)会被扫描到
@ComponentScan 自动扫描指定包下的全部标有 @Component注解 的类,包括 @Component 下的子注解@Service、@Repository@Controller,并注册成bean
@EnableConfigurationProperties(ServerProperties.class) 启动指定类的

ConfigurationProperties功能;将配置文件中对应的值和 ServerProperties 绑定起来;并把 ServerProperties 加入到 IOC 容器中 | | @EnableAutoConfiguration | 开启自动配置 | | @SpringBootApplication | @SpringBootApplication@Configuration@EnableAutoConfiguration@ComponentScan 注解所修饰,换言之 Springboot 提供了统一的注解来替代以上三个注解,简化程序的配置。 | | @ConditionalOnJava | 系统的java版本是否符合要求 | | @ConditionalOnBean | 容器中存在指定Bean | | @ConditionalOnMissingBean | 容器中不存在指定Bean | | @ConditionalOnExpression | 满足SpEL表达式指定 | | @ConditionalOnClass | 系统中有指定的类 | | @ConditionalOnMissingClass | 系统中没有指定的类 | | @ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean | | @ConditionalOnProperty | 系统中指定的属性是否有指定的值 | | @ConditionalOnResource | 类路径下是否存在指定资源文件 | | @ConditionalOnWebApplication | 当前是web环境 | | @ConditionalOnNotWebApplication | 当前不是web环境 | | @ConditionalOnJndi | JNDI存在指定项 |

使用IDEA创建SpringBoot项目

创建Spring Boot项目

【IDEA】 -> 【Create New Project】 -> 【Spring Initializr】 -> 【Next】 -> 【Next】 -> 选择Web,【Next】 -> 【Finish】。

设置项目为Maven项目

在/src/pom.xml上【右键】,【点击】“Add as Maven Project”。

1
2
3
4
5
6
7
8
9
10
11
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

@RequestMapping("/")
public String index() {
return "Hello,Spring Boot!";
}
}

运行

运行Application…main()。

测试

访问http://localhost:8080,测试能否正常访问。

配置文件

外部配置文件

Spring程序会按优先级从下面这些路径来加载配置文件

  • 当前目录下的/config目录
  • 当前目录
  • classpath里的 /config 目录
  • classpath 根目录

自定义配置文件

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

$ java -jar -Dspring.config.location=yourPath/config.properties app.jar

按Profile不同环境读取不同配置

1
java -jar app.jar --spring.profiles.active = prod|dev|test

Maven打包

打成jar包

修改pom.xml

pom.xml

1
<packaging>jar</packaging>

打包

1
2
3
4
$ cd 项目目录(和pom.xml同级)
$ mvn clean package
## 排除测试代码后进行打包
$ mvn clean package -Dmaven.test.skip=true

打包完成后jar包会生成到target目录下,命名一般是 项目名+版本号.jar。

打成war包

修改pom.xml

pom.xml

1
<packaging>war</packaging>

排除tomcat

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>

打包

1
2
3
4
$ cd 项目目录(和pom.xml同级)
$ mvn clean package
## 排除测试代码后进行打包
$ mvn clean package -Dmaven.test.skip=true

打包完成后jar包会生成到target目录下,命名一般是 项目名+版本号.war。

打包成可执行文件

1
2
3
4
5
6
7
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>

这样生成的jar包,就可以直接以如下命令运行了。

1
$ ./app-1.0.jar

Gradle打包

打成jar包

1
2
$ gradle build
$ java -jar build/libs/mymodule-0.0.1-SNAPSHOT.jar

打成war包

配置gradle.build

1
2
3
4
5
6
7
8
9
10
11
12
...

apply plugin: 'war'

...

dependencies {
compile("org.springframework.boot:spring-boot-starter-web:1.4.2.RELEASE"){
exclude mymodule:"spring-boot-starter-tomcat"
}
}
...

构建

1
$ gradle build

war会生成在build目录下。

启动

控制台启动

1
$ java -jar target/app-1.0.0.jar

这种方式,只要控制台关闭,服务就不能访问了。

后台运行的方式启动

1
$ nohup java -jar target/app-1.0.0.jar &

生产环境

以init.d方式发布为服务

1
2
$ ln -s /var/app/app.jar /etc/init.d/app
chmod +x /etc/init.d/app

以systemd方式发布为服务

systemed比init.d晚出现,在性能、功能等方面都有改进,被推荐使用。

1
$ vi /etc/systemd/system/app.service

内容如下:

1
2
3
4
5
6
7
8
9
10
[Unit]
Description=app
After=syslog.target

[Service]
ExecStart=/var/app/app-1.0.jar
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target

管理服务

方式1

1
$ /etc/init.d/app start|stop|restart|enable|disable

方式2

1
$ service app start|stop|restart|enable|disable

运行运行jar包

1
$ java -jar target/spring-boot-xxx-1.0.0.jar

这种方式,只要控制台关闭,服务就不能访问了。下面我们使用在后台运行的方式来启动:

1
nohup java -jar target/spring-boot-xxx-1.0.0.jar &

也可以在启动的时候选择读取不同的配置文件

1
java -jar app.jar --spring.profiles.active=dev

也可以在启动的时候设置jvm参数

1
java -Xms10m -Xmx80m -jar app.jar &

运行war包

war包放到tomcat服务器下。

查看JVM参数的值

可以根据java自带的jinfo命令:

1
$ jinfo -flags pid

来查看jar 启动后使用的是什么gc、新生代、老年代分批的内存都是多少,示例如下:

1
-XX:CICompilerCount=3 -XX:InitialHeapSize=234881024 -XX:MaxHeapSize=3743416320 -XX:MaxNewSize=1247805440 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=78118912 -XX:OldSize=156762112 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
  • -XX:CICompilerCount :最大的并行编译数
  • -XX:InitialHeapSize 和 -XX:MaxHeapSize :指定JVM的初始和最大堆内存大小
  • -XX:MaxNewSize : JVM堆区域新生代内存的最大可分配大小
  • -XX:+UseParallelGC :垃圾回收使用Parallel收集器

重启

1
2
3
4
5
$ ps -ef|grep java 
# 拿到对于Java程序的pid
$ kill -9 pid
## 再次启动
$ Java -jar xxxx.jar

监控端点

添加启动器

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

查看端点信息

  • http://localhost:8080/actuator
  • http://localhost:8080/actuator/health
  • http://localhost:8080/actuator/info

基础配置

配置启动加载数据

实际应用中,我们会有在项目服务启动的时候就去加载一些数据或做一些事情这样的需求。

方法一

解决办法是: - 通过实现接口CommandLineRunner 来实现 - 使用@Order注解(或者实现Order接口)来规定所有CommandLineRunner实例的运行顺序。

MyStartupRunner1

1
2
3
4
5
6
7
8
9
10
11
12
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
public class MyStartupRunner1 implements CommandLineRunner {
@Override
@Order(value=1)
public void run(String... args) throws Exception {
System.out.println(">>>>>>>>>>>>>>>服务启动执行,执行加载数据等操作1<<<<<<<<<<<<<");
}
}

MyStartupRunner2

1
2
3
4
5
6
7
8
9
10
11
12
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
public class MyStartupRunner2 implements CommandLineRunner{
@Override
@Order(value=2)
public void run(String... args) throws Exception {
System.out.println(">>>>>>>>>>>>>>>服务启动执行,执行加载数据等操作2<<<<<<<<<<<<<");
}
}

方法二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

@Configuration
public class Config {
/**
* 使用 @PostConstruct 注解 启动时加载
*/
@PostConstruct
public void init(){
System.out.println("加载init方法成功");
}
}

配置启动banner

配置/src/main/resources/banner.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
////////////////////////////////////////////////////////////////////
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
////////////////////////////////////////////////////////////////////

配置自定义属性

定义自定义属性

application.properties

1
2
com.wlb.name="kevin"
com.wlb.age=30

调用自定义属性

方法1

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
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "com.wlb")
public class People {
private String Name;
private int Age;

public String getName() {
return Name;
}

public void setName(String name) {
Name = name;
}

public int getAge() {
return Age;
}

public void setAge(int age) {
Age = age;
}
}

方法2

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
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class People {
@Value("${com.wlb.name}")
private String Name;
@Value("${com.wlb.age}")
private int Age;

public String getName() {
return Name;
}

public void setName(String name) {
Name = name;
}

public int getAge() {
return Age;
}

public void setAge(int age) {
Age = age;
}
}

输出自定义属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

@Autowired
private People wlb;

@RequestMapping("/index")
public String index() {
return "姓名"+wlb.getName()+",年龄"+wlb.getAge();
}
}

定义随机数属性

随机项说明 表达式
获取随机字符串 ${random.value}
获取10以内的随机数 ${random.int(10)}
获取10-20的随机数 ${random.int[10,20]}
获取随机long ${random.long}
获取随机uuid ${random.uuid}

配置多环境配置

添加配置文件

在src/resources目录下添加配置文件,分别是: - application-dev.properties #开发环境配置文件 - application-test.properties #测试环境配置文件 - application-prod.properties #生产环境配置文件

激活配置文件

在application.properties添加:

1
2
3
4
5
6
#引用开发的配置文件
spring.profiles.active=dev
#引用测试的配置文件
#spring.profiles.active=test
#引用生产的配置文件
#spring.profiles.active=prod

命令行启动时激活配置文件

1
$ java -jar xxx.jar --spring.profiles.active=dev

禁止命令行设置参数

1
2
3
4
5
6
7
8
9
10
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

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

配置java版本

pom.xml中,配置如下:

1
2
3
<properties>
<java.version>1.8</java.version>
</properties>

配置jdk编译版本

pom.xml -> build节点 -> plugins节点下,增加

1
2
3
4
5
6
7
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>

IDEA中实现热部署

配置IDEA

开启自动make功能

【Build Execution,Deployment】->【Compiler】,Build project automatically --> 选中。 或者CTRL+SHIFT+A --> 查找Build project automatically --> 选中。 PS:Mac下是Command+Shift+A。

勾选compiler.automake.allow.when.app.running

CTRL+SHIFT+A --> 查找Registry --> 勾选compiler.automake.allow.when.app.running

重启IDEA

重新启动IDEA。

配置pom.xml

配置maven依赖

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

配置maven插件

1
2
3
4
5
6
7
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>

配置application.properties

禁止thymeleaf缓存

1
spring.thymeleaf.cache=false

配置全局异常捕获

创建全局异常处理类的方法:

  • 新建一个类GlobalExceptionHandler
  • 在class注解上@ControllerAdvice
  • 在方法上注解上@ExceptionHandler(value = Exception.class)

返回错误页面

定义error.html

在templates目录下创建error.html,将请求的URL和Exception对象的message输出。

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<title>统一异常处理</title>
</head>
<body>
<h1>Error Handler</h1>
<div th:text="${url}"></div>
<div th:text="${exception.message}"></div>
</body>
</html>

定义全局异常处理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

@ControllerAdvice
public class GlobalExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";

@ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
}

返回错误json

定义全局消息实体类

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
public class ErrorInfo<T> {

public static final Integer OK = 0;
public static final Integer ERROR = 100;

private Integer code;
private String message;
private String url;
private T data;

public Integer getCode() {
return code;
}

public void setCode(Integer code) {
this.code = code;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url;
}

public T getData() {
return data;
}

public void setData(T data) {
this.data = data;
}
}

自定义异常类

1
2
3
4
5
6
public class MyException extends Exception {

public MyException(String message) {
super(message);
}
}

定义全局异常处理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(value = MyException.class)
@ResponseBody
public ErrorInfo<String> jsonErrorHandler(HttpServletRequest req, MyException e) throws Exception {
ErrorInfo<String> r = new ErrorInfo<>();
r.setMessage(e.getMessage());
r.setCode(ErrorInfo.ERROR);
r.setData("Some Data");
r.setUrl(req.getRequestURL().toString());
return r;
}
}

测试全局异常捕获

创建异常方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

@RequestMapping("/zeroException")
public int zeroException(){
return 100/0;
}

@RequestMapping("/json")
public String json() throws MyException {
throw new MyException("发生错误2");
}
}

测试全局异常捕获

  • 启动应用程序
  • 访问http://127.0.0.1:8080/zeroException
  • 访问http://127.0.0.1:8080/json
  • 观察控制台是否有异常输出

修改端口号

Spring boot 默认端口是8080,如果想要进行更改的话,只需要修改applicatoin.properties文件,在配置文件中加入:

1
server.port=9090

配置ContextPath

ContextPath默认路径是/,可以在application.properties修改。

步骤

修改application.properties

1
server.servlet.contextPath=/spring-boot

测试修改是否生效

  • 重新启动应用
  • 访问http://localhost:8080/spring-boot/
标题 说明
SpringBoot2.0之前的配置 server.context-path=/spring-boot
SpringBoot2.0之后的配置 server.servlet.contextPath=/spring-boot

配置定时任务

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

@Configuration
@EnableScheduling
public class SchedulingConfig {
// 每10秒执行一次
@Scheduled(cron = "0/10 * * * * ?")
public void scheduler() {
System.out.println(">>>>>>>>> 自动执行1次");
}
}

配置过滤器

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.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;

@WebFilter(filterName="myFilter",urlPatterns="/*")
public class MyFilter implements Filter {

@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化");
}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("执行过滤操作");
filterChain.doFilter(servletRequest, servletResponse);
}

@Override
public void destroy() {
System.out.println("过滤器销毁");
}
}

配置监听器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class MyServerContextListener implements ServletContextListener {

@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("监听器初始化");
System.out.println(servletContextEvent.getServletContext().getServerInfo());
}

@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("监听器被销毁");
}
}

配置拦截器

MyInterceptor1

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
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

@Configuration
public class MyInterceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println(">>>MyInterceptor1>>>>>>>在请求处理之前进行调用(Controller方法调用之前)");

return true;// 只有返回true才会继续向下执行,返回false取消当前请求
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println(">>>MyInterceptor1>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)");
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println(">>>MyInterceptor1>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)");
}
}

MyInterceptor2

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
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class MyInterceptor2 implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println(">>>MyInterceptor2>>>>>>>在请求处理之前进行调用(Controller方法调用之前)");

return true;// 只有返回true才会继续向下执行,返回false取消当前请求
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println(">>>MyInterceptor2>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)");
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println(">>>MyInterceptor2>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)");
}
}

MyWebAppConfigurer

1
2
3
4
5
6
7
8
9
10
11
12
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

public class MyWebAppConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加一个拦截器,连接以/admin为前缀的 url路径
registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");
registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**");
}
}

改变自动扫描的包

Spring Boot默认会扫描启动类同包以及子包下的注解。如果想改变 这种扫描包的方式,就需要使用@ComponentScan注解进行指定要扫描的包以及要扫描的类。 示例:

1
2
3
4
5
6
7
8
9
10
11
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages={"包名1","包名2","包名..."})
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

配置使用Thymeleaf模板引擎

添加Maven依赖

1
2
3
4
5
<!-- thymeleaf 模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

配置application.properties

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
# ================================================
# Thymeleaf配置
# ================================================
# 是否启用thymeleaf模板解析
spring.thymeleaf.enabled=true
# 是否开启模板缓存(建议:开发环境下设置为false,生产环境设置为true)
spring.thymeleaf.cache=false
# Check that the templates location exists.
spring.thymeleaf.check-template-location=true
# 模板的媒体类型设置,默认为text/html
spring.thymeleaf.content-type=text/html
# 模板的编码设置,默认UTF-8
spring.thymeleaf.encoding=UTF-8
# 设置可以被解析的视图,以逗号,分隔
#spring.thymeleaf.view-names=
# 排除不需要被解析视图,以逗号,分隔
#spring.thymeleaf.excluded-view-names=
# 模板模式设置,默认为HTML5
#spring.thymeleaf.mode=HTML5
# 前缀设置,SpringBoot默认模板放置在classpath:/template/目录下
spring.thymeleaf.prefix=classpath:/templates/
# 后缀设置,默认为.html
spring.thymeleaf.suffix=.html
# 模板在模板链中被解析的顺序
#spring.thymeleaf.template-resolver-order=

编写Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
import java.util.ArrayList;

@Controller
public class HelloController {

@RequestMapping("/index")
public String home(ModelMap modelMap) {

modelMap.put("name", "kevin");

List<String> list = new ArrayList<>();
list.add("kevin a");
list.add("kevin b");
list.add("kevin c");
list.add("kevin d");
modelMap.put("list", list);

return "index";
}
}

编写模板

src/main/resources/templates/index.html,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Index</title>
</head>
<body>
<span th:text="${name}"></span>
<ul>
<li th:each="item : ${list}" th:text="${item}"></li>
</ul>
</body>
</html>

配置使用FreeMarker模板引擎

添加Maven依赖

1
2
3
4
5
<!-- freemarker 模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

配置application.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# ================================================
# FreeMarker配置
# ================================================
# 是否开启模板缓存
spring.freemarker.cache=true
# 编码格式
spring.freemarker.charset=UTF-8
# 模板的媒体类型设置
spring.freemarker.content-type=text/html
# 前缀设置 默认为 ""
spring.freemarker.prefix=
# 后缀设置 默认为 .ftl
spring.freemarker.suffix=.ftl
#spring.freemarker.allow-request-override=false
#spring.freemarker.check-template-location=true
#spring.freemarker.expose-request-attributes=false
#spring.freemarker.expose-session-attributes=false
#spring.freemarker.expose-spring-macro-helpers=false
#spring.freemarker.request-context-attribute=
#spring.freemarker.template-loader-path=classpath:/templates/
#spring.freemarker.view-names=

编写Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
import java.util.ArrayList;

@Controller
public class HelloController {

@RequestMapping("/index")
public String home(ModelMap modelMap) {

modelMap.put("name", "kevin");

List<String> list = new ArrayList<>();
list.add("kevin a");
list.add("kevin b");
list.add("kevin c");
list.add("kevin d");
modelMap.put("list", list);

return "index";
}
}

编写模板

src/main/resources/templates/index.ftl,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<span>${name}</span>
<ul>
<#list list as item >
<li>${item}</li>
</#list>
</ul>
</body>
</html>

集成

集成fastjson

引入依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.22</version>
</dependency>

@Bean注入fastJsonHttpMessageConverters

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
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.HttpMessageConverter;

@SpringBootApplication
public class DemoApplication {

@Bean
public HttpMessageConverters fastJsonHttpMessageConverters(){
// 1.需要先定义一个vonvert转换消息的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();

// 2.添加fastjson的配置信息,比如:是否格式化返回的json数据
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);

// 3.在conver中添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);

HttpMessageConverter<?> converter = fastConverter;
// 4.将vonver添加到converters当中
return new HttpMessageConverters(converter);
}

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

测试

编写Demo实体

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
import com.alibaba.fastjson.annotation.JSONField;

import java.util.Date;

public class Demo {
private int id;
private String name;

@JSONField(format = "yyyy-MM-dd HH:ss:mm")
private Date createTime;//创建时间

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}

编写TestController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

@RestController
public class TestController {
@RequestMapping("/testfastjson")
public Demo getDemo(){
Demo demo = new Demo();
demo.setId(1);
demo.setName("兵兵");
demo.setCreateTime(new Date());
return demo;
}
}

测试

启动应用,访问http://localhost:8080/testfastjson,观察序列化的结果。

集成Swagger2

添加依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>

创建Swagger2配置类

在Application.java同级创建Swagger2的配置类Swagger2。

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
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import org.springframework.context.annotation.Bean;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;

@Configuration
@EnableSwagger2
public class Swagger2 {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.demo"))
.paths(PathSelectors.any())
.build();
}

private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("XX项目接口文档")
.description("XX项目描述")
.termsOfServiceUrl("https://www.wanglibing.com")
.contact(new Contact("王丽兵", "www.wanglibing.com", "iamwanglibing@qq.com"))
.licenseUrl("")
.version("1.0")
.build();
}
}

给接口添加注解

实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class User {
private int id;
private String name;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

带注解的Controller类

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
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.util.*;

@RestController
@RequestMapping(value = "/users")
public class UserController {

@ApiOperation(value = "获取用户列表", notes = "")
@RequestMapping(value = {""}, method = RequestMethod.GET)
public List<User> getUserList() {

return null;
}

@ApiOperation(value = "创建用户", notes = "根据User对象创建用户")
@ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
@RequestMapping(value = "", method = RequestMethod.POST)
public String postUser(@RequestBody User user) {
return "success";
}

@ApiOperation(value = "获取用户详细信息", notes = "根据url的id来获取用户详细信息")
@ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long")
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public User getUser(@PathVariable Long id) {
return null;
}

@ApiOperation(value = "更新用户详细信息", notes = "根据url的id来指定更新对象,并根据传过来的user信息来更新用户详细信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long"),
@ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
})

@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public String putUser(@PathVariable Long id, @RequestBody User user) {
return "success";
}

@ApiOperation(value = "删除用户", notes = "根据url的id来指定删除对象")
@ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long")
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public String deleteUser(@PathVariable Long id) {
return "success";
}
}

测试

访问 http://localhost:8080/swagger-ui.html

集成MyBatis

添加依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>

配置application.properties

1
2
3
4
spring.datasource.url=jdbc:mysql://192.168.0.11:3306/springbootsolution
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

集成Redis

增加依赖

1
2
3
4
5
6
<!-- Redis  Start-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>

配置application.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=192.168.0.11
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=5000

集成Druid

配置依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.20</version>
</dependency>

配置属性

application.properties增加如下:

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
# 数据库访问配置
# 主数据源,默认的
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456

# 下面为连接池的补充设置,应用到上面所有数据源中
# 初始化大小,最小,最大
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,log4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多个DruidDataSource的监控数据
#spring.datasource.useGlobalDataSourceStat=true

配置Web界面的监控后台

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
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DruidConfiguration {
/**
* 注册一个StatViewServlet
* @return
*
*/
@Bean
public ServletRegistrationBean DruidStatViewServle2() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
// 添加初始化参数:initParams
// 白名单:
servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
// 登录查看信息的账号密码.
servletRegistrationBean.addInitParameter("loginUsername", "admin");
servletRegistrationBean.addInitParameter("loginPassword", "admin");
// 是否能够重置数据.
servletRegistrationBean.addInitParameter("resetEnable", "false");
return servletRegistrationBean;
}
/**
* 注册一个:filterRegistrationBean
* @return
*
*/
@Bean
public FilterRegistrationBean druidStatFilter2() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
// 添加过滤规则.
filterRegistrationBean.addUrlPatterns("/*");
// 添加不需要忽略的格式信息.
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
}

测试配置

  • 启动项目
  • 访问http://localhost:8080/druid/index.html
  • 用户名:admin
  • 密码:admin

集成MongoDB

集成RabbitMQ

集成EHCache

集成Shiro

集成Elasticsearch

Spring Boot Actuator

配置 Spring Boot Actuator

配置pom.xml

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

配置application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
management:
endpoints:
web:
exposure:
include: '*'

endpoint:
health:
show-details: always

metrics:
tags:
application: actuator-demo

配置Prometheus

prometheus.yml内容:

1
2
3
4
- job_name: 'actuator-demo'
metrics_path: '/prometheus'
static_configs:
- targets: ['172.16.2.204:8080']

常见错误

bootstrap 配置文件不生效

问题描述

bootstrap.properties 或 bootstrap.yaml不生效。

解决办法

1
2
3
4
5
<!--需要引入该jar才能使bootstrap配置文件生效-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</dependency>

Thymeleaf-meta标签导致的错误

错误描述

Spring Boot 使用 Thymeleaf 模板引擎报错,错误提示:“org.xml.sax.SAXParseException: 元素类型 "meta" 必须由匹配的结束标记 "

" 终止。”

解决办法

标签加上结束标记。 修改前

1
<meta charset="UTF-8">

修改后

1
<meta charset="UTF-8" />

打包时找不到类

原因分析

打包webapp时,没有把 webapp 所引用的 jar 包打包进来,webapp.jar 中没有包含 service.jar 、dao.jar 导致扫描时找不到类。

解决办法

pom.xml中增加如下代码:

1
2
3
4
5
6
7
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

SLF4J: Class path contains multiple SLF4J bindings

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.0.4.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

WebMvcConfigurerAdapter解决办法

问题描述

在使用SpringBoot中使用WebMvcConfigurerAdapter时,编译器提示该类已经被遗弃。

1
2
3
4
5
6
7
8
9
10
11
12
public class UaaServiceApplication extends WebMvcConfigurerAdapter {

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

@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/oauth/confirm_access").setViewName("authorize");
}
}

以下WebMvcConfigurerAdapter 比较常用的重写接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/** 解决跨域问题 **/
public void addCorsMappings(CorsRegistry registry) ;
/** 添加拦截器 **/
void addInterceptors(InterceptorRegistry registry);
/** 这里配置视图解析器 **/
void configureViewResolvers(ViewResolverRegistry registry);
/** 配置内容裁决的一些选项 **/
void configureContentNegotiation(ContentNegotiationConfigurer configurer);
/** 视图跳转控制器 **/
void addViewControllers(ViewControllerRegistry registry);
/** 静态资源处理 **/
void addResourceHandlers(ResourceHandlerRegistry registry);
/** 默认静态资源处理器 **/
void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);

解决办法

直接继承WebMvcConfigurationSupport

1
2
3
4
5
6
7
8
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/oauth/confirm_access").setViewName("authorize");
}
}

直接实现WebMvcConfigurer

1
2
3
4
5
6
7
8
9
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/oauth/confirm_access").setViewName("authorize");
}
}

SpringBoot2.x版本JedisConnectionFactory设置连接已过时解决办法

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package com.wanglibing.redis.config;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.time.Duration;

/**
* @author: iamwlb
* @date: 2018/8/26 17:57
*/
@Configuration
public class JedisRedisConfig {
@Value("${spring.redis.database}")
private int database;
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.jedis.pool.min-idle}")
private int minIdle;
@Value("${spring.redis.jedis.pool.max-wait}")
private long maxWaitMillis;
@Value("${spring.redis.jedis.pool.max-total}")
private int maxTotal;

@Bean
public JedisClientConfiguration getJedisClientConfiguration() {
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder JedisPoolingClientConfigurationBuilder = (
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder) JedisClientConfiguration.builder();
GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
genericObjectPoolConfig.setMaxIdle(maxIdle);
genericObjectPoolConfig.setMinIdle(minIdle);
genericObjectPoolConfig.setMaxWaitMillis(maxWaitMillis);
genericObjectPoolConfig.setMaxTotal(maxTotal);
return JedisPoolingClientConfigurationBuilder.poolConfig(genericObjectPoolConfig).build();
}

@Bean
JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration ();
redisStandaloneConfiguration.setDatabase(database);
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setPassword(RedisPassword.of(password));

JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfiguration = JedisClientConfiguration.builder();
jedisClientConfiguration.connectTimeout(Duration.ofMillis(timeout));

JedisConnectionFactory factory = new JedisConnectionFactory(redisStandaloneConfiguration,
jedisClientConfiguration.build());
return factory;
}

@Bean
public JedisPool redisPoolFactory() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);

JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);
return jedisPool;
}
}

在线工具

参考

JPA

坚持原创技术分享,您的支持将鼓励我继续创作!
0%