【注:本笔记参照雷神公开课程所做笔记,仅学习使用】
一、Spring Boot入门程序 1.1 创建Maven工程,添加配置在pom.xml里添加配置
1.2 创建主程序org.springframework.boot spring-boot-starter-parent 2.3.4.RELEASE org.springframework.boot spring-boot-starter-web
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.SpringApplication;
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
1.3 业务编写
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/hello")
public String handler01(){
return "Hello, Spring boot 2!";
}
}
1.4 运行代码
- 运行MainApplication类在浏览器输入http://localhost:8080/hello
可以在resource文件夹中创建application.properties文件,其中可以设置配置。
二、依赖管理 2.1 starter场景启动器- spring-boot-starter-*,其中的*代表某一种场景只要引入starter,这个场景的所有常规需要的依赖都自动引入
Springboot所有支持的场景:链接第三方starter:*-spring-boot-starter所有场景启动器依赖,最底层的依赖,都会依赖spring-boot-starter
2.2 无需关注版本号,自动版本仲裁org.springframework.boot spring-boot-starter 2.3.4.RELEASE compile
- 引入依赖默认都可以不写版本号引入非版本仲裁的jar,要写版本号
- 查看spring-boot-dependencies里面规定当前依赖的版本用的key。在当前项目里面重写配置,如下面的代码。
三、自动配置特性 3.1 自动配置Tomcat5.1.43
3.2 自动配置SpringMVCorg.springframework.boot spring-boot-starter-tomcat 2.3.4.RELEASE compile
引入了SpringMVC的全套组件自动配置SpringMVC的常用组件 3.3 自动配置Web的常见功能
SpringBoot已经自动配置好了web开发的所有场景,比如字符编码问题自动配置解决
3.4 自动配置了包结构- 主程序所在的包及其下面的所有子包都会默认扫描无需包扫描配置如果路由不想放在主程序包下,即扫描不到时,可以改变扫描路径,使用@SpringBootApplication(scanbasePackages=“com...”)
@SpringBootApplication等同于:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.lun")
3.5 各种配置拥有默认值
默认配置最终都是映射到某个类上配置文件的值最终会绑定每个类上,这个类会在容器中创建对象 3.6 按需加载自动配置项
有很多的starter,引入了哪些场景这个场景的自动配置才会开启SpringBoot的所有自动配置功能都在 spring-boot-autoconfigure 包里面 四、注解详解 4.1 @Configuration
- @Configuration告诉SpringBoot这是一个配置类配置类里面使用@Bean注解,给容器添加组件,以方法名作为组件的id,返回类型就是组件的类型,返回值是在容器中的实例配置类本身也是组件Full全配置:如果@Configuration里的proxyBeanMethods值是true(默认),意思是保证每个@Bean方法被调用多少次返回的组件都是单实例的。去容器中找组件。Lite轻量级:如果@Configuration里的proxyBeanMethods值是false,意思是调用实例是每一次调用都会产生一个新的对象
配置类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
配置类组件之间有依赖关系,方法会被调用得到之前的单实例组件,用Full模式
import com.atguigu.boot.bean.Pet;
import com.atguigu.boot.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false) //这个注解,告诉SpringBoot这是一个配置类
public class MyConfig {
@Bean //给容器添加组件,以方法名作为组件的id,返回类型就是组件类型,返回的值就的组件是实例
public User user01(){
return new User("zhangsan", 18);
}
@Bean("tom")
public Pet tomcatPet(){
return new Pet("tomcat");
}
}
import com.atguigu.boot.bean.Pet;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
// 1. 返回IOC容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
// 2. 查看容器里的组件
String[] names = run.getBeanDefinitionNames();
for (String name : names){
System.out.println(name);
}
// 3. 从容其中获取组件*
Pet tom01 = run.getBean("tom", Pet.class);
}
}
4.2 @Conditional
条件装配:满足Conditional指定的条件,则进行组件注入
以@ConditionalOnBean(name = “tom”)为例,这个注解加在@Bean之前,就意味着容器中存在tom的实例时,才可以创建这个Bean此标签还可以加在类上,意思是如果容器中有tom时候,这个类的方法才能实现
4.3 @importResource
如果bean是由xml文件导入的,此时可以在配置文件类上使用@importResource注解,让其导入xml文件
@importResource("classpath:beans.xml")
4.4 配置绑定
目的:使用Java读取到properties文件中的内容,并且把它封装到JavaBean中。
1. @ConfigurationProperties + @Component
配置文件里有配置
mycar.brand = BYD mycar.price = 100000
给Java实例添加注解@Component,@ConfigurationProperties(prefix = "")
先创建容器,才会拥有SpringBoot的功能
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "mycar")//这里的mycar是properties中的mycar
public class Car {
private String brand;
private Integer price;
public Car() {
}
public Car(String brand, Integer price) {
this.brand = brand;
this.price = price;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + ''' +
", price=" + price +
'}';
}
}
自动装配
@RestController
public class HelloController {
@Autowired
Car car;
@RequestMapping("/car")
public Car car(){
return car;
}
}
2. @EnableConfigurationProperties + @ConfigurationProperties
在配置类里开启配置绑定
@EnableConfigurationProperties(Car.class)
public class MyConfig {
...
}
自动注入到容器中
@ConfigurationProperties(prefix = "mycar")
public class Car {
...
}
五、SpringBoot技巧
5.1 最佳应用实战技巧
1. 引入场景依赖
官方文档
2. 查看自动配置了哪些(选做)
自己分析,引入场景对应的自动配置一般都生效了配置文件中debug=true开启自动配置报告,Negative(不生效)/ Positive(生效)
3. 是否需要修改
参照文档修改配置项
参照文档自己分析。xxxxProperties绑定了配置文件的哪些。 自定义加入或者替换组件
@Bean、@Component… 自定义器 XXXXXCustomizer 5.2 Lombok简化开发
Lombok用标签方式代替构造器、getter/setter、toString等代码。
spring boot已经管理Lombok;
引入依赖
org.projectlombok lombok
安装Lombok插件
使用
简化JavaBean代码
import lombok.*;
@ToString //toString()方法
@Data //getter/setter方法
@AllArgsConstructor //全参构造器
@NoArgsConstructor //无参构造器
@EqualsAndHashCode //重写equals和hashcode方法
public class Pet {
private String name;
}
简化日志开发
@Slf4j
public class HelloController {
@RequestMapping("/hello")
public String handler01(){
log.info("....");
return "Hello, Spring boot 2!";
}
}
使用dev-tools项目热更新
添加依赖
org.springframework.boot spring-boot-devtools true
当修改代码之后,按Ctrl+F9即可
1. 基本语法
key: value;kv之间有空格,大小写敏感使用缩进表示层级关系,缩进不允许使用tab,只允许空格缩进的空格数不重要,只要相同层级的元素左对齐即可'#'表示注释字符串无需加引号单引号、双引号表示字符串内容会被转义、不转义
2. 实例
person:
userName: zhangsan
boss: true
birth: 2022/3/1
age: 22
interests:
- 篮球
- 足球
animal: [阿猫,阿狗]
score: {english: 80,math: 90}
salarys:
- 999.99
- 999.98
pet:
name: 阿狗
weight: 99.99
allPets:
sick:
- {name: 阿狗, weight: 99.99}
- name: 阿猫
weight: 88.88
- name: 阿猪
weight: 77.77
health:
- {name: 阿虫, weight: 3.3}
import lombok.Data;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ConfigurationProperties(prefix = "person")
@Component
@ToString
@Data
public class Person {
private String userName;
private Boolean boss;
private Date birth;
private Integer age;
private Pet pet;
private String[] interests;
private List animal;
private Map score;
private Set salarys;
private Map> allPets;
}
5.5 自定义类配置的代码提示
- 自定义的类中自己定义的属性往往在yaml中书写时没有提示,那么可以使用自定义类配置的代码提示引入依赖
org.springframework.boot spring-boot-configuration-processor true
静态资源默认的文件夹名:/static 、 /public 、 /resources 、 /meta-INF/resources
访问方式:当前项目根路径+静态资源名
当请求进来时,会先去匹配动态请求,如果请求未找到则再去静态资源中找,静态资源也找不到则返回404
静态资源默认映射 @PostMapping("/saveuser") public Person saveuser(Person person){ return person; }
运行结果,将请求的值,和JavaBean里面的属性进行自动绑定。
如果想返回json数据的话,需要引入依赖
org.springframework.boot spring-boot-starter-json 2.3.4.RELEASE compile
只需要在控制层的方法上添加@ResponseBody注解即可
import com.atguigu.boot.bean.Person;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Date;
@Controller
public class ResponseTestController {
@ResponseBody
@GetMapping("/test/person")
public Person getPerson(){
Person person = new Person();
person.setAge(28);
person.setBirth(new Date());
person.setUserName("zs");
return person;
}
}
1. 引入starter
org.springframework.boot spring-boot-starter-thymeleaf
2. 自动配置好thymeleaf
自动配好的策略
所有thymeleaf的配置值都在ThymeleafProperties配置好了 SpringTemplateEngine配好了 ThymeleafViewResolver我们只需要直接开发页面 7.2 thymeleaf语法
在html标签中,加上themeleaf命名空间
基本语法
${…}:变量取值,获取请求域、session域或对象的值*{…}:选择变量,获取上下文对象的值#{…}:消息,获取国际化等值@{…}:链接,生成链接~{…}:片段表达式,引入公共页面片段
字面量
文本值: ‘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)
特殊操作
无操作: _
八、单元测试Junit5Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库
8.1 Junit5常用注解@Test:表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试
@DisplayName:为测试类或者测试方法设置展示名称
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("junit5功能测试类")
public class testJunit5 {
@DisplayName("测试displayname注解")
@Test
void testDisplayName(){
System.out.println(1);
}
}
@BeforeEach:表示在每个单元测试之前执行
@AfterEach:表示在每个单元测试之后执行
package com.atguigu.boot;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("junit5功能测试类")
public class testJunit5 {
@DisplayName("测试displayname注解")
@Test
void testDisplayName(){
System.out.println(1);
}
@BeforeEach
void testBeforeEach(){
System.out.println("测试就要开始了");
}
@AfterEach
void testAfterEach(){
System.out.println("测试就要结束了");
}
}
@BeforeAll:表示在所有单元测试之前执行
@AfterAll:表示在所有单元测试之后执行
@Tag:表示单元测试类别,类似于JUnit4中的@Categories
@Disabled:表示测试类或测试方法不执行
@Timeout:表示测试方法运行如果超过了指定时间将会返回错误
@Timeout(value = 500, unit = TimeUnit.MICROSECONDS)
@Test
void testTimeout() throws InterruptedException {
Thread.sleep(500);
}
- @ExtendWith:为测试类或测试方法提供扩展类引用@RepeatedTest(n):重复测试n次
断言Assertion是测试方法中的核心部分,用来对测试需要满足的条件进行验证这些断言方法都是org.junit.jupiter.api.Assertions的静态方法检查业务逻辑返回的数据是否合理,所有的测试运行结束以后,会有一个详细的测试报告
| 方法 | 说明 |
|---|---|
| assertEquals | 判断两个对象或两个原始类型是否相等 |
| assertNotEquals | 判断两个对象或两个原始类型是否不相等 |
| assertSame | 判断两个对象引用是否指向同一个对象 |
| assertNotSame | 判断两个对象引用是否指向不同的对象 |
| assertTrue | 判断给定的布尔值是否为 true |
| assertFalse | 判断给定的布尔值是否为 false |
| assertNull | 判断给定的对象引用是否为 null |
| assertNotNull | 判断给定的对象引用是否不为 null |
前边的断言失败了,后面的代码都不会执行
import com.atguigu.boot.bean.Person;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
public class testAssertions {
@DisplayName("测试简单断言")
@Test
void testSimpleAssertions(){
int cal = cal(2, 3);
// 是否计算的值是5
assertEquals(5, cal, "业务逻辑计算失败");
// 是否是同一个对象
Person p1 = new Person();
Person p2 = new Person();
assertSame(p1, p2,"两个对象不一样");
}
int cal(int i, int j){
return i+j;
}
}
8.2.2 数组断言
比较数组是否相等
@DisplayName("数组断言")
@Test
void array(){
assertArrayEquals(new int[]{2, 1}, new int[]{1, 2}, "数组内容不相等");
}
8.2.3 组合断言
一个断言中有好多个断言
@DisplayName("组合断言")
@Test
void all(){
assertAll("test",
()->assertTrue(true && true, "结果不是true"),
()->assertEquals(1 ,2, "结果不是1"));
System.out.println(1);
}
8.2.4 异常断言
程序肯定会有异常,如果没有异常则返回“异常居然正常运行”
@DisplayName("异常断言")
@Test
void testException(){
assertThrows(ArithmeticException.class, ()->{int i = 10/0;}, "业务逻辑居然正常运行");
}
8.2.5 超时断言
@Test
@DisplayName("超时测试")
public void timeoutTest() {
//如果测试方法时间超过1s将会异常
Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}
8.2.6 快速失败
通过 fail 方法直接使得测试失败
@Test
@DisplayName("fail")
public void shouldFail() {
fail("This should fail");
}
8.3 Assumptions假设
assumptions类似于断言,不同之处在于不满足的断言assertions会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class testAssumptions {
@DisplayName("测试前置条件")
@Test
void testassumptions(){
Assumptions.assumeTrue(true, "结果不是true");
System.out.println(1111);
}
}
九、 数据访问
9.1 配置Druid数据源和MySQL数据库
1. 导入JDBC依赖
org.springframework.boot spring-boot-starter-data-jdbc
2. 导入MySQL数据库依赖
mysql mysql-connector-java
3. 导入Druid-starter数据源依赖
com.alibaba druid-spring-boot-starter 1.1.17
4. 在application.yaml中添加配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/
username: root
password: 123
driver-class-name: com.mysql.jdbc.Driver
9.2 注解方式整合MyBatis
1. 创建项目
添加MyBatis、MySQL和Web
2. 已经自动导入了数据库相关的依赖
org.springframework.boot spring-boot-starter-web org.mybatis.spring.boot mybatis-spring-boot-starter 2.2.2 mysql mysql-connector-java runtime 5.1.49 org.springframework.boot spring-boot-starter-test test org.projectlombok lombok
3. 编写Mapper接口
import com.hkd.bean.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper {
@Select("select * from user1 where id = #{id}")
public User getId(int id);
}
4. 编写Controller
import com.hkd.bean.User;
import com.hkd.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Slf4j
@Controller
public class UserController {
@Autowired
UserService userService;
@ResponseBody
@GetMapping("/user")
public User getId(@RequestParam("id") int id) {
return userService.getId(id);
}
}
5. 编写Service层
import com.hkd.bean.User;
import com.hkd.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
UserMapper userMapper;
public User getId(int id){
return userMapper.getId(id);
}
}
6. 编写配置文件
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/jdbc
username: root
password: 123
mvc:
hiddenmethod:
filter:
enabled: true #开启页面表单的Rest功能
7. 编写Bean
import lombok.Data;
@Data
public class User {
private Integer id;
private String username;
private String password;
}
8. 数据库不在此展示了,展示效果图如下
1. 引入依赖
由于mybatis-plus已经给我们引入了jdbc、mybatis,所以我们只需要引入这一个依赖即可
org.springframework.boot spring-boot-starter-web mysql mysql-connector-java runtime 5.1.49 org.springframework.boot spring-boot-starter-test test org.projectlombok lombok com.baomidou mybatis-plus-boot-starter 3.5.1
2. mybatis-plus自动配置
MybatisPlusAutoConfiguration配置类,MybatisPlusProperties配置项绑定。SqlSessionFactory自动配置好,底层是容器中默认的数据源。mapperLocations自动配置好的,有默认值classpath*:/mapper*.xml,这表示任意包的类路径下的所有mapper文件夹下任意路径下的所有xml都是sql映射文件。 建议以后sql映射文件放在 mapper下。容器中也自动配置好了SqlSessionTemplate。@Mapper 标注的接口也会被自动扫描,建议直接@MapperScan(“com.lun.boot.mapper”)批量扫描。
十、拦截器 10.1 拦截器的使用MyBatisPlus优点之一:只需要我们的Mapper继承MyBatisPlus的baseMapper 就可以拥有CRUD能力,减轻开发工作。
- 编写一个拦截器实现HandlerInterceptor接口拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors())指定拦截规则【注意,如果是拦截所有,静态资源也会被拦截】
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//登录检查
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 {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
import com.hkd.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") // 拦截所有请求,包括静态资源
.excludePathPatterns("/css/**", "/fonts/**", "/img/**", "/js/**"); //放行
}
}
10.2 拦截器的执行时机和原理
- 根据当前请求,找到HandlerExecutionChain(可以处理请求的handler以及handler的所有拦截器)先来顺序执行所有拦截器的preHandle()方法。如果当前拦截器preHandle()返回为true。则执行下一个拦截器的preHandle()如果当前拦截器返回为false。直接倒序执行所有已经执行了的拦截器的 afterCompletion()如果任何一个拦截器返回false,直接跳出不执行目标方法。所有拦截器都返回true,才执行目标方法。倒序执行所有拦截器的postHandle()方法。前面的步骤有任何异常都会直接倒序触发 afterCompletion()页面成功渲染完成以后,也会倒序触发 afterCompletion()



