SpringBoot的核心功能包括,配置文件、web开发、数据访问、单元测试、指标监控等,本文记录SpringBoot核心功能的相关学习过程。
配置文件 properties Java中常见的配置文件,基本格式如下所示:
1 2 3 4 5 6 7 8 9 10 11 str = helloworld dbPort = localhost databaseName = mydb dbUserName = root dbPassword = 123456 dbTable = mytable ip = 192.168.0.9
yaml 简介 YAML 是 “YAML Ain’t Markup Language”(YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:”Yet Another Markup Language”(仍是一种标记语言)。
非常适合用来做以数据为中心的配置文件
基本语法
数据类型
字面量:单个的、不可再分的值。date、boolean、string、number、null
1 2 3 4 5 key1: 123 key2: true key3: "hello" key4: null key5: 2022 /1/13 09 :25:21
对象:键值对的集合,例如:map、hash、set、object等
1 2 3 4 5 6 7 key1:{k1: v1, k2: v2, k3: v3} k: k1: v1 k2: v2 k3: v3
示例 配置一个person对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @ConfigurationProperties(prefix = "person") @Component @Data public class Person { private String userName; private Boolean boss; private Date birth; private Integer age; private Pet pet; private String[] interests; private List<String> animal; private Map<String, Object> score; private Set<Double> salarys; private Map<String, List<Pet>> allPets; } @Data public class Pet { private String name; private Double weight; }
application.yaml文件
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 person: userName: zhangsan boss: false birth: 2019 /12/12 20 :12:33 age: 18 pet: name: tomcat weight: 23.4 interests: [篮球 ,游泳 ] animal: - jerry - mario score: english: first: 30 second: 40 third: 50 math: [131 ,140 ,148 ] chinese: {first: 128 ,second: 136 } salarys: [3999 ,4999.98 ,5999.99 ] allPets: sick: - {name: tom } - {name: jerry ,weight: 47 } - name: dog weight: 100 health: [{name: mario ,weight: 47 },{name: tiger , weight: 180 }]
配置提示 自定义的类和配置文件在配置文件绑定时一般没有提示,加入依赖即可进行提示
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-configuration-processor</artifactId > <optional > true</optional > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <configuration > <excludes > <exclude > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-configuration-processor</artifactId > </exclude > </excludes > </configuration > </plugin > </plugins > </build >
Web开发 SpringMVC自动配置概览
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.(大多场景我们都无需自定义配置)
The auto-configuration adds the following features on top of Spring’s defaults:
Inclusion of ContentNegotiatingViewResolver
and BeanNameViewResolver
beans.
Support for serving static resources, including support for WebJars (covered later in this document )).
Automatic registration of Converter
, GenericConverter
, and Formatter
beans.
自动注册 Converter,GenericConverter,Formatter
Support for HttpMessageConverters
(covered later in this document ).
支持 HttpMessageConverters
(后来我们配合内容协商理解原理)
Automatic registration of MessageCodesResolver
(covered later in this document ).
自动注册 MessageCodesResolver
(国际化用)
Static index.html
support.
Custom Favicon
support (covered later in this document ).
Automatic use of a ConfigurableWebBindingInitializer
bean (covered later in this document ).
自动使用 ConfigurableWebBindingInitializer
,(DataBinder负责将请求数据绑定到JavaBean上)
简单功能分析 静态资源访问 默认的情况下,只需要静态资源放在类路径下,就可以通过资源名访问到静态资源:
/static
/public
/resources
/META-INF/resources
访问 : 当前项目根路径/ + 静态资源名 如:http://localhost:8080/hello.jpg
原理: 静态映射/**。
请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面
改变默认的静态资源路径 默认的静态资源访问是无前缀的,但此时若需要做拦截,与静态资源合理地做区分,而不影响效率,可以将静态资源的前缀进行修改:
1 2 3 4 5 6 7 8 spring: mvc: static-path-pattern: /res/** resources: static-locations: [classpath:/haha/ ]
前后端不分离访问jquery 自动映射 /webjars/**
网址:https://www.webjars.org/
需要在pom.xml
中引入相关的依赖
1 2 3 4 5 <dependency > <groupId > org.webjars</groupId > <artifactId > jquery</artifactId > <version > 3.5.1</version > </dependency >
访问地址:http://localhost:8080/webjars/jquery/3.5.1/jquery.js 后面地址要按照依赖里面的包路径
欢迎页支持
第一种方式:静态资源路径下 index.html
可以配置静态资源路径
但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问
1 2 3 4 5 6 spring: resources: static-locations: [classpath:/haha/ ]
第二种方式:存在controller能处理/index请求
自定义Favicon 将favicon.ico
放入到静态资源目录下即可使网站的图标被替换
请求参数的处理 普通参数与基本注解 注解 @PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue、@RequestBody
使用样例如下所示:
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 @RestController public class ParameterTestController { @GetMapping("/car/{id}/owner/{username}") public Map<String,Object> getCar (@PathVariable("id") Integer id, @PathVariable("username") String name, @PathVariable Map<String,String> pv, @RequestHeader("User-Agent") String userAgent, @RequestHeader Map<String,String> header, @RequestParam("age") Integer age, @RequestParam("inters") List<String> inters, @RequestParam Map<String,String> params, @CookieValue("_ga") String _ga, @CookieValue("_ga") Cookie cookie) { Map<String,Object> map = new HashMap<>(); map.put("age" ,age); map.put("inters" ,inters); map.put("params" ,params); map.put("_ga" ,_ga); System.out.println(cookie.getName()+"===>" +cookie.getValue()); return map; } @PostMapping("/save") public Map postMethod (@RequestBody String content) { Map<String,Object> map = new HashMap<>(); map.put("content" ,content); return map; } @GetMapping("/cars/{path}") public Map carsSell (@MatrixVariable("low") Integer low, @MatrixVariable("brand") List<String> brand, @PathVariable("path") String path) { Map<String,Object> map = new HashMap<>(); map.put("low" ,low); map.put("brand" ,brand); map.put("path" ,path); return map; } @GetMapping("/boss/{bossId}/{empId}") public Map boss (@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge, @MatrixVariable(value = "age",pathVar = "empId") Integer empAge) { Map<String,Object> map = new HashMap<>(); map.put("bossAge" ,bossAge); map.put("empAge" ,empAge); return map; } }
Servlet API WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId
ServletRequestMethodArgumentResolver以上的部分参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override public boolean supportsParameter (MethodParameter parameter) { Class<?> paramType = parameter.getParameterType(); return (WebRequest.class.isAssignableFrom(paramType) || ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType) || HttpSession.class.isAssignableFrom(paramType) || (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) || Principal.class.isAssignableFrom(paramType) || InputStream.class.isAssignableFrom(paramType) || Reader.class.isAssignableFrom(paramType) || HttpMethod.class == paramType || Locale.class == paramType || TimeZone.class == paramType || ZoneId.class == paramType); }
复杂类型 Map、Model(map、model里面的数据会被放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder
使用map,model,request作为Controller的参数,向其中放置数据等于向requestScope中放数据
1 2 Map<String,Object> map, Model model, HttpServletRequest request; request.getAttribute();
Map、Model类型的参数 ,会返回 mavContainer.getModel();得到 —> BindingAwareModelMap 是Model 也是Map
mavContainer.getModel(); 获取到值
自定义对象参数 可以自动类型转换与格式化,可以级联封装。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Data public class Person { private String userName; private Integer age; private Date birth; private Pet pet; } @Data public class Pet { private String name; private String age; }
数据响应与内容协商 数据响应JSON JSON数据响应使用jackson.jar + @ResponseBody
实现功能
首先需要引用依赖pom.xml
:
1 2 3 4 5 6 7 8 9 10 11 12 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-json</artifactId > <version > 2.3.4.RELEASE</version > <scope > compile</scope > </dependency >
给前端自动返回JSON数据的原理如下:
1. 首先在15个返回值解析器中寻找目标类型所需的解析器
2. 通过MessageConverter将对象转换为前端需要的内容
其原理如下所示:
返回值处理器判断是否支持这种类型返回值supportsReturnType
返回值处理器调用handleReturnValue
进行处理
RequestResponseBodyMethodProcessor
解析器可以处理返回值标记了@ResponseBody
注解的。
利用MessageConverters
进行处理将数据重新写为json
格式
内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)
服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据
SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter
,看谁能处理?
得到MappingJackson2HttpMessageConverter
可以将对象写为json
利用MappingJackson2HttpMessageConverter
将对象转为json
再写出去。
支持的返回值解析器有如下几种:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ModelAndView Model View ResponseEntity ResponseBodyEmitter StreamingResponseBody HttpEntity HttpHeaders Callable DeferredResult ListenableFuture CompletionStage WebAsyncTask RequestResponseBodyMethodProcessor;
支持的转换器类型有如下几种:
1 2 3 4 5 6 7 8 0 ByteArrayHttpMessageConverter - 只支持Byte类型的 1 StringHttpMessageConverter - String 2 ResourceHttpMessageConverter - Resource 3 ResourceRegionHttpMessageConverter - ResourceRegion 4 SourceHttpMessageConverter - DOMSource.class \ SAXSource.class) \ StAXSource.class \StreamSource.class \Source.class 5 AllEncompassingFormHttpMessageConverter - MultiValueMap 6 MappingJackson2HttpMessageConverter - 将对象转换为json类型 7 Jaxb2RootElementHttpMessaaeConverter - 支持注解方式xml处理的。
内容协商 引入xml依赖 引入xml依赖后,服务端允许按照xml回传数据,因为json和xml的依赖不同,因此需要导入xml依赖:
1 2 3 4 <dependency > <groupId > com.fasterxml.jackson.dataformat</groupId > <artifactId > jackson-dataformat-xml</artifactId > </dependency >
apifox测试返回json和xml数据 比如电脑端浏览器访问后端接口时只能处理xml数据,而手机app访问时只能处理json数据,应该如何写后端接口,保证前端能够获取所需的数据呢?
其实并不需要人为进行判断,spring可以解析http请求头中的Accept
字段,该字段是Http协议中规定的,用于告诉服务器发起请求的客户端能够接收处理的数据类型,因此需要返回json
或者xml
格式,只需要在Accept
中进行设定即可。
1 2 3 4 5 6 Accept :*/* Accept :application/json Accept :application/xml
开启浏览器使用参数内容进行返回值协商的功能 如果使用浏览器直接发送请求,除非使用Ajax
,否则可能没有办法指定可以接收的类型,会按照顺序使用靠前的格式响应:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
因此可以开启返回值协商功能,达到相应的目的
1 2 3 spring: contentnegotiation: favor-parameter: true
客户端通过参数协商获取json格式数据:
http://localhost:8080/test/person?format=json
客户端通过参数协商获取xml格式数据:
http://localhost:8080/test/person?format=xml
内容协商的原理 1、判断当前响应头中是否已经有确定的媒体类型(MediaType)。
2、获取客户端(PostMan、浏览器、app)支持接收的内容类型。(获取客户端Accept请求头字段)**[application/xml]**
3、遍历循环所有当前系统的 MessageConverter
,看谁支持操作这个对象(Person)
4、找到支持操作Person
类型的converter
,把converter
支持的媒体类型统计出来。
5、找到客户端所需的**[application/xml]**返回类型,进行内容协商,匹配最佳的媒体类型。
6、使用获取的支持将对象转换为最佳的匹配媒体类型的converter
转换器,调用其进行相应的转化。
视图解析与模板引擎 视图解析:SpringBoot默认不支持JSP
,因此需要引入第三方模板引擎技术实现页面渲染
视图解析的处理方式有以下几种:
转发
重定向
自定义视图
模板引擎-Thymeleaf介绍 Thymeleaf是现代化,服务端的java模板引擎
Thymeleaf文档:Tutorial: Using Thymeleaf
基本语法 表达式
表达式名字
语法
用途
变量取值
${…}
获取请求域、session域、对象等值
选择变量
*{…}
获取上下文对象值
消息
#{…}
获取国际化等值
链接
@{…}
生成链接,自动拼接项目的路径
片段表达式
~{…}
jsp:include 作用,引入公共页面片段
字面量 文本值: ‘one text’ , ‘Another one!’ ,… 数字: 0 , 34 , 3.0 , 12.3 ,… 布尔值: true , false
空值: null
变量: one,two,…. 变量中不能出现空格
文本操作 字符串拼接: +
变量替换: |The name is ${name}|
数学运算 运算符: + , - , * , / , %
布尔运算 运算符: and , or
一元运算: ! , not
比较运算 比较: > , <** **,** **>= , <= ( gt , lt , ge , le **)**等式: == , != ( eq , ne )
条件运算 If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
特殊操作 无操作: _
设置属性值 th:attr 设置单个值:
1 2 3 4 5 6 <form action ="subscribe.html" th:attr ="action=@{/subscribe}" > <fieldset > <input type ="text" name ="email" /> <input type ="submit" value ="Subscribe!" th:attr ="value=#{subscribe.submit}" /> </fieldset > </form >
设置多个值:
1 <img src ="../../images/gtvglogo.png" th:attr ="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
替代写法:
1 2 <input type ="submit" value ="Subscribe!" th:value ="#{subscribe.submit}" /> <form action ="subscribe.html" th:action ="@{/subscribe}" >
迭代 1 2 3 4 5 6 <tr th:each ="prod : ${prods}" > <td th:text ="${prod.name}" > Onions</td > <td th:text ="${prod.price}" > 2.41</td > <td th:text ="${prod.inStock}? #{true} : #{false}" > yes</td > </tr >
1 2 3 4 5 <tr th:each ="prod,iterStat : ${prods}" th:class ="${iterStat.odd}? 'odd'" > <td th:text ="${prod.name}" > Onions</td > <td th:text ="${prod.price}" > 2.41</td > <td th:text ="${prod.inStock}? #{true} : #{false}" > yes</td > </tr >
条件运算 1 2 3 4 5 6 7 8 9 <a href ="comments.html" th:href ="@{/product/comments(prodId=${prod.id})}" th:if ="${not #lists.isEmpty(prod.comments)}" > view</a > <div th:switch ="${user.role}" > <p th:case ="'admin'" > User is an administrator</p > <p th:case ="#{roles.manager}" > User is a manager</p > <p th:case ="*" > User is some other thing</p > </div >
属性优先级
thymeleaf的使用 引入Starter 1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-thymeleaf</artifactId > </dependency >
1、所有thymeleaf的配置值都在 ThymeleafProperties
2、配置好了 SpringTemplateEngine
3、配好了 ThymeleafViewResolver
4、我们只需要直接开发页面
1 2 3 4 5 6 7 8 9 10 @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(ThymeleafProperties.class) @ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class }) @AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class }) public class ThymeleafAutoConfiguration { }public static final String DEFAULT_PREFIX = "classpath:/templates/" ;public static final String DEFAULT_SUFFIX = ".html" ;
页面开发 1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h1 th:text ="${msg}" > 你好哇!Hello world!</h1 > </body > </html >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Controller public class ViewTestController { @GetMapping("/heavytiger") public String heavytiger (Model model) { model.addAttribute("msg" , "来自后端的你好!" ); return "success" ; } }
若直接访问该页面,即没有后台传值,会正常显示标签内的数据:
若后台开启,通过controller
进行访问,得到的即是requestScope
中的值
可以看到此时标签内的值已经被后端传递的${msg}
覆盖
拦截器 HandlerInterceptor接口 拦截器需要继承HandlerInterceptor
接口即可运行
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 @Slf4j public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requestURI = request.getRequestURI(); log.info("preHandle拦截的请求路径是{}" ,requestURI); HttpSession session = request.getSession(); Object loginUser = session.getAttribute("loginUser" ); if (loginUser != null ){ return true ; } request.setAttribute("msg" ,"请先登录" ); request.getRequestDispatcher("/" ).forward(request,response); return false ; } @Override public void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("postHandle执行{}" ,modelAndView); } @Override public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("afterCompletion执行异常{}" ,ex); } }
配置拦截器 所有的web相关的配置文件都继承WebMvcConfigurer
接口即可
继承WebMvcConfigurer
接口的addInterceptors
方法,通过该方法的参数InterceptorRegistry
注册拦截器的相关参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration public class AdminWebConfig implements WebMvcConfigurer { @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/**" ) .excludePathPatterns("/" ,"/login" ,"/css/**" ,"/fonts/**" ,"/images/**" ,"/js/**" ); } }
拦截器的原理 1、根据当前请求,找到HandlerExecutionChain
【可以处理请求的handler以及handler的所有拦截器】
2、先来顺序执行所有拦截器的preHandle
方法
1、如果当前拦截器prehandler
返回为true
。则执行下一个拦截器的preHandle
2、如果当前拦截器返回为false
。直接倒序执行所有已经执行了的拦截器的afterCompletion
;
3、如果任何一个拦截器返回false
。直接跳出不执行目标方法
4、所有拦截器都返回True
。执行目标方法
5、倒序执行所有拦截器的postHandle
方法。
6、前面的步骤有任何异常都会直接倒序触发afterCompletion
7、页面成功渲染完成以后,也会倒序触发afterCompletion
文件上传 页面表单 1 2 3 4 5 6 7 <form method ="post" action ="/upload" enctype ="multipart/form-data" > <input type ="file" name ="file" id ="InputOneFile" > <br > <input type ="file" name ="files" id ="InputFiles" multiple > <input type ="submit" value ="提交" > </form >
文件上传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 @PostMapping("/upload") public String upload (@RequestPart("file") MultipartFile headerImg, @RequestPart("files") MultipartFile[] photos) throws IOException { log.info("上传的信息:headerImg={},photos={}" , headerImg.getSize(), photos.length); if (!headerImg.isEmpty()){ String originalFilename = headerImg.getOriginalFilename(); headerImg.transferTo(new File("H:\\cache\\" +originalFilename)); } if (photos.length > 0 ){ for (MultipartFile photo : photos) { if (!photo.isEmpty()){ String originalFilename = photo.getOriginalFilename(); photo.transferTo(new File("H:\\cache\\" +originalFilename)); } } } return "main" ; }
文件上传自动配置原理 文件上传自动配置类-MultipartAutoConfiguration
-MultipartProperties
FileCopyUtils
。实现文件流的拷贝
异常处理 默认规则
替换错误页面 可以在templates/error
文件夹下放置需要相应的页面,将会自动替换SpringBoot的白页报错,例如templates/error/4xx.html
,templates/error/500.html
其逻辑如下:
error/404.html error/5xx.html;
有精确的错误状态码页面就匹配精确,没有就找 4xx.html;
如果都没有就触发白页
数据访问 SQL数据库 数据源自动配置 使用HikariDataSource 进行数据源的自动配置
首先要导入JDBC场景:
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-jdbc</artifactId > </dependency >
会引入以下依赖:
HikanCP 数据源
spring-jdbc jdbc驱动
spring-tx 事务支持
我们会发现官方没有导入数据库驱动,原因是spring不知道开发人员会使用什么驱动,因此不导入,需要自己导入
导入数据库驱动:
默认版本是: <mysql.version>8.0.22</mysql.version>
数据库的版本需要和驱动版本对应
两种方法可以解决:
1 2 3 4 5 6 7 8 9 10 11 <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.49</version > </dependency > <properties > <java.version > 1.8</java.version > <mysql.version > 5.1.49</mysql.version > </properties >
使用Druid数据源 导入依赖:
1 2 3 4 5 <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.1.17</version > </dependency >
创建数据源配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" destroy-method ="close" > <property name ="url" value ="${jdbc.url}" /> <property name ="username" value ="${jdbc.username}" /> <property name ="password" value ="${jdbc.password}" /> <property name ="maxActive" value ="20" /> <property name ="initialSize" value ="1" /> <property name ="maxWait" value ="60000" /> <property name ="minIdle" value ="1" /> <property name ="timeBetweenEvictionRunsMillis" value ="60000" /> <property name ="minEvictableIdleTimeMillis" value ="300000" /> <property name ="testWhileIdle" value ="true" /> <property name ="testOnBorrow" value ="false" /> <property name ="testOnReturn" value ="false" /> <property name ="poolPreparedStatements" value ="true" /> <property name ="maxOpenPreparedStatements" value ="20" /> </bean >
@ConditionalOnMissingBean(DataSource.class) 的含义是:当容器中没有 DataSource(数据源类)时,Spring Boot 才会使用 HikariCP 作为其默认数据源。 也就是说,若我们向容器中添加 Druid 数据源类(DruidDataSource,继承自 DataSource)的对象时,Spring Boot 就会使用 Druid 作为其数据源,而不再使用 HikariCP。
例如,我们需要在config包中创建一个MyDataSourceConfig
的配置类,并将 Druid 数据源对象添加到容器中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Configuration public class MyDataSourceConfig implements WebMvcConfigurer { @ConfigurationProperties("spring.datasource") @Bean public DataSource dataSource () throws SQLException { DruidDataSource druidDataSource = new DruidDataSource(); return druidDataSource; } }
在配置文件 application.yml 中添加以下数据源配置,它们会与与 Druid 数据源中的属性进行绑定:
1 2 3 4 5 6 7 spring: datasource: username: root password: root url: jdbc:mysql://127.0.0.1:3306/jdbc driver-class-name: com.mysql.cj.jdbc.Driver
至此,我们就已经将数据源从 HikariCP 切换到了 Druid 了。
通过starter整合Druid Druid 可以说是国内使用最广泛的数据源连接池产品,但到目前为止 Spring Boot 官方只对 Hikari、Tomcat、Dbcp2 和 OracleUcp 等 4 种数据源产品提供了自动配置支持,对于其他的数据源连接池产品(包括 Druid),则并没有提供自动配置支持。这就导致用户只能通过自定义的方式整合 Druid,非常繁琐。
为了解决这一问题,于是阿里官方提供了 Druid Spring Boot Starter ,它可以帮助我们在 Spring Boot 项目中,轻松地整合 Druid 的数据库连接池和监控功能。
引入依赖:
1 2 3 4 5 6 <dependency > <groupId > com.alibaba</groupId > <artifactId > druid-spring-boot-starter</artifactId > <version > 1.1.17</version > </dependency >
配置属性:
在 Spring Boot 配置文件中配置以下内容:
JDBC 通用配置
Druid 数据源连接池配置
Druid 监控配置
Druid 内置 Filter 配置
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 spring: datasource: username: root password: root url: jdbc:mysql://127.0.0.1:3306/jdbc driver-class-name: com.mysql.cj.jdbc.Driver spring: datasource: druid: initial-size: 5 min-idle: 5 max-active: 20 max-wait: 60000 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 validation-query: SELECT 1 FROM DUAL test-while-idle: true test-on-borrow: false test-on-return: false pool-prepared-statements: false max-pool-prepared-statement-per-connection-size: 20 filters: stat,wall connection-properties: 'druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000' spring: datasource: druid: stat-view-servlet: enabled: true url-pattern: '/druid/*' reset-enable: true login-username: admin login-password: admin web-stat-filter: enabled: true url-pattern: '/*' exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' session-stat-enable: true aop-patterns: com.heavytiger.jdbc.* spring: datasource: druid: filter: stat: enabled: true slow-sql-millis: 1000 log-slow-sql: true wall: enabled: true config: update-allow: true drop-table-allow: false insert-allow: true delete-allow: true
在配置 Druid 内置 Filter 时,需要先将对应 Filter 的 enabled 设置为 true,否则内置 Filter 的配置不会生效。
整合MyBatis 引入依赖 1 2 3 4 5 6 <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > <version > 2.2.0</version > </dependency >
配置MyBatis 1 2 3 4 5 6 7 8 9 mybatis: mapper-locations: classpath:mybatis/mapper/*.xml type-aliases-package: com.heavytiger.pojo configuration: map-underscore-to-camel-case: true
创建实体类及接口 实体类不再占用位置书写
Mapper接口需要进行标记:@Mapper
如:
1 2 3 4 5 @Mapper public interface UserMapper { User getByUserNameAndPassword (User user) ; }
当 mapper 接口较多时,我们可以在 Spring Boot 主启动类上使用 @MapperScan 注解扫描指定包下的 mapper 接口,而不再需要在每个 mapper 接口上都标注 @Mapper 注解。
参考资料
[1] 尚硅谷雷神SpringBoot2零基础入门springboot全套完整版(spring boot2)_哔哩哔哩_bilibili
[2] SpringBoot2核心技术与响应式编程 · 语雀 (yuque.com)