<form role="form" action="/upload" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="exampleInputEmail1">邮箱</label>
<input type="email" class="form-control" name="email" id="exampleInputEmail1" placeholder="Enter email">
</div>
<div class="form-group">
<label for="exampleInputPassword1">名字</label>
<input type="password" class="form-control" name="username" id="exampleInputPassword1" placeholder="Password">
</div>
<div class="form-group">
<label for="exampleInputFile">壁纸</label>
<input type="file" id="exampleInputFile" name="wallpaperInputFile">
</div>
<div class="form-group">
<label for="exampleInputFile">批量照片</label>
<!-- multiple 代表该 file 属性可以支持多文件上传 -->
<input type="file" name="photos" multiple>
</div>
<div class="checkbox">
<label>
<input type="checkbox"> Check me out
</label>
</div>
<button type="submit" class="btn btn-primary">提交</button>
</form>
@PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String upload(
@RequestPart("wallpaperInputFile") MultipartFile wallpaperInputFile,
@RequestPart("photos") MultipartFile[] photos) throws IOException {
if (!wallpaperInputFile.isEmpty()){
String originalFilename = wallpaperInputFile.getOriginalFilename();
wallpaperInputFile.transferTo(new File("F:\\"+originalFilename));
}
if ((photos.length > 0)) {
for (MultipartFile photo : photos) {
if (!photo.isEmpty()) {
String originalFilename = photo.getOriginalFilename();
photo.transferTo(new File("F:\\"+originalFilename));
}
}
}
return "index";
}
当提示文件大小过大时,可以修改 spring.servlet.max-file-size 和 max-request-file-size 属性来解决这个问题
# -------------- SpringBoot - properties ----------------
# 文件最大大小
spring.servlet.multipart.max-file-size=10MB
# 请求最大大小
spring.servlet.multipart.max-request-size=100MB
文件上传需要指定 content-type 值为:multipart/form-data
因此,无论在控制器还是表单都必须设置好对应的类型
/**
* ----------------控制器-----------
*
* consumes 使用内容类型 = MediaType.MULTIPART_FORM_DATA_VALUE -- "multipart/form-data"
* MULTIPART_FORM_DATA_VALUE 常量就是 multipart/form-data
* 该属性限定了接收的内容类型
*/
@PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
<!--
enctype="multipart/form-data"
enctype 属性可以设置 content-type 的值
这里直接指定为 multipart/form-data**
-->
<form role="form" action="/upload" method="post" enctype="multipart/form-data"></form>
文件上传自动配置类 - MultipartAutoConfiguration-MultiaprtProperties
/error
处理所有错误的映射View
解析为error
ErrorController
并注册该类型的Bean定义,或添加ErrorAttributes
类型的组件以使用现有机制但替换其内容
errorAttributes
如果想要返回页面;就会找error视图 StaticView。(默认是白页)
@ServletComponentScan(“com.renex.admin”):指定原生Servlet组件都放在哪里
@WebServlet(urlPatterns = “/my”):直接响应,没有Spring的拦截器
@WebFilter(urlPatterns = {“/my”}):拦截指定路径下的所有请求
@WebListener:声明的类是一个 监听类
使用 ServletRegistrationBean
、FilterRegistrationBean
和ServletListenerRegistrationBean
@Configuration
public class MyRegistConfig {
@Bean
public ServletRegistrationBean myServlet(){
MyServlet myServlet = new MyServlet();
return new ServletRegistrationBean(myServlet,"/my");
}
@Bean
public FilterRegistrationBean myFilter(){
MyFilter myFilter = new MyFilter();
// 方式一:
// return new FilterRegistrationBean(myFilter,myServlet());// 拦截myServlet()返回的全部路径
// 方式二:
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/my01"));
return filterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean myListener(){
MyListener myListener = new MyListener();
return new ServletListenerRegistrationBean(myListener);
}
}
默认映射的是 /
路径
多个servlet都能处理到同一层路径,精确优先原则
默认支持的 webServer
切换服务器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 排除默认的 Tomcat 服务器 -->
<exclusions>
<groupId>org.springframework.boot</groupId>
<artifactid>spring-boot-starter-tomcat</artifactid>
</exclusions>
</dependency>
<dependency>
<!-- 设置 undertow 服务器场景包 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
原理:
xxxxxxCustomizer:定制化器,可以该变 xxxxxx 的默认规则
修改配置文件
xxxxxCustomizer
编写自定义的配置类 xxConfiguratin + @Bean 替换、增加容器中的默认组件;视图解析器
web应用 实现 WebMvcConfigurer 即可定制化 web 功能 + @Bean 给容器中再扩展一些组件
@Configuration
public class AdminWebConfig impements WebMvcConfigurer
@EnableWebMvc
+ @WebMvcConfigurer —— @Bean 可以全面接管 SpringMVC,所有规则全部自己重写配置;实现定制和扩展功能
原理:
······
场景starter - xxxxAutoConfiguration - 导入xxx组件 - 绑定xxxProperties - 绑定配置文件项
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
没有数据库驱动?官方不知道我们接下来要操作的数据库
数据库版本和驱动版本对应,SpringBoot 默认会有驱动版本仲裁
导入驱动:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!--
想要修改版本
1. 直接引入具体版本(maven的就近依赖原则)
2. 重写声明版本(maven的属性的就近优先原则)
-->
<properties>
<java.version>11</java.version>
<mysql.version>8.0.30</mysql.version>
</properties>
DataSourceAutoConfiguration:数据源的自动配置
@Configuration(
proxyBeanMethods = false
)
@Conditional({DataSourceAutoConfiguration.PooledDataSourceCondition.class})
@ConditionalOnMissingBean({DataSource.class, XADataSource.class})
@Import({Hikari.class, Tomcat.class, Dbcp2.class, Generic.class, DataSourceJmxConfiguration.class})
protected static class PooledDataSourceConfiguration {
protected PooledDataSourceConfiguration() {
}
}
DataSourceTransctionManagerAutoConfiguration:事务管理器的自动配置
JdbcTemplateAutoConfiguration:JdbcTemplate的自动配置,可以对数据库进行crud
JndiDataSourceAutoConfiguration:Jndi的自动配置
XADataSourceAutoConfiguration:分布式事务相关的
spring:
datasource:
url: jdbc:mysql://localhost:3306/test_1?serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
# 超时处理;此处意思:当查询时间超过 3 秒视为查询超时
spring:
jdbc:
template:
query-timeout: 3 #单位秒
@Slf4j
@SpringBootTest
class SpringbootWebAdminApplicationTests {
@Autowired
JdbcTemplate jdbcTemplate;
@Test
void contextLoads() {
Long aLong = jdbcTemplate.queryForObject("select count(*) from person", long.class);
log.info("记录总数"+aLong);
}
}
整合第三方技术的两种方式
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.11</version>
</dependency>
https://github.com/mybatis
SpringBoot官方的Starter:spring-boot-starter-*
第三方的:*-spring-boot-starter
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
可以修改配置文件中 mybatis 的配置文件
# 配置 mybatis 规则
mybatis:
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
# Mapper接口 -> 绑定xml
<?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.renex.admin.mapper.PersonMapper">
<select id="getPerson" resultType="com.renex.admin.pojo.Person">
select * from person where id = #{id}
</select>
</mapper>
注意事项:
# 配置 mybatis 规则
mybatis:
# config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
# 开启驼峰命名转换
configuration: # 指定全局配置文件中的相关配置项
map-underscore-to-camel-case: true
可以不写全局配置文件,所有全局配置文件都放在configuration配置项中即可
使用了 properties(yaml) 就不能使用 xml 配置文件,两者选一个
与原生mybatis使用并无两样
在一个服务类中可以使用注解也可以使用配置模式
SpringBoot 2.2.0 版本开始引入 JUnit5 作为单元测试默认库
作为最小的版本 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:由于 Junit 已经发展多年,为了照顾老的项目,Junit Vintage 提供了兼容Junit4.x,Junit3.x的测试引擎
SpringBoot2.4 以上移除了默认对 Vintage 的依赖。如果需要兼容 junit4 需要自行引入(不能使用Junit4的功能)
如果需要继续兼容 Junit4 需要自行引入 vintage
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<scope>test</scope>
</dependency>
现在版本:
@SpringBootTest
class Boot05WebAdminApplicationTestsO{
@Test
void contentLoads(){
}
}
以前:
@SpringBootTest + @RunWith(SpringTest.class)
SpringBoot 整合 Junit 以后:
Junit5 的注解与JUnit4的注解有所变化。详细更新或注解介绍:JUnit 5 User Guide
注解名称: | 详情用法: |
---|---|
@Test | 表示方法是测试方法。但是与Junit4的@Test不同。它的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供测试 |
@ParmeterizedTest | 表示方法是参数化测试 |
@RepeatedTest | 表示方法可以重复执行 |
@DispalyName | 为测试类或者测试方法设置展示名称 |
@BeforeEach | 表示在每个单元测试之前执行 |
@AfterEach | 表示在每个单元测试之后执行 |
@BeforeAll | 表示在所有单元测试之前执行 |
@AfterAll | 表示在所有单元测试之后执行 |
@Tag | 表示单元测试类别,类似于JUnit4中的@Categories |
@Disabled | 表示测试类或测试方法不知想,类似于JUnit4中的@Ignore |
@Timeout | 表示测试方法运行如果超过了指定时间将会返回错误 |
@ExtendWith | 为测试类或测试方法提供扩展类引用。集合了 SpringBoot 可以使用 @SpringBootTest 进行代替,但是会启动 SpringBoot 的容器功能 |
断言(assertions)是测试方法中的核心部分,用来对测试需要满足的条件进行验证。这些断言方法都是 org.junit.jupiter.api.Assertions 的静态方法。JUnit5 内置的断言可以分成如下几个类别:
检查业务逻辑返回的数据是否合理
所有的测试运行结束以后,会有一个详细的测试报告;
用来对单个值进行简单的验证,如:
方法 | 说明 |
---|---|
assertEquals | 判断两个对象或两个原始类型是否相等 |
assertNotEquals | 判断两个对象或两个原始类型是否不相等 |
assertSame | 判断两个对象引用是否指向同一个对象 |
assertNotSame | 判断两个对象引用是否指向不同的对象 |
assertTrue | 判断给定的布尔值是否为 true |
assertFalse | 判断给定的布尔值是否为 false |
assertNull | 判断给定的对象引用是否为 null |
assertNotNull | 判断给定的对象引用是否不为 null |
assertAll 方法接收多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容的提供这些断言
@DisplayName("组合断言")
@Test
void all(){
/**
* 所有断言需要全部成功
*/
assertAll("test",
()-> assertTrue(true && false),
()-> assertEquals(1,1)
);
}
在 JUnit4 使其,想要测试方法的异常情况时,需要用 @Rule 注解的ExpectedException 变量还是比较麻烦的。而JUnit5提供了一种新的断言方式 Assertions.assertThrows(),配合函数式编程就可以进行使用
@Test
@DisplayName("异常断言")
void testException(){
// 断言业务逻辑一定出现异常
assertThrows(ArithmeticException.class, ()->{
int i = 1/0;
},"业务逻辑正常运行!!");
}
JUnit5 还提供了 Assertions.asserTimeout() 为测试方法设置了超时时间
@Test
@DisplayName("超时断言")
void testTimeoutAssert(){
/**
* 运行时间已超出 1000 毫秒
*/
assertTimeout(Duration.ofMillis(1000),()->{
Thread.sleep(1100);
});
}
通过 fail 方法直接使得测试失败
@Test
@DisplayName("快速失败")
void testFail(){
System.out.println("测试开始.......");
fail("直接宣告测试失败!");
System.out.println("测试正在运行....");
System.out.println("测试结束......");
}
JUnit5 中的前置条件(assumptions 【假设】)类似于断言,不同之处在于不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止。前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。
@Test
@DisplayName("测试前置条件")
void testAssumptions(){
Assumptions.assumeTrue(assumeIsTrue(),"结果不是true");
System.out.println("11");
}
boolean assumeIsTrue(){
return false;
}
assumeTrue 和 assumFalse 确保给定的条件为 true 或 false,不满足条件会使得测试执行种植。assumingThat 的参数是表示条件的布尔值和对应的 Executable 接口的实现对象。只有田间满足时,Executable 对象才会被执行;当条件不满足时,测试执行并不会终止
JUnit5 可以通过 Java 中的内部类和 @Nested 注解实现嵌套测试,从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用 @BeforEach 和 @AfterEach 注解,而且嵌套的层次没有限制
内层的 Test 可以驱动外层的 Befor(After)Each / All 之类的方法 提前 / 之后 运行
参数化测试是 JUnit5 很重要的一个新特性,它使得用不同的参数多次运行测试成为了可能,也为我们的单元测试带来许多遍历。
利用 @ValueSrouce 等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不是每新增一个参数就新增一个单元测试。
注解名称 | 说明 |
---|---|
@ValueSource | 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型 |
@NullSource | 表示为参数化测试提供一个null的入参 |
@EnumSource | 表示为参数化测试提供一个枚举入参 |
@CsvFileSource | 表示读取指定CSV文件内容作为参数化测试入参 |
@MethodSource | 表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流) |
当然如果参数化测试仅仅只能做到指定普通的入参还达不到让我觉得惊艳的地步。真正强大之处在于他可以支持外部的各类入参。如:CSV,YML,JSON 文件甚至方法的返回值也可以作为入参。只需要去实现 ArgumentsProvider 接口,任何外部文件都可以作为它的入参。
@ParameterizedTest
@DisplayName("参数化测试")
@ValueSource(ints = {1,2,3,4,5,6})
void testParameterize(int i){
System.out.println(i);
}
@ParameterizedTest
@DisplayName("参数化测试")
@MethodSource("stringProvider")// 方法名
void testParameterize(String i){
System.out.println(i);
}
static Stream<String> stringProvider(){
return Stream.of("apple","bannana","atguigu");
}