spring boot 学习
springboot 的mvc自动支持ajax(导入jquery)
文件路径 / ./ ../ 的意思
注解的作用
@Configuration(proxyBeanMethods = true)//这个配置类页是springboot管理的类
//false表示返回的是原型对象、true表示放回单例对象
//@ConditionalOnBean(name = "pet")//这个注解是表示如果没有pet就不会配置这个类
@ImportResource("classpath:bean.xml")
@EnableConfigurationProperties(Car.class)
public class MyConfig {
@Bean
public Pet pet(){
return new Pet(1,"猫猫");
}
@Bean
public User user(){
User zhangsan = new User(1, "zhangsan", pet());
return zhangsan;
}
}
注解
@ImportResource(“classpath:bean.xml”)
原生xml文件引入
@ConfigurationProperties
/**
* 只有在容器中的组件,才会拥有SpringBoot提供的强大功能
*/
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {
private String brand;
private Integer price;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
'}';
}
}
转发和重定向的区别
自动装配原理
小结
总结:
• SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
• 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
• 生效的配置类就会给容器中装配很多组件
• 只要容器中有这些组件,相当于这些功能就有了
• 定制化配置
• 用户直接自己@Bean替换底层的组件
• 用户去看这个组件是获取的配置文件什么值就去修改。
xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 ----> application.properties
最佳实践
• 引入场景依赖
• https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
• 查看自动配置了哪些(选做)
• 自己分析,引入场景对应的自动配置一般都生效了
• 配置文件中debug=true开启自动配置报告。Negative(不生效)\Positive(生效)
• 是否需要修改
• 参照文档修改配置项
• https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties
• 自己分析。xxxxProperties绑定了配置文件的哪些。
• 自定义加入或者替换组件
• @Bean、@Component。。。
• 自定义器 XXXXXCustomizer;
• …
springboot简化开开发
lombok
@NoArgsConstructor
@AllArgsConstructor
@Data
@ToString
@EqualsAndHashCode
@Slf4j
@EqualsAndHashCode
dev-tools
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
项目或者页面修改以后:Ctrl+F9;
静态资源访问
1、静态资源目录
只要静态资源放在类路径下: called /static (or /public or /resources or /META-INF/resources
访问 : 当前项目根路径/ + 静态资源名
原理: 静态映射/**。
请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面
改变默认的静态资源路径
yml
spring:
mvc:
static-path-pattern: /res/**
resources:
static-locations: [classpath:/haha/]
webjar
自动映射 /webjars/**
https://www.webjars.org/
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>
访问地址:http://localhost:8080/webjars/jquery/3.5.1/jquery.js 后面地址要按照依赖里面的包路径
2021.3.3 springboot学习
• 静态资源路径下 index.html
• 可以配置静态资源路径
• 但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问
spring:
mvc:
static-path-pattern: /res/** 这个会导致welcome page功能失效
resources:
static-locations: [classpath:/haha/]
• controller能处理/index
自定义 Favicon
favicon.ico 放在静态资源目录下即可。
spring:
mvc:
static-path-pattern: /res/**
这个会导致 Favicon 功能失效
请求映射rest
1、rest使用与原理
• @xxxMapping;
• Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
• 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
• 现在: /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
• 核心Filter;HiddenHttpMethodFilter
• 用法: 表单method=post,隐藏域 _method=put
• SpringBoot中手动开启
• 扩展:如何把_method 这个名字换成我们自己喜欢的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="/car/1/owern/lisi?age=23&inters=basketball&inters=football">测试地址</a>
<h1>helloworld</h1>
<form action="/user" method="post">
<input name="_method" type="hidden" value="PUT">
<input value="TEST_PUT" type="submit">
</form>
<form action="/user" method="post">
<input name="_method" type="hidden" value="DELETE">
<input value="TEST_DELETE" type="submit">
</form>
<form action="/save" method="post">
--------------------------<br>
<input type="text" name="name" placeholder="姓名">
<input type="password" name="password" placeholder="密码">
<input type="submit" value="提交">
</form>
</body>
</html>
各种注解的使用
package com.ming.boot02.controller;
import com.sun.org.apache.regexp.internal.REUtil;
import org.springframework.boot.web.servlet.server.Session;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
import javax.websocket.server.PathParam;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
public class ParaController {
@GetMapping("/car/{id}/owern/{username}")
public Map<String,Object> car(@PathVariable("id") Integer id,
@PathVariable("username") String username,
@PathVariable Map<String ,String > pv,
@RequestHeader("User-Agent")String agent,
@RequestHeader Map<String,String> headers,
@RequestParam("age") Integer age,
@RequestParam("inters")List<String> inters,
@RequestParam Map<String,String > parms,
@CookieValue("Idea-3b398484") Cookie cookie
){
// System.out.println("接收到参数为:"+id);
Map<String, Object> map = new HashMap<>();
// map.put("id",id);
// map.put("username",username);
// map.put("pv",pv);
// map.put("agent",agent);
// map.put("headers",headers);
map.put("age",age);
map.put("inters",inters);
map.put("parms",parms);
map.put("cookie",cookie);
return map;
}
@PostMapping("/save")
public Map test(@RequestBody String content){
Map<String, Object> map = new HashMap<>();
map.put("content",content);
return map;
}
}
32
@Controller
public class RequestController {
@GetMapping("/goto")
public String goto1(HttpServletRequest request){
request.setAttribute("msg","成功");
return"forward:/success";
}
@ResponseBody
@GetMapping("/success")
public Map test(HttpServletRequest request, @RequestAttribute("msg") String msg){
String remsg = (String) request.getAttribute("msg");
Map<String, Object> map = new HashMap<>();
map.put("reqmsg",remsg);
map.put("msg",msg);
return map;
** 参数传递**
@GetMapping("/params")
public String params(Map map,
Model model,
HttpServletRequest request,
HttpServletResponse response){
map.put("s1","11111");
model.addAttribute("s2","2222");
request.setAttribute("s3","3333");
Cookie cookie = new Cookie("cook1","v1");
response.addCookie(cookie);
return "forward:/success";
}
@ResponseBody
@GetMapping("/success")
public Map test(HttpServletRequest request,
@RequestAttribute(value = "msg",required = false) String msg){
// String remsg = (String) request.getAttribute("msg");
Map<String, Object> map = new HashMap<>();
// map.put("reqmsg",remsg);
// map.put("msg",msg);
Object s1=request.getAttribute("s1");
Object s2=request.getAttribute("s2");
Object s3=request.getAttribute("s3");
map.put("s1",s1);
map.put("s2",s2);
map.put("s3",s3);
return map;
返回数据 json类型
@RestController
@RequestMapping(value = "/user",method = RequestMethod.GET)
@Slf4j
public class UserController {
@PostMapping("")
public String test(User user){
System.out.println("用户信息:"+user);
return "OK";
}
<form action="/user" method="post">
<input type="text" name="id" placeholder="id">
<input type="text" name="username" placeholder="username">
<input type="text" name="pet.id" placeholder="pet.id">
<input type="text" name="pet.name" placeholder="pet.name">
<input type="submit" value="提交">
</form>
返回值解析器原理、
• 1、返回值处理器判断是否支持这种类型返回值 supportsReturnType
• 2、返回值处理器调用 handleReturnValue 进行处理
• 3、RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的。
• 1. 利用 MessageConverters 进行处理 将数据写为json
• 1、内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)
• 2、服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,
• 3、SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理?
• 1、得到MappingJackson2HttpMessageConverter可以将对象写为json
• 2、利用MappingJackson2HttpMessageConverter将对象转为json再写出去。
内容协商
根据客户端接收能力不同,返回不同媒体类型的数据。
1、引入xml依赖
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
开启浏览器参数方式内容协商功能
为了方便内容协商,开启基于请求参数的内容协商功能。
spring:
contentnegotiation:
favor-parameter: true #开启请求参数内容协商模式
发请求:
http://localhost:8080/test/person?format=json
http://localhost:8080/test/person?format=xml
模板引擎-Thymeleaf
1、thymeleaf简介
Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text.
现代化、服务端Java模板引擎
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
设置属性值-th:attr
设置单个值
<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>
<form action="subscribe.html" th:action="@{/subscribe}">
所有h5兼容的标签写法
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#setting-value-to-specific-attributes
引入模板引擎
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>牛逼</title>
</head>
<body>
<h1 th:text="${msg}"></h1>
<a th:href="${url}">连接</a>
</body>
</html>
设置前置路径
# 设置前置路径、
server:
servlet:
context-path: /world
thymeleaf替换
替换
注意的问题
如果用replace会出现两个head标签
共享session
当把显示用户的代码添加到公共页面的时可以用session返回
@GetMapping("/main.html")
public String main(HttpSession session,Model model){
User user = (User)session.getAttribute("user");
if (user!=null){
model.addAttribute("user",user);
return "main";
}
session.setAttribute("session",user);
return "login";
}
thymely遍历
拦截器
自定义拦截器
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获得被拦截的请求路径
String url = request.getRequestURI();
log.info("被拦截的路径为{}"+url);
//登陆检查逻辑
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("user");
if (loginUser!=null){
//放行
return true;
}
//拦截住 重定向到登陆页面
session.setAttribute("msg","请登录");
response.sendRedirect("/login");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
自定义config
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")//这表示所有请求都被拦截包括静态资源
.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**");
}
}
文件上传功能
设置文件大小不受限制
spring.servlet.multipart.max-file-size=-1
或者:spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=100MB
表单
文件类型需要method="post" enctype="multipart/form-data"
如果是提交多个照片:<input type="file" name="photos" multiple>
<form role="form" method="post" enctype="multipart/form-data" th:action="@{/upload}">
<div class="form-group">
<label for="exampleInputEmail1">邮箱</label>
<input name="email" type="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email">
</div>
<div class="form-group">
<label for="exampleInputPassword1">密码</label>
<input name="password" type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
</div>
<div class="form-group">
<label for="exampleInputFile">头像上传</label>
<input type="file" name="headerImg" id="exampleInputFile">
<p class="help-block">Example block-level help text here.</p>
</div>
<div class="form-group">
<label for="exampleInputFile">照片上传</label>
<input type="file" name="photos" multiple>
<p class="help-block">Example block-level help text here.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox"> Check me out
</label>
</div>
<button type="submit" class="btn btn-primary">提交</button>
</form>
控制器
需要判断文件是否为空文件
注解@RequestPart配合使用
@PostMapping("/upload")
public String upload(
@RequestParam("email") String email,
@RequestParam("password") String password,
@RequestPart("headerImg") MultipartFile headerImg,
@RequestPart("photos") MultipartFile[] photos
) throws IOException {
log.info("邮箱:{},密码{},头像大小{},照片数量{}",email,password,headerImg.getSize(),photos.length);
if (!headerImg.isEmpty()){
String headerImgName = headerImg.getOriginalFilename();
headerImg.transferTo(new File("H:/test/"+headerImgName));
}
if (photos.length>1){
for (MultipartFile photo : photos) {
if (photo!=null){
String photoFileName = photo.getOriginalFilename();
photo.transferTo(new File("H:/test/"+photoFileName));
}
}
}
return "main";
}
自动配置原理
文件上传自动配置类-MultipartAutoConfiguration-MultipartProperties
• 自动配置好了 StandardServletMultipartResolver 【文件上传解析器】
• 原理步骤
• 1、请求进来使用文件上传解析器判断(isMultipart)并封装(resolveMultipart,返回MultipartHttpServletRequest)文件上传请求
• 2、参数解析器来解析请求中的文件内容封装成MultipartFile
• 3、将request中文件信息封装为一个Map;MultiValueMap<String, MultipartFile>
FileCopyUtils。实现文件流的拷贝
错误处理
1、默认规则
• 默认情况下,Spring Boot提供/error处理所有错误的映射
• 对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据
自定义异页面
要对其进行自定义,添加View解析为error
• 要完全替换默认行为,可以实现 ErrorController 并注册该类型的Bean定义,或添加ErrorAttributes类型的组件以使用现有机制但替换其内容。
• error/下的4xx,5xx页面会被自动解析;
•
自定义处理异常
一
二
三
• 、DefaultErrorAttributes先来处理异常。把异常信息保存到rrequest域,并且返回null;
• 2、默认没有任何人能处理异常,所以异常会被抛出
• 1、如果没有任何人能处理最终底层就会发送 /error 请求。会被底层的BasicErrorController处理
• 2、解析错误视图;遍历所有的 ErrorViewResolver 看谁能解析。
•
• 3、默认的 DefaultErrorViewResolver ,作用是把响应状态码作为错误页的地址,error/500.html
• 4、模板引擎最终响应这个页面 error/500.html
Web原生组件注入(Servlet、Filter、Listener)
1、使用Servlet API
@ServletComponentScan(basePackages = “com.atguigu.admin”) :指定原生Servlet组件都放在那里
@WebServlet(urlPatterns = “/my”):效果:直接响应,没有经过Spring的拦截器?
进行包扫描才生效
自定义servlet
@WebFilter(urlPatterns={"/css/","/images/"})
package com.ming.boot03.servlet;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* 拦截器功能
*/
//@WebFilter(urlPatterns = {"/css/*","/images/*"})
@Slf4j
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("执行拦截器");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("初始化拦截器");
}
@Override
public void destroy() {
log.info("销毁拦截器");
}
}
@WebListener
package com.ming.boot03.servlet;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
@Slf4j
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
log.info("开始监听");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
log.info("监听结束");
}
}
第二种使用方法
去掉自定义类的@Web....
package com.ming.boot03.servlet;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
import java.util.List;
@Configuration
public class MyRegisteConfig {
@Bean
public ServletRegistrationBean myServlet(){
return new ServletRegistrationBean(new MyServlet(),"/my","/my02");
}
@Bean
public FilterRegistrationBean myFilter(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new MyFilter());
// filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
return filterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean myListener(){
return new ServletListenerRegistrationBean(new MyListener());
}
}
• @EnableWebMvc + WebMvcConfigurer —— @Bean 可以全面接管SpringMVC,所有规则全部自己重新配置; 实现定制和扩展功能
容器中所有的配置相互配合,只要存在就会生效
• 原理
• 1、WebMvcAutoConfiguration 默认的SpringMVC的自动配置功能类。静态资源、欢迎页…
• 2、一旦使用 @EnableWebMvc 、。会 @Import(DelegatingWebMvcConfiguration.class)
• 3、DelegatingWebMvcConfiguration 的 作用,只保证SpringMVC最基本的使用
• 把所有系统中的 WebMvcConfigurer 拿过来。所有功能的定制都是这些 WebMvcConfigurer 合起来一起生效
• 自动配置了一些非常底层的组件。RequestMappingHandlerMapping、这些组件依赖的组件都是从容器中获取
• public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
• 4、WebMvcAutoConfiguration 里面的配置要能生效 必须 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
• 5、@EnableWebMvc 导致了 WebMvcAutoConfiguration 没有生效。
数据访问
jdbc
导入JDBC场景
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
数据库驱动?
为什么导入JDBC场景,官方不导入驱动?官方不知道我们接下要操作什么数据库。
数据库版本和驱动版本对应
默认版本:<mysql.version>8.0.22</mysql.version>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- <version>5.1.49</version>-->
</dependency>
想要修改版本
1、直接依赖引入具体版本(maven的就近依赖原则)
2、重新声明版本(maven的属性的就近优先原则)
<properties>
<java.version>1.8</java.version>
<mysql.version>5.1.49</mysql.version>
</properties>
进行配置
spring.datasource.url=jdbc:mysql://localhost:3306/straw?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
使用Druid数据源
1、druid官方github地址
https://github.com/alibaba/druid
使用官方starter方式
1、引入druid-starter
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>
#spring:
# datasource:
# url: jdbc:mysql://localhost:3306/straw?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
# username: root
# password: root
# driver-class-name: com.mysql.jdbc.Driver
spring:
datasource:
druid:
aop-patterns: com.ming.boot03.* #监控SpringBean
filters: stat,wall # 底层开启功能,stat(sql监控),wall(防火墙)
stat-view-servlet: # 配置监控页功能
enabled: true
login-username: admin
login-password: admin
resetEnable: false
web-stat-filter: # 监控web
enabled: true
urlPattern: /*
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
filter:
stat: # 对上面filters里面的stat的详细配置
slow-sql-millis: 1000
logSlowSql: true
enabled: true
wall:
enabled: true
config:
drop-table-allow: false
分析自动配置
• 扩展配置项 spring.datasource.druid
• DruidSpringAopConfiguration.class, 监控SpringBean的;配置项:spring.datasource.druid.aop-patterns
• DruidStatViewServletConfiguration.class, 监控页的配置:spring.datasource.druid.stat-view-servlet;默认开启
• DruidWebStatFilterConfiguration.class, web监控配置;spring.datasource.druid.web-stat-filter;默认开启
• DruidFilterConfiguration.class}) 所有Druid自己filter的配置
整合MyBatis操作
配置mybatis规则
mybatis:
# config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
configuration:
map-underscore-to-camel-case: true #驼峰命名法
可以不写全局;配置文件,所有全局配置文件的配置都放在configuration配置项中即可
Demo
package com.ming.boot03.mapper;
import com.ming.boot03.entity.Books;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
Books queryBookById(Integer bookID);
Integer insert(Books books);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ming.boot03.mapper.UserMapper">
<!-- public Account getAcct(Long id); -->
<select id="queryBookById" resultType="com.ming.boot03.entity.Books">
select * from books where bookID=#{bookID}
</select>
<!-- Integer insert(Books books);-->
<insert id="insert" parameterType="com.ming.boot03.entity.Books">
insert into
books (bookID,bookName,bookCounts,detail)
values(#{bookID},#{bookName},#{bookCounts},#{detail})
</insert>
</mapper>
• 导入mybatis官方starter
• 编写mapper接口。标准@Mapper注解
• 编写sql映射文件并绑定mapper接口
• 在application.yaml中指定Mapper配置文件的位置,以及指定全局配置文件的信息 (建议;配置在mybatis.configuration)
整合 MyBatis-Plus 完成CRUD
什么是MyBatis-Plus
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
mybatis plus 官网
建议安装 MybatisX 插件
自动配置
• MybatisPlusAutoConfiguration 配置类,MybatisPlusProperties 配置项绑定。mybatis-plus:xxx 就是对mybatis-plus的定制
• SqlSessionFactory 自动配置好。底层是容器中默认的数据源
• mapperLocations 自动配置好的。有默认值。classpath*:/mapper/**/*.xml;任意包的类路径下的所有mapper文件夹下任意路径下的所有xml都是sql映射文件。 建议以后sql映射文件,放在 mapper下
• 容器中也自动配置好了 SqlSessionTemplate
• @Mapper 标注的接口也会被自动扫描;建议直接 @MapperScan(“com.atguigu.admin.mapper”) 批量扫描就行
优点:
• 只需要我们的Mapper继承 BaseMapper 就可以拥有crud能力
简化操作
mapper
package com.ming.boot03.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ming.boot03.entity.Books;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<Books> {
Books queryBookById(Integer bookID);
int insert(Books books);
}
IService
package com.ming.boot03.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ming.boot03.entity.Books;
import org.springframework.stereotype.Service;
@Service
public interface IUserService extends IService<Books> {
}
实现类
package com.ming.boot03.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ming.boot03.entity.Books;
import com.ming.boot03.mapper.UserMapper;
public class UserServiceImpl
extends ServiceImpl<UserMapper, Books> implements IUserService {
}
直接使用
单元测试
JUnit5 的变化
Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库
作为最新版本的JUnit框架,JUnit5与之前版本的Junit框架有很大的不同。由三个不同子项目的几个不同模块组成。
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
JUnit Platform: Junit Platform是在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入。
JUnit Jupiter: JUnit Jupiter提供了JUnit5的新的编程模型,是JUnit5新特性的核心。内部 包含了一个测试引擎,用于在Junit Platform上运行。
JUnit Vintage: 由于JUint已经发展多年,为了照顾老的项目,JUnit Vintage提供了兼容JUnit4.x,Junit3.x的测试引擎。
引入jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
JUnit5常用注解
JUnit5的注解与JUnit4的注解有所变化
https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations
• @Test :表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试
• @ParameterizedTest :表示方法是参数化测试,下方会有详细介绍
• @RepeatedTest :表示方法可重复执行,下方会有详细介绍
• @DisplayName :为测试类或者测试方法设置展示名称
• @BeforeEach :表示在每个单元测试之前执行
• @AfterEach :表示在每个单元测试之后执行
• @BeforeAll :表示在所有单元测试之前执行
• @AfterAll :表示在所有单元测试之后执行
• @Tag :表示单元测试类别,类似于JUnit4中的@Categories
• @Disabled :表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
• @Timeout :表示测试方法运行如果超过了指定时间将会返回错误
• @ExtendWith :为测试类或测试方法提供扩展类引用
断言(assertions)
断言(assertions)是测试方法中的核心部分,用来对测试需要满足的条件进行验证。这些断言方法都是 org.junit.jupiter.api.Assertions 的静态方法。JUnit 5 内置的断言可以分成如下几个类别:
检查业务逻辑返回的数据是否合理。
所有的测试运行结束以后,会有一个详细的测试报告;
数组断言
通过 assertArrayEquals 方法来判断两个对象或原始类型的数组是否相等
@Test
@DisplayName("array assertion")
public void array() {
assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}
3、组合断言
assertAll 方法接受多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易的提供这些断言
@Test
@DisplayName("assert all")
public void all() {
assertAll("Math",
() -> assertEquals(2, 1 + 1),
() -> assertTrue(1 > 0)
);
}
4、异常断言
在JUnit4时期,想要测试方法的异常情况时,需要用@Rule注解的ExpectedException变量还是比较麻烦的。而JUnit5提供了一种新的断言方式Assertions.assertThrows() ,配合函数式编程就可以进行使用。
@Test
@DisplayName("异常测试")
public void exceptionTest() {
ArithmeticException exception = Assertions.assertThrows(
//扔出断言异常
ArithmeticException.class, () -> System.out.println(1 % 0));
}
5、超时断言
Junit5还提供了Assertions.assertTimeout() 为测试方法设置了超时时间
@Test
@DisplayName("超时测试")
public void timeoutTest() {
//如果测试方法时间超过1s将会异常
Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}
6、快速失败
通过 fail 方法直接使得测试失败
@Test
@DisplayName("fail")
public void shouldFail() {
fail("This should fail");
}
测试类Demo
package com.ming.boot03;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@DisplayName("自定义测试")
public class DemoTest {
@DisplayName("测试display注解")
@Test
public void test2(){
System.out.println(1);
}
@BeforeEach
public void test3(){
System.out.println("测试要开始了");
}
@AfterEach
public void test4(){
System.out.println("测试结束了");
}
@BeforeAll
static public void test5(){
System.out.println("所有测试要开始了");
}
@AfterAll
static public void test6(){
System.out.println("所有测试结束了");
}
@RepeatedTest(5)
public void test7(){
System.out.println(2);
}
@Test
public void test8(){
int r = cal(2,3);
assertEquals(5,r,"结果不符合预期");
}
public int cal(int a,int b){
return a+b;
}
@Test
public void test11(){
assertAll("test",
() ->assertTrue(true&& true,"结果不为true"),
() -> assertEquals(2,1,"值不相同"));
}
@ParameterizedTest
@DisplayName("参数化测试")
@ValueSource(ints = {1,2,3,4,5})
public void test12(int i){
System.out.println(i);
}
}
配置文件
1、Profile功能
为了方便多环境适配,springboot简化了profile功能。
1、application-profile功能
• 默认配置文件 application.yaml;任何时候都会加载
• 指定环境配置文件 application-{env}.yaml
• 激活指定环境
• 配置文件激活
• 命令行激活:java -jar xxx.jar --spring.profiles.active=prod --person.name=haha
• 修改配置文件的任意值,命令行优先
• 默认配置与环境配置同时生效
• 同名配置项,profile配置优先
1、外部配置源
常用:Java属性文件、YAML文件、环境变量、命令行参数;
2、配置文件查找位置
(1) classpath 根路径
(2) classpath 根路径下config目录
(3) jar包当前目录
(4) jar包当前目录的config目录
(5) /config子目录的直接子目录
3、配置文件加载顺序:
- 当前jar包内部的application.properties和application.yml
- 当前jar包内部的application-{profile}.properties 和 application-{profile}.yml
- 引用的外部jar包的application.properties和application.yml
- 引用的外部jar包的application-{profile}.properties 和 application-{profile}.yml
4、指定环境优先,外部优先,后面的可以覆盖前面的同名配置项