SpringBoot
基础入门
快速开始
导入依赖
<!--父项目-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.6</version>
</parent>
<!--web工程依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
编写主类
//声明这是一个SpringBoot工程
@SpringBootApplication
public class Main {
public static void main(String[] args) {
//运行main方法即可让整个工程运行 不再需要配置tomcat服务器
SpringApplication.run(Main.class, args);
}
}
编写业务
//声明一个Controll类 类中所有方法的返回值都会原样返回给客户端
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello(){
return "hello springboot";
}
}
配置文件
SpringBoot的配置文件在classpath路径下使用application.properties或application.yml表示
所有可用的配置可以在官方文档中查看
server:
port: 8088
快速部署
使用SpringBoot提供的maven插件可以快速将工程打为jar包,直接运行即可。
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!--设置主类-->
<mainClass>com.xinnn.boot.Main</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
自动配置
依赖管理
SpringBoot使用父项目进行依赖管理,在父项目的pom文件中,声明了常用的开发中所需插件的版本号。在子项目需要引用这些插件时,无需写版本号。
<!--
自定义版本号
1.在 spring-boot-dependencies 里面查看规定该插件版本使用的 key
2.在当前pom文件中重写
-->
<properties>
<mysql.version>8.0.1</mysql.version>
</properties>
**starter场景启动器 **
spring-boot-starter-*,*号表示某种场景- 引入starter后,该场景下的所有依赖会自动导入
- SpringBoot官方的starter列表
- *-spring-boot-starter 是第三方提供的
- 所有场景启动器都依赖于
spring-boot-starter
自动配置组件
-
自动配置Tomcat
-
自动配置SpringMVC
-
自动配置Web开发的常用功能(字符编码、视图解析、文件上传解析等)
-
默认的包结构
- 主程序所在的包及子包中的所有组件都会被扫描
- 指定扫描路径
@SpringBootApplication(scanBasePackages = "com.xinnn")
-
配置拥有默认值
-
按需加载所有自动配置项
- 所有的自动配置功能都在
spring-boot-autoconfigure里面
- 所有的自动配置功能都在
容器功能
组件添加
1. @Configuration
/**
* 声明这是一个配置类
* @Configuration(proxyBeanMethods = true)
* proxyBeanMethods:代理bean的方法
* 等于 true 时,是full模式,每个bean方法返回的对象都是单例的
* 等于 false 时,是lite模式,每个bean方法返回的对象都是新创建的
* 类组件之间无依赖关系使用lite模式,加速启动;有依赖使用full模式 确保得到的是同一个
*/
@Configuration(proxyBeanMethods = true)
public class SpringConfig {
@Bean //在容器中添加组件 组件的id等于方法的名字 类型是方法的返回类型
public User user(){
User user = new User("张三", 18);
//user组件依赖于cat组件 设置proxyBeanMethods=true 确保依赖的组件是同一个。
user.setCat = (cat());
return user;
}
@Bean("tom") //自定义id cat方法在容器中的id为 tom
public Cat cat(){
return new Cat("小黑");
}
}
//测试代码
//scanBasePackages 指定扫描组件的包 默认为主类同级和子包
@SpringBootApplication(scanBasePackages = "com.xinnn")
public class Main {
public static void main(String[] args) {
//获取IOC容器
ConfigurableApplicationContext applicationContext = SpringApplication.run(Main.class, args);
//获取容器中所有组件的id
for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
//获取user组件
User user = applicationContext.getBean("user", User.class);
//获取cat组件
Cat cat = applicationContext.getBean("tom", Cat.class);
}
}
2.@import
//在容器中创建这两个类型的组件,默认id是类的全类名
@Import({User.class, Date.class})
3.@Conditional
//条件装配 条件满足时才执行 这是根注解 有非常多的派生注解 可以被标识在方法和类上
@Conditional
//容器中存在名称为tom的组件时才会执行被标识的方法或类
@ConditionalOnBean(name = "tom")
4.@ImportResource
//通过配置文件导入bean
@ImportResource("classpath:spring.xml")
配置绑定
1.@Component + @ConfigurationProperties
//声明一个组件
@Component
//配置绑定 properties文件中配置的前缀 car.name=byd
@ConfigurationProperties(prefix = "car")
public class Car{
//自动赋值为 byd
private String name;
}
2.@EnableConfigurationProperties + @ConfigurationProperties
//在配置类上声明 Car类会自动注册为容器中的组件并开始配置绑定
@EnableConfigurationProperties(Car.class)
//在Car类上声明 用法同上
@ConfigurationProperties(prefix = "car")
自动配置原理
引导加载自动配置类
@SpringBootApplication
//这个注解可以被拆分为
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
1.@SpringBootConfiguration
表示这是一个SpringBoot的配置类
2.@ComponentScan
需要扫描的路径
3.@EnableAutoConfiguration
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
-
@AutoConfigurationPackage
@Import({AutoConfigurationPackages.Registrar.class}) //利用Registrar给容器导入一系列组件 -
@Import({AutoConfigurationImportSelector.class})
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata){} //给容器批量导入组件SpringBoot会从所有
META-INF/spring.factories中扫描需要加载的组件在
spring-boot-autoconfigure-2.3.4.RELEASE.jar下写死了127个场景的的所有配置类
按需加载
通过 @Conditional条件装配注解,所有场景的自动配置类会按需加载
修改默认配置
//配置文件上传解析器
@Bean
//容器中有这个组件
@ConditionalOnBean(MultipartResolver.class)
//容器中没有这个名字 multipartResolver 的组件
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
//给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
//相当于对原有的对象进行重命名 文件上传解析器的名字需要符合规范
return resolver;
}
SpringBoot默认在底层配置好了所有组件。如果需要自定义配置时,要么通过@Bean注解对底层的组件进行覆盖,SpringBoot会使用定义的组件,要么修改配置文件里面底层组件需要的值。
便捷开发
Lombok
通过注解提供实体类的Get、Set方法和有参无参构造器
-
引入依赖
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> -
Idea安装lombok插件
-
在实体类上加上注解
//get,set方法 @Data //全参构造器 @AllArgsConstructor //无参构造器 @NoArgsConstructor //toString方法 @ToString //引入日志 @Slf4j //输出日志 log.debug("test");
dev—tools
引入依赖,当代码或页面发生改动时。使用 Ctorl + F9可快速重新部署
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
Spring Initailizr(SpringBoot项目初始化向导)
使用Idea新建一个Spring Initailizr项目,定义项目名、包名、需要的依赖后,Idea会自动创建Springboot项目,同时配置好依赖、项目结构和主类。
核心功能
配置文件 YAML
基本语法
以key: value的形式存储数据,: 号后面有一个空格;大小写敏感;缩进使用空格表示,不能使用tab;一次缩进表示一个层级关系;#表示注释;字符串不需要引号,'' 单引号会将转义字符作为字符串输出,""双引号会将转义字符原样输出
数据类型
-
字面量 基本数据类型 单个的值
name: jony date: 2022/11/11 12:00:00 num: 11 -
对象、Map 多个键值对
# 行内写法 student: {name:jony,age:18} student: name: jony age: 18 -
数组、集合
# 行内写法 names: [jony,jike] names: - jony - jike
配置提示
使用插件开启配置文件提示
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
Web开发
资源访问
静态资源目录
SpringBoot默认规定的静态资源目录有:"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/",
当静态资源放在这些目录中时,使用项目地址+静态资源名字即可访问,不用写二级文件夹。
请求先通过 DispatcherServlet去控制层里面匹配路径,匹配不到再使用 DefaultServlet去匹配静态资源,再匹配不到就抛出错误
修改访问路径
spring:
mvc:
# 添加访问前缀
# 此时访问静态资源链接为 项目地址 + 访问前缀 + 资源名
static-path-pattern: /sta/**
web:
resources:
# 自定义静态资源目录 数组类型
static-locations: classpath:/zystatic
# 禁用所有静态资源
add-mappings: false
WebJars
webjars是将常用的静态资源jquery、boostrap等打成jar包的形式,通过maven引用到项目中。/webjars/**的目录会自动映射
欢迎页和网站Favicon
在静态资源目录下放置 index.html和 favicon.ico文件
如果配置了静态资源的访问前缀,那么会失效
请求参数处理
rest风格使用
rest的使用和SpringMVC中一致,但是不再需要自己去配置 HiddenHttpMethodFilter,SpringBoot在底层已经配置好了,只需要在配置文件中启用。
spring:
mvc:
hiddenmethod:
filter:
enabled: true
普通参数和注解
-
方法参数
@GetMapping("/test/{id}/{name}") public Map<String, Object> test( //获取请求路径参数 @PathVariable("id")Integer id, @PathVariable("name")String name, //将所有请求路径参数封装到Map集合 key为注解中的变量 @PathVariable Map<String, String> pathMap, //获取客户端标头 @RequestHeader("User-Agent")String agent, //获取所有标头 @RequestHeader Map<String, String> headerMap, //获取请求参数 @RequestParam("user")String user, @RequestParam("age")Integer age, //获取所有请求参数 @RequestParam Map<String, String> requestMap, //获取指定cookie中的值 @CookieValue("_uname") String uname, //根据key获取Cookie @CookieValue("_uname")Cookie cookie //获取post请求体的值 @RequestBody String body ){} -
转发参数
@GetMapping("/test") public String test(Model model){ //Model中的数据最后会保存到Request请求中 model.addAttribute("name", "jony"); return "forward:/index"; } @GetMapping("/index") @ResponseBody //@RequestAttribute从请求中获取参数 public String index(@RequestAttribute("name")String name){ return name; } -
矩阵变量
//矩阵变量使用 ; 号进行分割,参数表现形式为key=value,一个key有多个value的话,value之间用 , 号分隔 //矩阵变量必须配合路径参数使用! //url = /index/1;name=jony;star=lanqiu,zuqiu @GetMapping("/index/{id}") @ResponseBody public Map<String, Object> index(@MatrixVariable("name")String name, @MatrixVariable("star")List<String> list, @PathVariable("id")Integer id){ Map<String, Object> map = new HashMap<>(); map.put("name", name); map.put("star", list); map.put("id", id); return map; } //{"star":["zuqiu","lanqiu"],"name":"jony","id":1} //有多个路径参数时 // url = /index/10;sname=xiaoming/20;tname=ahua @GetMapping("/index/{stuId}/{theId}") @ResponseBody public Map<String, Object> index(@MatrixVariable(value = "sname", pathVar = "stuId")String sname, @MatrixVariable(value = "tname", pathVar = "theId")String tname, @PathVariable("stuId")Integer stuId, @PathVariable("theId")Integer theId){ Map<String, Object> map = new HashMap<>(); map.put("sname", sname); map.put("tname", tname); map.put("stuId", stuId); map.put("theId", theId); return map; } //{"stuId":10,"sname":"xiaoming","tname":"ahua","theId":20}SpringBoot默认会移除路径 ; 号后面的参数,如果需要使用矩阵变量,需要手动开启
//1. 使用@Bean重新声明 WebMvcConfigurer 中的 PathMatchConfigurer @Bean public PathMatchConfigurer pathMatchConfigurer(){ PathMatchConfigurer pathMatchConfigurer = new PathMatchConfigurer(); UrlPathHelper urlPathHelper = new UrlPathHelper(); //关闭路径截取 urlPathHelper.setRemoveSemicolonContent(false); pathMatchConfigurer.setUrlPathHelper(urlPathHelper); return pathMatchConfigurer; } //2. 继承 WebMvcConfigurer 接口,重写其中的configurePathMatch方法 @Override public void configurePathMatch(PathMatchConfigurer configurer) { UrlPathHelper urlPathHelper = new UrlPathHelper(); //关闭路径截取 urlPathHelper.setRemoveSemicolonContent(false); configurer.setUrlPathHelper(urlPathHelper); } -
封装为POJO类
当控制器方法的参数为实体类时,SpringBoot可以自动将请求参数封装为实体类的属性,支持实体类的级联封装。需要注意的是请求参数的name需要和实体类属性同名,需要级联封装时请求参数的name命名为
实体类的属性名+类中的属性(student.name)
自定义参数解析器 封装实体类
和开启矩阵变量一样,也有两种方法
//WebMvcConfigurer 重写其中的addFormatters方法 添加自定义的参数解析器
@Override
public void addFormatters(FormatterRegistry registry) {
//Converter负责将请求参数转换为指定的数据类型 将String转为Book类型
registry.addConverter(new Converter<String, Book>(){
@Override
//具体的转换方法
public Book convert(String source) {
String[] stu = source.split(",");
Integer id = Integer.parseInt(stu[0]);
return new Book(id, stu[1]);
}
});
}
//具体的请求 为Post请求 Student类包含了的一个Book类的实体类型 通过一个参数为Book类的两个属性赋值
http://localhost:8088/test/stu?id=1&name=jony&age=18&book=2,语文
数据响应和内容协商
内容协商:客户端会以请求头的方式告诉服务端它能接收什么样的数据,服务端拿到这个请求头之后会根据自身所支持的数据格式,最终决定返回哪种类型格式的数据。
服务器可以根据不同的请求头参数 Accept 返回不同类型的数据
-
返回Json格式的数据
application/json只需要在方法上标明@ResponseBody注解或者在控制器类上标明@RestController注解就可自动返回Json数据
//返回的数据 { "id": 1, "name": "jony", "age": 18, "book": { "id": 2, "name": "语文" } } -
返回XML的数据
application/xml引入Jackson的XMl依赖
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency> <!--返回的数据--> <Student> <id>1</id> <name>jony</name> <age>18</age> <book> <id>2</id> <name>语文</name> </book> </Student>
以请求参数方式的内容协商功能
开启以请求参数方式的内容协商功能后,SpringBoot的优先级会以请求参数中 format参数指定的格式为准,如果format指定的内容类型不支持,再以Accept标头的为准。
//开启
spring:
mvc:
contentnegotiation:
favor-parameter: true
自定义MessageConverter
添加自定义的协议支持,可以按照请求协议的不同,分发多种不同格式的数据
//添加根据标头请求协议的自定义协议
//WebMvcConfigurer中的extendMessageConverters方法
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//要定义协议支持的类型 也可以为Object
converters.add(new HttpMessageConverter<Book>() {
@Override
public List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
return HttpMessageConverter.super.getSupportedMediaTypes(clazz);
}
@Override
//是否可读 用于请求参数的转换
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return false;
}
@Override
//是否可写 用于返回参数的转换
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return clazz.isAssignableFrom(Book.class);
}
@Override
//支持的协议
public List<MediaType> getSupportedMediaTypes() {
//自定义的协议名称
return MediaType.parseMediaTypes("application/x-xin");
}
@Override
public Book read(Class<? extends Book> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
@Override
//转换方法的具体实现
public void write(Book book, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
//通过反射获取类中的所有参数
Class bookClass = book.getClass();
Field[] bookFields = bookClass.getDeclaredFields();
StringBuilder msg = new StringBuilder();
for(Field field : bookFields){
field.setAccessible(true);
try {
Object bookObj = field.get(book);
//将参数拼接起来 中间用;分隔
msg.append(String.valueOf(bookObj)).append(";");
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
//将主体内容返回给客户端
outputMessage.getBody().write(msg.toString().getBytes());
}
});
}
//添加根据请求参数的自定义协议
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
Map<String, MediaType> map = new HashMap<>();
//添加数据类型 原生支持的json和xml也必须添加
//设置请求参数的自定义媒体类型处理器时 必须先添加该协议
map.put("xin", MediaType.parseMediaType("application/x-xin"));
map.put("json", MediaType.APPLICATION_JSON);
map.put("xml", MediaType.APPLICATION_XML);
//根据请求参数的解析器
ParameterContentNegotiationStrategy parameterContentNegotiationStrategy = new ParameterContentNegotiationStrategy(map);
//根据标头的解析器 不设置的话无法通过标头进行内容协商
HeaderContentNegotiationStrategy headerContentNegotiationStrategy = new HeaderContentNegotiationStrategy();
//把新生成的解析器添加到配置中
configurer.strategies(Arrays.asList(parameterContentNegotiationStrategy, headerContentNegotiationStrategy));
}
文件上传
//请求必须为Post 前端form表单要设置 enctype="multipart/form-data"
@PostMapping("/upload")
//@RequestPart("author")文件输入框的name属性值 使用MultipartFile[]接受多个文件
public String uploadFile(@RequestParam("email") String email, @RequestParam("username") String username,
@RequestPart("author")MultipartFile author, @RequestPart("imgs") MultipartFile[] imgs) throws IOException {}
spring:
servlet:
multipart:
# 单个文件最大大小
max-file-size: 10MB
# 单次上传总文件 最大大小
max-request-size: 100MB
错误处理
默认情况下,对于浏览器SpringBoot提供/error中的错误映射,这时返回的是一个白页;对于Json会响应Json格式的错误消息。
如果需要替换默认的错误页,可以在模板或者静态资源路径下新建/error目录,里面的4xx.html、5xx.html会被自动解析(根据http的错误状态码查找页面,精确查找不到就模糊查找,再查找不到就返回默认页面)
要完全自定义错误行为,可以实现ErrorController接口
//1、声明异常处理类
@ControllerAdvice
public class MyExceptionHandler {
//该方法能够处理的异常类型
@ExceptionHandler({NullPointerException.class, ArrayIndexOutOfBoundsException.class})
public String nullExceptionHandler(){
return "error";
}
}
//2、自定义异常处理类 当抛出这个自定义异常时 会返回 value状态码和自定义的消息 message
@ResponseStatus(value = HttpStatus.CONFLICT, reason = "message")
public class ResponeException extends RuntimeException{
public ResponeException(String message){
super(message);
}
}
自定义异常处理器
@Component
//优先级为最高 不然会被默认的异常处理器处理
@Order(value = Ordered.HIGHEST_PRECEDENCE)
public class CustomException implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
try {
//直接返回错误码和错误消息
response.sendError(512, "error");
} catch (IOException e) {
throw new RuntimeException(e);
}
//返回一个ModelAndView对象 不然上层方法会忽略当前异常处理器并继续执行下一个
return new ModelAndView();
}
}
使用原生Servlet组件
-
使用@ServletComponentScan注解
在项目主类上使用
@ServletComponentScan注解声明原生Servlet组件所在的包名,SpringBoot会自动扫描。需要配合Servlet原生的注解使用。@WebServlet、@WebFilter、@WebListener -
使用
RegistrationBean注册Servlet组件@Configuration public class ServletConfig { @Bean //添加Servlet组件 public ServletRegistrationBean<MyServlet> myServlet(){ MyServlet servlet = new MyServlet(); //路径可以是多个 return new ServletRegistrationBean<>(servlet, "/servlet", "/ssss"); } @Bean //添加Listener组件 public ServletListenerRegistrationBean<MyContextListener> MyContextListener(){ MyContextListener myContextListener = new MyContextListener(); return new ServletListenerRegistrationBean<>(myContextListener); } } // FilterRegistrationBean 添加Filter组件
嵌入式Web服务器
-
切换Web容器
SpringBoot默认支持的Web容器有
tomcat、jetty、undertow,这3个Web容器在SpringBoot的Web场景启动器里面都有自动配置类,starter-web默认的Web容器是tomcat。如果需要更换Web容器,只需要在starter里面排除之前的Web容器,再引入需要容器的依赖即可。<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> -
定制Web容器
- SpringBoot所有关于Web的配置都在
server.前缀下,只需要修改配置文件即可。
-
实现
WebServerFactoryCustomizer接口,这是SpringBoot提供的Web容器工厂自定义类@Component public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> { @Override //自定义容器配置的方法 public void customize(ConfigurableServletWebServerFactory server) { server.setPort(9000); } }
-
直接使用
ConfigurableServletWebServerFactory@Bean ConfigurableServletWebServerFactory webServerFactory(){ UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory(); factory.setPort(8088); return factory; }
- SpringBoot所有关于Web的配置都在
数据访问
SpringBoot关于数据访问都在 spring-boot-startrt-data-*场景启动器下。
导入 spring-boot-startrt-data-jdbc场景后,需要自己导入数据库驱动。这个场景启动器默认开启了数据源、事务管理器、JdbcTemplate(操作数据库)的自动配置,默认使用的连接池是HikariDataSource
关于数据源的配置在 spring-datasource下
关于JdbcTemplate的配置在 spring-jdbc下
使用第三方数据源
导入第三方数据源有两种方式
- 自定义数据源的配置类
- 使用数据源提供的starter
自定义方式
以 Druid为例,参照官方文档alibaba/druid
spring:
datasource:
url: jdbc:postgresql://120.46.151.166:5432/zhsx
username: lisang
password: 00000...
driver-class-name: org.postgresql.Driver
max-active: 10
# 开启Druid的Sql统计和Sql防火墙
filters: stat, wall
@Configuration
//Druid数据源的配置类
public class DruidDatasourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() throws SQLException {
DruidDataSource dataSource = new DruidDataSource();
//开启了配置文件绑定 参数直接写在配置文件中即可
// dataSource.setUrl("//");
// dataSource.setMaxActive(10);
// dataSource.setFilters("stat, wall");
return dataSource;
}
//配置Druid的监控面板
@Bean
public ServletRegistrationBean<StatViewServlet> statViewServlet(){
StatViewServlet servlet = new StatViewServlet();
ServletRegistrationBean<StatViewServlet> druidServlet = new ServletRegistrationBean<>(servlet, "/druid/*");
druidServlet.addInitParameter("loginUsername", "admin");
druidServlet.addInitParameter("loginPassword", "admin");
return druidServlet;
}
//配置Druid的Web统计
@Bean
public FilterRegistrationBean<WebStatFilter> webStatFilter(){
WebStatFilter statFilter = new WebStatFilter();
FilterRegistrationBean<WebStatFilter> druidWebStatFilter = new FilterRegistrationBean<>(statFilter);
druidWebStatFilter.addUrlPatterns("/*");
druidWebStatFilter.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return druidWebStatFilter;
}
}
使用druid-spring-boot-starter
-
导入
druid-spring-boot-starter依赖 -
编写配置文件,参考官方文档
spring: datasource: # 数据源的连接配置 url: jdbc:postgresql://120.46.151.166:5432/zhsx username: lisang password: 00000... driver-class-name: org.postgresql.Driver druid: # 配置数据统计 stat sql监控 wall sql防火墙 filters: stat,wall # 连接池中的最大活跃连接数 max-active: 10 # 配置web监控 web-stat-filter: enabled: true url-pattern: /* exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' # 配置druid后台页面 stat-view-servlet: enabled: true url-pattern: /druid/* login-password: admin login-username: admin reset-enable: false filter: wall: config: # 阻止所有delete语句执行 delete-allow: false # 配置spring监控 aop-patterns: com.xinnn.boot.controller.*
整合MyBatis
参考官方文档
mybatis:
# mapper接口配置文件所在的路径
mapper-locations: classpath:mappers/*.xml
# config-location 和 configuration 不能同时存在
# config-location: classpath:mybatis/
# 驼峰映射
type-aliases-package: com.xinnn.boot.pojo
# 配置文件中的配置项
configuration:
map-underscore-to-camel-case: true
注解模式,直接将操作数据库的语句写在Mapper接口的方法上。
@Mapper
public interface ScenicMapper {
@Select("select * from t_scenic where id = #{id}")
//可以通过 @Options 注解开启一直在sql标签里的配置
Scenic getScenic(@Param("id") Integer id);
}
混合模式,一个Mapper接口里面,一部分方法使用注解,另一部分方法使用xml文件。
@Select("select * from t_scenic where id = #{id}")
Scenic getScenic(@Param("id") Integer id);
void deleteScenicById(@Param("id") Integer id);
<delete id="deleteScenicById">
delete from t_scenic where id = #{id}
</delete>
整合MyBatis-Plus
参考官方文档
-
引入
mybatis-plus-boot-starter依赖,依赖里面自带了mybatis。 -
编写
mapper接口和service层@Mapper //只需要继承 BaseMapper 即可,泛型是这个接口对应的实体类 不用编写Sql语句 mybatis-plus默认封装好了 public interface UserMapper extends BaseMapper<User> {} //server接口 一样需要继承 IService 这个顶级接口 public interface UserService extends IService<User> {} @Service //service接口的实现类需要继承mybatis-plus提供的 ServiceImpl public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {} -
直接在controller层中调用
UserServiceImpl即可 -
开启分页功能
@Configuration //mybatis-plus的配置类 public class MyBatisPlusConfig { @Bean //配置MyBatisPlus拦截器 public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); //分页插件的配置类 PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); //开启溢出处理 paginationInnerInterceptor.setOverflow(true); //每一页的条数 paginationInnerInterceptor.setMaxLimit(3L); //数据库的类型 paginationInnerInterceptor.setDbType(DbType.POSTGRE_SQL); mybatisPlusInterceptor.addInnerInterceptor(paginationInnerInterceptor); return mybatisPlusInterceptor; } } //controller层方法 //pageNum是需要获取数据的页码 Page<UserS> page = new Page<>(pageNum, 3); //page对象封装了分页的详细内容 page = userService.page(page, null);
整合NOSQL(Redis)
引入 spring-boot-starter-data-redis依赖
spring:
# reids的配置都在 spring.redis 下
redis:
host: localhost
password: 00000...
port: 6379
database: 0
# 声明 redisTempletes底层要使用那个客户端操作redis lettuce和jedis是可选的 lettuce默认选择
# 如果需要使用 jedis 那么还需要导入jedis的依赖
client-type: lettuce
lettuce:
pool:
max-active: 10
//对redis的简单操作
ValueOperations<String, String> redisStringTemplate = redisTemplate.opsForValue();
redisStringTemplate.set("hello", "world");
String hello = redisStringTemplate.get("hello");
单元测试
整合JUnit5
SpringBoot默认以JUnit5作为单元测试默认库,想要使用只需要引入 spring-boot-starter-boot依赖即可。
JUnit5由3个模块组成:
- JUnit Platform: Junit Platform是在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入。
- JUnit Jupiter: JUnit Jupiter提供了JUnit5的新的编程模型,是JUnit5新特性的核心。内部 包含了一个测试引擎,用于在Junit Platform上运行。
- JUnit Vintage: JUnit Vintage提供了兼容JUnit4.x,Junit3.x的测试引擎。
//在测试类上标注了 @SpringBootTest 后,这个测试类会以Spring的单元测试驱动运行,可以使用Spring的全部功能
@SpringBootTest
public class MyTest {
@Test
//测试方法完成后 事务会自动回滚
@Transactional
public void redisTest(){
System.out.println("1");
}
}
JUnit5的常用注解
-
@Test:表示这是一个测试方法
-
@DisplayName:为测试类和测试方法设置显示的名称
-
@BeforeEach:每个测试方法执行之前执行
-
@AfterEach:每个测试方法执行后执行
-
@BeforeAll:所有测试方法执行之前执行
-
**@AfterAll **:所有测试方法执行之之后执行
-
@Tag:表示单元测试类别
-
@Disable:标注此注解的测试类或者测试方法不执行
-
@Timeout:该测试方法执行超过了指定时间会抛出异常
-
@RepeatedTest:测试方法运行指定次数
-
@ExtendWith:为测试类或测试方法提供扩展类引用(@SpringBootTest底层也使用了该注解)
@BootstrapWith(SpringBootTestContextBootstrapper.class) @ExtendWith({SpringExtension.class})
断言
断言是测试方法的核心,用来对需要满足的条件进行验证,并且在所有测试运行结束后,会有一个详细的测试报告。
JUnit5的断言方法都在 org.junit.jupiter.api.Assertions类里面,都是静态方法。
常用的断言方法
断言方法的格式都是 (你需要的值, 需要测试的值, 自定义的消息提示),通过断言方法没有通过,那么后续的代码都不会执行!
-
assertEquals:判断是否相等
-
assertNotEquals:判断是否不相等
-
assertSame:判断两个对象是否是同一个
-
assertNotSame:判断两个对象是否不是同一个
-
assertTrue:判断是否为true
-
assertFalse:判断是否为false
-
assertNull:判断值是否为null
-
assertNotNull:判断值是否不为null
-
assertArrayEquals:判断两个数组的值是否相等
-
assertAll:组合断言,可以通过
lambda表达式提供多个断言方法。@Test @DisplayName("组合断言") public void testAll(){ Assertions.assertAll( () -> Assertions.assertEquals(1, 1, "返回值不是1"), () -> Assertions.assertArrayEquals(new int[]{1,2}, new int[]{1, 3}, "数组不相等") ); } -
assertThrows:异常断言,配置
lambda使用。如果在测试方法中抛出了指定的异常,那么断言成功,反之断言失败。@Test @DisplayName("异常断言测试") public void testException(){ Assertions.assertThrows(ArrayIndexOutOfBoundsException.class,() -> { int[] nums = new int[2]; //这里数组下标没有越界 没有抛出ArrayIndexOutOfBoundsException异常 那么断言失败 nums[1] = 10; }, "数组下标越界异常"); } -
assertTimeout():超时时间,测试方法执行超过指定时间时断言失败
@Test @DisplayName("测试方法超时") public void testTimeout(){ Assertions.assertTimeout(Duration.ofMillis(500), () -> { Thread.sleep(600); }, "方法执行超时"); } -
fail:通过
fail()方法可以让断言可以使测试直接失败
前置条件
前置条件 Assumptions在 org.junit.jupiter.api包下,包里面的方法类似与断言,但和断言的区别是:条件不满足时断言会直接失败,而前置条件是让方法不在执行,不会报错。
@Test
@DisplayName("前置条件")
public void test(){
Assumptions.assumeTrue(1 == 2);
System.out.println("ok");
}
嵌套测试
通过Java的内部类和 @Nested注解实现嵌套测试,内部类可以使用外部的 @BeforeEach、@AfterEach注解,而外部类无法使用内部类的。
@DisplayName("A stack")
class TestingAStackDemo {
Stack<Object> stack;
@Test
@DisplayName("is instantiated with new Stack()")
void isInstantiatedWithNew() {
new Stack<>();
}
@Nested
@DisplayName("when new")
class WhenNew {
@BeforeEach
//内部类的方法执行时 会先 new 一个 Stack
void createNewStack() {
stack = new Stack<>();
}
@Test
@DisplayName("is empty")
void isEmpty() {
//因为先 new 了一个 Stack 并且里面现在还没有任何元素 断言通过
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("throws EmptyStackException when popped")
void throwsExceptionWhenPopped() {
//现在 stack 里面没有任何元素 无法调用stack的pop方法弹出一个元素 抛出EmptyStackException异常 断言通过
assertThrows(EmptyStackException.class, stack::pop);
}
@Test
@DisplayName("throws EmptyStackException when peeked")
void throwsExceptionWhenPeeked() {
//同上
assertThrows(EmptyStackException.class, stack::peek);
}
@Nested
@DisplayName("after pushing an element")
class AfterPushing {
String anElement = "an element";
@BeforeEach
void pushAnElement() {
stack.push(anElement);
}
@Test
@DisplayName("it is no longer empty")
void isNotEmpty() {
//执行方法会向调用外部的 createNewStack() 在执行内部的 @BeforeEach 这时stack不为空了
assertFalse(stack.isEmpty());
}
@Test
@DisplayName("returns the element when popped and is empty")
void returnElementWhenPopped() {
//stack可以弹出一个元素
assertEquals(anElement, stack.pop());
//弹出一个元素后里面又空了
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("returns the element when peeked but remains not empty")
void returnElementWhenPeeked() {
//同上
assertEquals(anElement, stack.peek());
assertFalse(stack.isEmpty());
}
}
}
}
参数化测试
测试方法可以根据提供的参数进行多次执行
- @ValueSource:指定参数来源,支持基本类型和String、Class类型
- @NullSource:提供一个空参数
- @EnumSource:提供一个枚举参数
- @CsvFileSource:读取
csv文件作为参数 - @MethodSource:使用指定方法的返回值作为参数,返回值需要是一个流类型
Stream
@ParameterizedTest
@DisplayName("int入参")
//使用基本类型作为入参
@ValueSource(ints = {1,2,3,4})
public void testValueSource(int i){
System.out.println(i);
}
//使用方法返回值作为入参
@ParameterizedTest
@DisplayName("method入参")
@MethodSource("getAllUser")
public void testMethodSource(User user){
System.out.println(user.getUsername());
System.out.println(user.getPassword());
}
static Stream<User> getAllUser(){
User user = new User("admin", "admin");
return Stream.of(user);
}
指标监控
对上线的应用进行监控、追踪和控制等,SpringBoot提供了Actuator场景用来快速的对微服务应用进行监控和审计等操作
使用
-
引入
spring-boot-starter-actuator依赖 -
为监控信息开启http支持
# 所有对 actuator 的操作都在 management.** 下 management: endpoints: # 是否开放所有端口信息 enabled-by-default: true # 也可以单独开放某个端口 # endpoint: # health: # enabled: true endpoint: # 让health始终显示详细信息 health: show-details: always web: exposure: # 以web方式暴露所有端口 默认情况大部分端口都是以jmx暴露的 include: '*' -
访问
http://localhost:8080/actuator/,返回的格式为Json。
Actuator Endpoint
| ID | 描述 |
|---|---|
auditevents |
暴露当前应用程序的审核事件信息。需要一个 AuditEventRepository组件。 |
beans |
显示应用程序中所有Spring Bean的完整列表。 |
caches |
暴露可用的缓存。 |
conditions |
显示自动配置的所有条件信息,包括匹配或不匹配的原因。 |
configprops |
显示所有 @ConfigurationProperties。 |
env |
暴露Spring的属性 ConfigurableEnvironment |
flyway |
显示已应用的所有Flyway数据库迁移。 需要一个或多个 Flyway组件。 |
health |
显示应用程序运行状况信息。 |
httptrace |
显示HTTP跟踪信息(默认情况下,最近100个HTTP请求-响应)。需要一个 HttpTraceRepository组件。 |
info |
显示应用程序信息。 |
integrationgraph |
显示Spring integrationgraph 。需要依赖 spring-integration-core。 |
loggers |
显示和修改应用程序中日志的配置。 |
liquibase |
显示已应用的所有Liquibase数据库迁移。需要一个或多个 Liquibase组件。 |
metrics |
显示当前应用程序的“指标”信息。 |
mappings |
显示所有 @RequestMapping路径列表。 |
scheduledtasks |
显示应用程序中的计划任务。 |
sessions |
允许从Spring Session支持的会话存储中检索和删除用户会话。需要使用Spring Session的基于Servlet的Web应用程序。 |
shutdown |
使应用程序正常关闭。默认禁用。 |
startup |
显示由 ApplicationStartup收集的启动步骤数据。需要使用 SpringApplication进行配置 BufferingApplicationStartup。 |
threaddump |
执行线程转储。 |
如果是Web应用程序,还可以使用附加端口:
| ID | 描述 |
|---|---|
heapdump |
返回 hprof堆转储文件。 |
jolokia |
通过HTTP暴露JMX bean(需要引入Jolokia,不适用于WebFlux)。需要引入依赖 jolokia-core。 |
logfile |
返回日志文件的内容(如果已设置 logging.file.name或 logging.file.path属性)。支持使用HTTP Range标头来检索部分日志文件的内容。 |
prometheus |
以Prometheus服务器可以抓取的格式公开指标。需要依赖 micrometer-registry-prometheus。 |
3个常用的Endpoint:
- Health:监控状况
- metrics:运行时指标
- Loggers:日志记录
自定义Health
通过自定义Health,可以添加添加自己想要判断健康状况的组件
//判断网络是否联通
@Component
public class NetworkHealthIndicator extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
Map<String, String> map = new HashMap<>();
long startTime = System.currentTimeMillis();
boolean bl = InetAddress.getByName("120.46.151.166").isReachable(3000);
long time = System.currentTimeMillis() - startTime;
if(bl){
//up表示健康状况良好
builder.up();
map.put("msg", "网络连接正常");
map.put("120.16.151.166", "连接耗时" + time + "毫秒");
}else{
//down表示宕机
builder.down();
map.put("msg", "网络连接失败");
}
//往builder中添加消息体
builder.withDetails(map);
}
}
自定义Info
@Component
//需要继承 InfoContributor 接口
public class MyInfoContributor implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
//添加信息
builder.withDetail("appName", "admin").withDetail("appVersion", "1.0");
}
}
自定义Metrics
@Service
public class ScenicService {
@Autowired
private ScenicMapper scenicMapper;
Counter counter;
public ScenicService(MeterRegistry meterRegistry){
//计数的指标监控
counter = meterRegistry.counter("scenicServer.getScenic.count");
}
public Scenic getScenic(Integer id){
//每次访问这个方法就加1
counter.increment();
return scenicMapper.getScenic(id);
}
}
定制Endpoint
@Component
//这里的id就是后面访问的路径
@Endpoint(id = "main")
public class MyEndpoint {
@ReadOperation
public Map<String, String> getMyEndpoint(){
Map<String, String> map = new HashMap<>();
map.put("msg", "ok");
//map会被序列化为json响应给客户端
return map;
}
@WriteOperation
public void stopMyEndpoint(){
System.out.println("stop");
}
}
可视化面板(spring-boot-admin-starter-server)
参考官方文档
-
服务端
引入
spring-boot-admin-starter-server依赖在程序主类上添加
@EnableAdminServer注解,访问项目地址/applications即可 -
客户端(被监控端)
引入
spring-boot-admin-starter-client依赖更改配置文件
spring: boot: admin: client: # 指向服务端的地址 url: http://localhost:8087 management: endpoints: enabled-by-default: true # web暴露所有的端点 web: exposure: include: '*'
原理解析
Profile功能
-
application-profile
默认配置文件
application.yml任何时候都会被加载环境配置文件
application-{env}.yml只有在指定在指定使用该环境时才会被加载-
配置文件指定
spring: profiles: # 指定加载 application-test.yml 配置文件 active: test -
命令行指定
在通过命令行启动jar包的时候,可以通过命名参数指定
java -jar xxx.jar --spring.profile.active=test # 命令行参数也可以指定配置文件中的任意配置 # 比如 --server.prot=8081 指定端口为8081 如果与配置文件中的配置项重复 那么命令行的优先
默认配置文件和环境配置会同时加载,如果两个配置文件中有配置项重复,会以环境配置文件中的优先。
-
-
@Profile条件装配
@Profile注解可以标注在类和方法上@Configuration //只有使用 prod 环境配置文件的时候,这个组件才会生效 @Profile("prod") public class MyConfig { @Bean ConfigurableServletWebServerFactory webServerFactory(){ UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory(); factory.setPort(8088); return factory; } } -
配置分组
在
application.yml配置文件里面新增spring.profiles.group.分组名字[0]=环境配置文件名称配置项spring.profiles.group.production[0]=proddb spring.profiles.group.production[1]=prodmq # 启动jar包的时候 使用 --spring-profile-active=production 来指定配置文件组 # 组里面包含的环境配置文件都会生效






0 条评论