Springboot

springboot优点

​ 创建独立的spring应用

​ 内嵌web服务器

​ 自动starte依赖,简化构建配置

​ 自动配置spring以及第三方功能

​ 提供生产级别的监控、健康检查以及外部化检查

​ 无代码生成、无需编写xml文件

缺点

​ 版本迭代过快需要时刻关注

​ 封装太深,内部原理复杂

第一个springboot程序

条件:maven3.3以上、JDK1.8

1在电脑终端输入mvn -v查看maven的版本

2在电脑终端输入java -version查看jdk版本

3使用idea创建一个maven工程

​ 新建项目maven

打开pom.xml文件加入依赖(在springboot官网也有)

<!--springboot依赖-->
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.4</version>
</parent><!--web开发依赖springboot官网--><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies>

在java目录中创建一个主文件(MainApplication.java)

package com.springboot;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*
主程序类
@SpringBootApplication告诉springboot这是一个springboot应用*/
@SpringBootApplication
public class MainApplication {public static void main(String[] args) {/*主程序运行*/       SpringApplication.run(MainApplication.class,args);}
}

在创建一个Controller类

package com.springboot.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;/*@ResponseBody
@Controller*/
@RestController//就是@ResponseBody和@Controller
public class HelloController {@RequestMapping("/hello")public  String hello01(){return "hello Springboot2";}
}

直接运行主类就可以了

springboot主配置文件(官网链接: https://docs.spring.io/spring-boot/docs/2.4.4/reference/html/appendix-application-properties.html#common-application-properties)

导包插件(在pom.xml文件中引入插件)

<!--导包插件-->
<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins>
</build>


运行结果

Springboot依赖管理

父项目做依赖管理(子项目继承父项目子项目无需版本号)

<!--springboot依赖-->
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.4</version>
</parent>

修改pom.xml里面jar包的版本

先找到父项目的版本

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.4.4</version>
</parent>

修改Mysql(<mysql.version>8.0.23</mysql.version>)这里mysql的版本号是8.0.23

在pom.xml里面修改

<properties><mysql.version>5.1.43</mysql.version>
</properties>

starter场景启动器

1.spring-boot-starter-*;星就代表的某种场景

2.只要引入spring-boot-starter-*,这个场景的所有需要的依赖和jar包都会引入进来

所有场景启动器最底层的依赖

<dependencies><dependency><groupId>org.springframework.boot</groupId><!--场景启动器--><artifactId>spring-boot-starter-web</artifactId></dependency>
</dependencies>

3.springboot所有支持的场景官方文档链接:https://docs.spring.io/spring-boot/docs/2.4.4/reference/html/using-spring-boot.html#using-boot-starter

*-spring-boot-starter(代表着第三方的starter)

自动配置

自动配好tomcat

​ 引入tomcat依赖

​ 配置tomcat

自动配好springmvc

​ 引入springmvc全套组件

​ 自动配置好springmvc常用组件

可以在主程序下用以下方法查看

public class MainApplication {public static void main(String[] args) {//1.返回一个ioc容器ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);//查看容器里面的组件String[] names = run.getBeanDefinitionNames();for (String name : names) {System.out.println(name);}}

自动配好web常见功能

​ springboot帮我们配置好了所有web开发的场景

默认的包结构

​ 主程序所在包及其下面的所欲子包里面的组件都会被默认扫面进来

​ 无需以前的包扫描配置

​ 想要改变扫面路径,使用:

@SpringBootApplication(scanBasePackages = "com.springboot")

各种配置拥有默认值

​ 默认配置最终都会映射到MultipartProperties

​ 配置文件的值最终会绑定某个类上,这个类会在容器中创建对象

按需加载所有自动配置项

​ 非常多的starter

​ 引入了那些场景这个场景的自动配置才会开启

​ springboot所有的自动配置功能都在spring-boot-autoconfigure包里面

容器功能

组件添加

​ 1.@Configuration

​ 基本使用

​ Full模式与Lite模式

@Configuration(proxyBeanMethods =true)/*告诉springboot这是一个配置类配置类本身也是一个组件proxyBeanMethods =true 代理方法Full(proxyBeanMethods =true)、Lite(proxyBeanMethods =false)(每次访问都不一样)@Configuration(proxyBeanMethods =true)(每次访问都是IOC里面的配置)代理对象调用方法。springboot总会检查这个组件是否在容器中保持组件单实例*/
public class MyConfig {/*** 外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象* **/@Bean//给容器中添加组件,以方法名作为组件id 返回类型就是组件类型。 返回对象就是组件容器中的实例(默认为单例)public User user01(){User Zhangsan=new User("zhangsan",18);Zhangsan.setPet(tomcat());return Zhangsan;}@Bean("tom")//括号里面为自定义名字(默认名字是方法名)public Pet tomcat(){return  new Pet("12"); }
}

在main方法测试(提前在User类里加入了Pet属性)

 User user01=run.getBean("user01", User.class);
Pet tom=  run.getBean("tom",Pet.class);user01.getPet();// Full(proxyBeanMethods =true)(配置类组件之间有依赖关系,方法会被调用得到之前单例组件)//Lite(proxyBeanMethods =false)(配置组件之间无依赖关系用Lite模式加速容器启动的过程,减少判断)System.out.println("用户宠物"+(user01.getPet()==tom));

Import注解(给容器中自动创建出这类型的组件)

/* @Import给容器中自动创建出这两个类型的组件
默认组件的名字就是全类名*/
@Import({User.class , DBHelper.class})

@Conditional条件装配

@Conditional的关系树(在满足某种条件或者不满足某种条件创建组件等方法 可以使用在类前面)

我们在main方法里面尝试拿取一下组件

运行结果

@ImportResource("classpath:beans.xml")可以导入以前xml的配置方式

我在spring.xml里面创建了一个组件如果直接拿那么结果是false因为springboot不知道它是是什么

如果在任何一个类上面加入@ImportResource(“classpath:beans.xml”)那么他会自动解析里面的组件放入springboot

<bean id="hh" class="com.springboot.bean.User"><property name="name" value="12"></property><property name="age" value="11"></property>
</bean>
@ImportResource("classpath:beans.xml")//可以导入以前xml的配置方式

配置绑定

第一种在实例类上加上

prefix是前缀代表着前缀里面的属性和car里面的属性一致

@Component加入ioc容器

Car实例类

package com.springboot.bean;import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/*
* 只有在放在容器中才会生效
* */
@Component
@ConfigurationProperties(prefix = "mycard")
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;}@Overridepublic String toString() {return "Car{" +"brand='" + brand + '\'' +", price=" + price +'}';}
}

主配置文件

mycard.brand=BM
mycard.price=10000

测试

HelloController

使用自动装配

package com.springboot.controller;import com.springboot.bean.Car;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*@ResponseBody(告诉springboot直接返回给浏览器)
@Controller*/
@RestController//就是@ResponseBody和@Controller
public class HelloController {@AutowiredCar car;@RequestMapping("/card")public Car car(){return car;}
}

测试结果

第二种方法:因为我们可能引入的car为第三方的无法加入@Component导致无法导入配置文件

使用@EnableConfigurationProperties+@ConfigurationProperties(开启car配置绑定功能
把car这个组件自动注册到容器中)@EnableConfigurationProperties在主配置文件类使用

@EnableConfigurationProperties(Car.class)

运行结果

自动配置原理

引导加载自动装配

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}
), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class}
)}
)

1.@SpringBootConfiguration:

​ Configuration代表这当前是一个配置类

2.@ComponentScan(指定扫面哪些注解)

3.@EnableAutoConfiguration

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {

@AutoConfigurationPackage 自动配置包(指定了默认的包规则)

@Import({Registrar.class})
//利用Registrar给容器种导入一系列组件
//将指定的包下的所有组件导入进来MainApplication所在的报下

@Import({AutoConfigurationImportSelector.class})

1.利用this.getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件2.    List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);获取到所有需要导入到容器中的配置类3.例如工厂加载List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader)得到组件4.从META-INF/spring.factories位置来加载一个文件默认扫面我们当前系统里所有MET-INF/spring.factories位置的文件

这些组件在xml中已经写死了

按需开启自动配置项

1.当我们导入相关的jar包才会开启相应的组件

2.springboot会预先加载配置类

3.每个自动配置类按照条件生效

4.生效的配置类就会给容器中装配很对组件

5.只要容器中有这些组件,就相当于这些功能有了

6.只要用户有自己配置的,就以用户的优先

​ xxxxAutoConfiguration–>组件–>xxxProperties里面拿值—>application.properties

实战

引入场景依赖

https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot

查看自动配置了那些

​ 自己分析,引入场景对应的配置一般都会生效

​ 配置文件中的debug=true开启自动配置报告 Negative(不生效)

是否修改配置项

​ 参照文档修改配置项

​ https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties

​ 自己查看分析xxxproperties绑定了配置文件的那些

自定义加入或替换组件

Lombok(javaben简化开发)

pom.xml中引入配置

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>

再idea软件市场下载lombok插件


搜索插件里面搜索lombok

再javabean中删除set/get方法再类上加入lombok的Data的注解

如果需要定制全参构造自己写就好

package com.springboot.bean;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@NoArgsConstructor//无参构造
@Data//set/get方法
@AllArgsConstructor//全参构造
public class User {private String name;private int age;private Pet pet;public  User(String name,Integer age){this.name=name;this.age=age;}}

controller类

package com.springboot.controller;import com.springboot.bean.Car;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*@ResponseBody(告诉springboot直接返回给浏览器)
@Controller*/
@RestController//就是@ResponseBody和@Controller
@Slf4j//(日志注解提供方法log.info)
public class HelloController {@AutowiredCar car;@RequestMapping("/card")public Car car(){log.info("请求");return car;}
}

运行结果

dev-tools(开发者工具)

作用:如果后台文件或者代码发生改变会重启如果是静态资源则不会重启

<!--devtools日更新 更改前端页面无需每次刷新ctrl+f9即可实时生效(自动重启)--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></dependency>

spring Initailizr(项目初始化)

再创建新项目时选择spring Initailizr

导入了依赖

springBOOT核心功能

配置文件(yaml)

YAML是"YAML Ain’t Markup Language”(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)。

非常适合来做以数据为中心的配置文件

基本语法

key:value; kv 之间有空格

大小写敏感

使用缩进便是层级关系

缩进不允许使用tab,只允许空格

缩进的空格数不重要,只要同层级的元素左对齐即可

‘#’代表注释

"与"表示字符串内容 会被 转义/不转义

数据类型

​ 字面量:单个的、不可在分值。date boolean string number null

​ k: v

对象:键值对的集合 map hash set object

Person类(再pom.xml中引入了lombok使用@Data和@ToString再用@Component放入容器)@ConfigurationProperties(prefix = “person”)(使用前缀引入配置文件)

package com.springinitailizr.boot01.bean;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
@Data
@ToString
public class Person {private String userName;private boolean boss;private Date birth;private Integer age;private String[] interests;private List<String> animal;private Map<String,Object> score;private Set<Double> salarys;private Map<String , List<Pet>> allPets;
}

Pet

package com.springinitailizr.boot01.bean;import lombok.Data;
import lombok.ToString;@Data
@ToString
public class Pet {private String name;private Double weight;
}

application.yaml(后缀为yml也可以)

person:#字面量书写格式userName: "zs \n ls"#单引号会将\n作为字符串输出#双引号会将\n作为换行输出boss: truebirth: 2019/2/19age: 18#数组的两种书写格式#interests: [篮球,足球]interests:- 篮球- 足球- 18#List集合书写格式animal: [阿毛,阿狗]#Map集合书写的两种格式
#  score:
#    english: 80
#    math: 90score: {english: 80,math: 90}#Set集合的书写方式(和数组书写方式一样)salarys:- 999- 9998#对象属性的书写格式pet:name: 阿狗weight: 99#Map<String,List<Pet>>v是集合 书写格式allPets:sick:- {name: 阿狗,weight: 99.99}- {name: 阿2,weight: 99.99}- name: 路weight: 75.1health:- {name: 阿黑,weight: 199}- {name: 啊, weight: 200}

双引号拿Person取值

单引号拿取Person名字的值

yaml配置提示功能以及排除processor被打包

添加依赖

<!--yaml依赖(配置yaml时有代码提示)-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional>
</dependency><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes> <!--打包插件不会把processor打包--><exclude><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId></exclude></excludes></configuration></plugin></plugins></build>

提示效果:

Web开发

静态资源访问

​ 1.静态资源目录

只要静态资源放在类路径下/static(or/publicor/resourcesor/META-INF/resources)

访问:当前项目的根路径/+静态资源名

原理:静态资源映射/**

请求进来先去controller处理,如不能处理则所有请求再交给静态资源。如果静态资源也找不到则404

​ 2.静态资源访问前缀

​ 默认无前缀

配置前缀后访问路径 项目名/res/静态资源名

再yaml下配置

#静态资源前缀
spring:mvc:static-path-pattern: /res/**#改变默认静态资源目录web:resources:static-locations: classpath:/bbb/

3.webjar(把一些常用的CSS、js的静态资源打成jar包只需要引用依赖则课访问它的静态资源)(官网链接:https://www.webjars.org/)

再pom.xml中引入依赖然后重启项目进行访问:

http://localhost:8080/webjars/jquery/3.6.0/jquery.js 后面的地址要按照包路径进行访问

运行结果

欢迎页面

1.给静态资源路径下方一个index.html

​ 可以配置静态资源路径

​ 但是不可以配置静态资源的访问前缀。否则导致index.html不能被默认访问

#静态资源前缀(会导致默认页面无法访问)
spring:mvc:static-path-pattern: /res/**#改变默认静态资源目录web:resources:static-locations: [classpath:/bbb/]

2.controller能处理/index

自定义Favicon

把图片名字改为favicon.ioc放入static静态资源即可

会使Favicon失效

spring:mvc:static-path-pattern: /res/**

静态资源配置管理

​ Springboot启动默认加载 xxxAutoConfiguration类(自动配置类)

springMvc功能的自动配置类WebMvcAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {}

给容器配置了什么

 @Configuration(proxyBeanMethods = false)@Import(EnableWebMvcConfiguration.class)@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })@Order(0)public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {}

配置文件的相关属性和xxx进行了绑定。WebMvcProperties==spring.mvc、ResourceProperties==spring.resources

spring:
#  mvc:
#    static-path-pattern: /res/**resources:add-mappings: false   禁用所有静态资源规则

请求参数处理

​ 请求映射

​ Rest风格支持(使用Http请求方式动词来表示对资源的操作)

*以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户

现在: /user GET-获取用户 DELETE-删除用户* PUT-修改用户 POST-保存用户

​ 核心 Filter:HiddenHttpMethodFilter

​ 用法:表单method=post,隐藏域_method=put

​ springboot中手动开启(默认是false)

Rest原理(表单提交要使用REST的时候)

  • 表单提交会带上**_method=PUT**

  • 请求过来被HiddenHttpMethodFilter拦截

    • 请求是否正常,并且是POST
    • 获取到**_method**的值。
    • 兼容以下请求;PUT.DELETE.PATCH
    • 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
    • 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。

Rest使用客户端工具,

  • 如PostMan直接发送Put、delete等方式请求,无需Filter。
    @Bean@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {return new OrderedHiddenHttpMethodFilter();}
#开启Rest风格mvc:hiddenmethod:filter:enabled: true

HelloController

@RequestMapping(value = "/user",method = RequestMethod.GET)
public String getUser(){return "GET-张三";
}@RequestMapping(value = "/user",method = RequestMethod.POST)
public String saveUser(){return "POST-张三";
}@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String putUser(){return "PUT-张三";
}@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){return "DELETE-张三";
}

index.jsp

<form action="/user" method="get"><input value="Get提交" type="submit">
</form>
<form action="/user" method="post"><input value="post提交" type="submit">
</form>+
<form action="/user" method="post"><input name="_method" type="hidden" value="DELETE"><input value="DELETE" type="submit">
</form>
<form action="/user" method="post"><input name="_method" type="hidden" value="PUT"><input value="Put提交" type="submit">
</form>

自定义Rest风格的参数

package com.boot.web.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.HiddenHttpMethodFilter;@Configuration(proxyBeanMethods = false)
public class WebConfig {@Beanpublic HiddenHttpMethodFilter hiddenHttpMethodFilter(){HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();//修改成_m(那么请求参数就只能是_m _method就不可用)methodFilter.setMethodParam("_m");return methodFilter;}}

2、请求映射原理

SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet-》doDispatch()

RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射规则。

  • SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;

  • SpringBoot自动配置了默认 的 RequestMappingHandlerMapping

  • 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。

    • 如果有就找到这个请求对应的handler
    • 如果没有就是下一个 HandlerMapping
  • 我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping

     protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;}
    

普通参数与基本注解

​ 注解:

@PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue、@RequestBody

 <ul><li>@PathVariable(路径变量)</li><li>@RequestHeader(获取请求头)</li><li>@RequestParam(获取请求参数)</li><li>@CookieValue(获取cookie值)</li><li>@RequestAttribute(获取request域属性)</li><li>@RequestBody(获取请求体)</li><li>@MatrixVariable(矩阵变量)</li></ul>

@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("Accept-Encoding") String Accept,@RequestHeader Map<String,String> header,@RequestParam("age") Integer age,@RequestParam("inters") List<String> inters,@RequestParam Map<String,String> params,@CookieValue("freePromorunningtmr") String _ga,@CookieValue("freePromorunningtmr") Cookie cookie){Map<String,Object> map = new HashMap<>();/* map.put("id",id);map.put("name",name);map.put("pv",pv);map.put("Accept-Encoding",Accept);map.put("headers",header);*/map.put("age",age);map.put("insters",inters);map.put("params",params);map.put("_ga",_ga);System.out.println(cookie.getName()+"==="+cookie.getValue());return map;}

2、POJO封装过程

  • ServletModelAttributeMethodProcessor

    3、参数处理原理

    • HandlerMapping中找到能处理请求的Handler(Controller.method())
    • 为当前Handler 找一个适配器 HandlerAdapter; RequestMappingHandlerAdapter
    • 适配器执行目标方法并确定方法参数的每一个值

1、HandlerAdapter

0 - 支持方法上标注@RequestMapping

1 - 支持函数式编程的

执行目标方法

// Actually invoke the handler.
//DispatcherServlet -- doDispatch
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
mav = invokeHandlerMethod(request, response, handlerMethod); //执行目标方法//ServletInvocableHandlerMethod
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//获取方法的参数值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

3、参数解析器-HandlerMethodArgumentResolver

确定将要执行的目标方法的每一个参数的值是什么;

SpringMVC目标方法能写多少种参数类型。取决于参数解析器。

  • 当前解析器是否支持解析这种参数

  • 支持就调用 resolveArgument

    4、返回值处理器

=

Thymeleaf模板引擎

thymeleaf简介

现代化、服务端java模板引擎

引入

引入statr

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

2.springboot自动配置好了thymeleaf(在ThymeleafAutoConfiguration)

@Configuration(proxyBeanMethods = false
)
@EnableConfigurationProperties({ThymeleafProperties.class})
@ConditionalOnClass({TemplateMode.class, SpringTemplateEngine.class})
@AutoConfigureAfter({WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class})

自动配好的策略

​ 所有的thymeleaf的配置都在ThymeleafProperties

​ 配置好了Spring TrmplateEngine

​ 配置好了ThymeleafViewResolver

默认后缀是.html

使用

新建html引入thymeleaf的命名空间

<html lang="en" xmlns:th="http://www.thymeleaf.org">

thymeleaf公用页面抽取

在thymeleaf公共页面 8 Template Layout

声明引用

<!--公共页面放在header里面用thymeleaf的fragment包裹-->
<head th:fragment="commonheader"><meta charset="UTF-8"><title>所有公共信息</title><!--common--><link href="css/style.css" th:href="@{/css/style.css}" rel="stylesheet"><link href="css/style-responsive.css" th:href="@{css/style-responsive.css}" rel="stylesheet"><!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries --><!--[if lt IE 9]>--><script src="js/html5shiv.js"></script><script src="js/respond.min.js"></script></head>
<!--或者用id的方式-->
<div id="commonscript">
<script th:src="@{/js/jquery-1.10.2.min.js}"></script>
<script th:src="@{/js/jquery-ui-1.9.2.custom.min.js}"></script>
<script th:src="@{/js/jquery-migrate-1.2.1.min.js}"></script>
<script th:src="@{/js/bootstrap.min.js}"></script>
<script th:src="@{/js/modernizr.min.js}"></script>
<script th:src="@{/js/jquery.nicescroll.js}"></script></div>

引用的使用

视图解析

​ 视图解析:Springboot默认不支持JSP,需要引入第三方模板引擎技术实现页面渲染

视图解析原理流程

1.目标方法处理的过程中,所有数据都会放在ModelAndViewContainer里面。包括数据和视图地址

2.方法的参数是一个自定义类型对象(从请求参数中确定的,把他重新放在ModelAndViewContainer)

3.任何目标方法执行完成以后都会返回ModelAndView(数据和视图地址)

4.processDispatchResult处理派发结果(页面该如何响应)

​ 1.render(mv,request,reponse);进行页面渲染逻辑

​ 1.根据方法的String返回值得到view对象{定义了页面的渲染逻辑}

​ 2.根据返回值得到了redirect:/xxx.html—> RedirectView

​ 3.ContentNegotiationViewResolver里面包含了所有的视图解析器得到视图对象

视图解析:

​ 返回值以forward(跳转):开始:newInternalResourceView(forwardUrl);

返回值以redirect(重定向):开始: new RedirectView()

拦截器

实现HandlerInterceptor接口以及重写三个方法

package com.springboot.admin.Interceptor;import lombok.extern.slf4j.Slf4j;
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;/*
* 登录检查(拦截器的作用)
* */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {/** 目标方法执行之前* 1.配置拦截器拦截那些请求* 2.把这些配置放入容器中* */@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String uri = request.getRequestURI();//请求显示log.info("拦截的请求是"+uri);//登录检查逻辑HttpSession session=request.getSession();Object user = session.getAttribute("user");if (user!=null){//放行return  true;}//未登录进行跳转到登录页request.setAttribute("msg","请先登录");//response.sendRedirect("/");request.getRequestDispatcher("/").forward(request,response);return false;}/** 目标方法执行之后* */@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("postHandle方法执行{}"+modelAndView);}/** 页面渲染以后* */@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("afterCompletion执行异常{}"+ex);}
}

定制springMvc

package com.springboot.admin.config;import com.springboot.admin.Interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*定制Springmvc的功能
*必须实现WebMvcConfigurer
* 1.编写一个拦截器实现HandlerInterceptor接口
* 2.拦截器注册到容器中(实现WebMvcConfigurer的addInterceptor)
* 3.指定拦截规则
* */
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {//通过addInterceptor把拦截器注册到容器中registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**")//所有请求都会被拦截包括静态资源.excludePathPatterns("/","/login","/css/**","/fonts/**","/js/**","/images/**");//放行(静态资源放行的第一种方法)}
}

文件单个和多个文件上传

FormTestConfig(用来处理文件上传的请求)

package com.springboot.admin.controller;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;import java.io.File;
import java.io.IOException;/*
* 文件上传
* */
@Slf4j
@Controller
public class FormTestController {@GetMapping("/form_layouts")public  String form_layouts(){return "form/form_layouts";}/** MultipartFile会自动封装上传过来的文件* */@PostMapping("/upload")public String upload(@RequestParam("email")String email, @RequestParam("username") String username,@RequestPart("headerImg") MultipartFile headerImg,//单个文件上传参数获取@RequestPart("photos")MultipartFile[] photos) throws IOException {//多个文件上传参数获取log.info("上传信息:email={},username={},heanderimg={},photos={}",email,username,headerImg.getSize(),photos.length);if (!headerImg.isEmpty()){String filename = headerImg.getOriginalFilename();headerImg.transferTo(new File("E:\\"+filename));if (photos.length>0){for (MultipartFile photo:photos) {String MultipartFile = photo.getOriginalFilename();photo.transferTo(new File("E:\\"+MultipartFile));}}}return "main";}
}

html文件上传

<form role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data"><div class="form-group"><label for="exampleInputEmail1">邮箱</label><input type="email" name="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email"></div><div class="form-group"><label for="exampleInputPassword1">名字</label><input type="text" name="username"  class="form-control" id="exampleInputPassword1" placeholder="name"></div><div class="form-group"><label for="exampleInputFile">头像</label><input type="file" id="exampleInputFile" name="headerImg"><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><!--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>

错误处理

1、错误处理

1、默认规则

  • 默认情况下,Spring Boot提供/error处理所有错误的映射

  • 对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据

  • 自定义错误页面只需要在templates下面的error(目录名固定)下放入自定义页面即可(页面名字5xx是代表所有5开头的错误状态,404是指定错误页面消息)

异常处理自动配置原理

​ ErrorMvcAutoConfiguration 自动配置了异常处理规则

​ 容器中的组件:组件的类型DefaultErrorAttributes—》组件的id:errorAttributes

public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered

​ 容器中组件的类型:BasicErrorController—》id:basicErrorController

​ 默认处理/erro路径的请求:相应页面 new ModelAndView(“erro”,model)

​ 容器中有组件:View->id:erro

全局异常处理@ControllerAdvice+ @ExceptionHandler

@ControllerAdvice//全局异常和处理注解
public class GlobalExceptionHander {@ExceptionHandler({ArithmeticException.class,NullPointerException.class})public String handlerArithException(Exception e){log.error("异常是:",e);System.out.println("123456");return "5xx";}}

​ 自定义错误继承HandlerExceptionResolver和实现它的方法

package com.springboot.admin.exception;import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class CustomerHandlerExceptionResolver implements HandlerExceptionResolver {@Overridepublic ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {try {httpServletResponse.sendError(511,"我喜欢的错误");} catch (IOException ioException) {ioException.printStackTrace();}return new ModelAndView();}

原生组件

Filter(过滤器)

继承implements Filter和实现它的方法和注解@WebFilter(urlPatterns = {"/css/*"})

package com.springboot.admin.servlet;import lombok.extern.slf4j.Slf4j;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;@WebFilter(urlPatterns = {"/css/*"})
@Slf4j
public class MyFilter  implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("MyFilter初始化");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {log.info("MyFilter工作");filterChain.doFilter(servletRequest,servletResponse);}@Overridepublic void destroy() {log.info("MyFilter销毁");}
}

Servlet继承HttpServlet实现doget方法和注解@WebServlet(urlPatterns ="/my")

package com.springboot.admin.servlet;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns ="/my")
public class MyServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write(666);}}

Listener继承ServletContextListener和实现方法和注解@WebListener

package com.springboot.admin.servlet;import lombok.extern.slf4j.Slf4j;import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;@Slf4j
@WebListener
public class MyServletContextListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {log.info("监听到项目初始化");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {log.info("监听到项目初销毁");}
}

在main方法里面写上@ServletComponentScan(basePackages = “com.springboot.admin”)来扫描上面的方法

package com.springboot.admin;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@ServletComponentScan(basePackages = "com.springboot.admin")
@SpringBootApplication
public class Boot05AdminApplication {public static void main(String[] args) {SpringApplication.run(Boot05AdminApplication.class, args);}}

方法二(单独写一个类把监听器,拦截器和servlet放入里面加上bean注解放入容器中)

package com.springboot.admin.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;@Configuration(proxyBeanMethods = true)
public class MyRegistConfig {@Beanpublic ServletRegistrationBean myServlet(){MyServlet myServlet = new MyServlet();return new ServletRegistrationBean(myServlet,"/my","/my02");}@Beanpublic FilterRegistrationBean myFilter(){MyFilter myFilter = new MyFilter();FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));return filterRegistrationBean;}@Beanpublic ServletListenerRegistrationBean myListener(){MyServletContextListener myServletContextListener = new MyServletContextListener();return new ServletListenerRegistrationBean(myServletContextListener);}
}

1、切换嵌入式Servlet容器

  • 默认支持的webServer

    • Tomcat, Jetty, or Undertow
    • ServletWebServerApplicationContext 容器启动寻找ServletWebServerFactory 并引导创建服务器
  • 切换服务器

<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>
  • 原理

    • SpringBoot应用启动发现当前是Web应用。web场景包-导入tomcat
    • web应用会创建一个web版的ioc容器 ServletWebServerApplicationContext
    • ServletWebServerApplicationContext 启动的时候寻找 ServletWebServerFactory(Servlet 的web服务器工厂---> Servlet 的web服务器)
    • SpringBoot底层默认有很多的WebServer工厂;TomcatServletWebServerFactory, JettyServletWebServerFactory, or UndertowServletWebServerFactory
    • 底层直接会有一个自动配置类。ServletWebServerFactoryAutoConfiguration
    • ServletWebServerFactoryAutoConfiguration导入了ServletWebServerFactoryConfiguration(配置类)
    • ServletWebServerFactoryConfiguration 配置类 根据动态判断系统中到底导入了那个Web服务器的包。(默认是web-starter导入tomcat包),容器中就有 TomcatServletWebServerFactory
    • TomcatServletWebServerFactory 创建出Tomcat服务器并启动;TomcatWebServer 的构造器拥有初始化方法initialize---this.tomcat.start();
    • 内嵌服务器,就是手动把启动服务器的代码调用(tomcat核心jar包存在

2、定制Servlet容器

  • 实现 WebServerFactoryCustomizer

    • 把配置文件的值和**ServletWebServerFactory 进行绑定**
  • 修改配置文件 server.xxx

  • 直接自定义 ConfigurableServletWebServerFactory

数据访问

SQL

数据源配置

​ 导入JDBC场景

 <!--数据库访问--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jdbc</artifactId></dependency>
</dependencies>

2.分析自动配置

数据库驱动

springboot仲裁了mysql驱动版本

<mysql.version>8.0.23</mysql.version>

数据库驱动

<!--修改版本方法一直接修改version使用maven的就近依赖原则-->
<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>

2、分析自动配置

1、自动配置的类

  • DataSourceAutoConfiguration : 数据源的自动配置

    • 修改数据源相关的配置:spring.datasource
    • 数据库连接池的配置,是自己容器中没有DataSource才自动配置的
    • 底层配置好的连接池是:HikariDataSource
    @Configuration(proxyBeanMethods = false)@Conditional(PooledDataSourceCondition.class)@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })protected static class PooledDataSourceConfiguration

**
**

  • DataSourceTransactionManagerAutoConfiguration: 事务管理器的自动配置

  • JdbcTemplateAutoConfiguration: JdbcTemplate的自动配置,可以来对数据库进行crud

    • 可以修改这个配置项@ConfigurationProperties(prefix = “spring.jdbc”) 来修改JdbcTemplate
    • @Bean@Primary JdbcTemplate;容器中有这个组件
  • JndiDataSourceAutoConfiguration: jndi的自动配置

  • XADataSourceAutoConfiguration: 分布式事务相关的

测试

package com.springboot.admin;import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;@Slf4j
@SpringBootTest
class Boot05AdminApplicationTests {@AutowiredJdbcTemplate jdbcTemplate;@Testvoid contextLoads() {Long along=jdbcTemplate.queryForObject("select count(*) from student",Long.class);log.info("记录总数{}",along);}}

使用Druid数据源

整合第三方技术的两种方式

1、druid官方github地址

https://github.com/alibaba/druid

​ 自定义

​ 找starter

2.自定义方式

1.创建数据源

<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.10</version>
</dependency>

绑定配置文件以及监控页

package com.springboot.admin.config;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.sql.DataSource;
@Configuration
public class MyDataSourceConfig {//绑定配置文件(默认跟application.yaml绑定)@ConfigurationProperties("spring.datasource")@Beanpublic DataSource dataSource(){DruidDataSource druidDataSource = new DruidDataSource();
//        druidDataSource.setUrl();
//        druidDataSource.setUsername();
//        druidDataSource.setPassword();
//加入监控功能druidDataSource.setFilters("stat");return druidDataSource;}/*配置监控页*/@Beanpublic ServletRegistrationBean statViewServlet(){StatViewServlet statViewServlet = new StatViewServlet();ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");return registrationBean;}
}

测试结果

写一个请求测试类来测试是否能监控到sql

//数据监控请求
@Autowired
JdbcTemplate jdbcTemplate;
@ResponseBody
@GetMapping("/sql")
public String queryFromDb(){Long along=jdbcTemplate.queryForObject("select count(*) from student",Long.class);return along.toString();
}

测试结果

开启web监控

通过官方文档可知要在web.xml里面放入filter

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aoWgfogb-1618298400523)(C:\Users\lbw\AppData\Roaming\Typora\typora-user-images\image-20210408215216881.png)]

/*监控web应用*/
@Bean
public FilterRegistrationBean webStatFilter(){WebStatFilter webStatFilter = new WebStatFilter();FilterRegistrationBean<WebStatFilter> webStatFilterFilterRegistrationBean = new FilterRegistrationBean<>(webStatFilter);webStatFilterFilterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));webStatFilterFilterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");return webStatFilterFilterRegistrationBean;
}

测试结果

配置监控页的登录

@Bean
public ServletRegistrationBean statViewServlet(){StatViewServlet statViewServlet = new StatViewServlet();ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");/*配置监控页面的登录账号密码*/registrationBean.addInitParameter("loginUsername","admin");registrationBean.addInitParameter("loginPassword","123456");return registrationBean;
}

StatViewServlet配置

根据官方文档引入druid-starter

<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.17</version>
</dependency>

分析自动配置

  • 扩展配置项 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的配置
spring:datasource:url: jdbc:mysql://localhost:3306/testusername: rootpassword: root#  type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.jdbc.Driverdruid:aop-patterns: com.springboot.admin.* #监控springbeanfilters: stat,wall #底层功能开启,stat(sql监控),wall(防火墙)stat-view-servlet: #  #监控页配置(账号密码)enabled: truelogin-username: adminlogin-password: 123465#web监控web-stat-filter:enabled: trueurl-pattern: '/*'exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'filter: #对上面stat和wall的详细配置stat:slow-sql-millis: 1000log-slow-sql: truewall:enabled: trueconfig:update-allow: truejdbc:template:query-timeout: 3

整合Mybatis操作

https://github.com/mybatis

starter(命名规范)

springBoot官网的Starter:spring-boot-starter-*

第三方: *-spring-boot-starter


引入statr

<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.4</version>
</dependency>

配置模式

全局配置文件

SqlsessionFactory:以及自动配置好了

Sqlsession:自动配置了SqlsessionTemplate组合了Sqlsession

@import(AutoConfigredMapperScannerRegistrar.class)

Mapper:只要我们操作Mybatis的接口标准了@Mapper就会被扫描进来

@EnableConfigurationProperties({MybatisProperties.class})//Mybatis配置绑定类
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})public class MybatisProperties {public static final String MYBATIS_PREFIX = "mybatis";}

修改配置文件中mybatis开始的所有;

#配置mybatis规则
mybatis:config-location: classpath:mybatis/mybatis-config.xml #指定全局配置文件位置mapper-locations: classpath:mybatis/mapper/*.xml #sql映射文件位置

测试使用

Mapper接口绑定xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.springboot.admin.mapper.StudentMapper"><select id="getStudent" resultType="com.springboot.admin.bean.Student">select * from student where stuno=#{stuno}</select>
</mapper>

操作数据库

开启驼峰命名

​ 所有配置都在MybatisProperties自动配置好了

写一个Mapepr接口(接口名要和StudentMapper.xml里面的namespace引用路径一致,方法名要和id一致注意要写@Mapper注解)

package com.springboot.admin.mapper;import com.springboot.admin.bean.Student;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface StudentMapper {public Student getStudentBystuno(Long stuno);
}

service

@Service
public class StudentService  {@AutowiredStudentMapper studentMapper;public Student getStudentBystuno(Long stuno){return  studentMapper.getStudentBystuno(stuno);}
}

controller

//整合mybatis测试
@Autowired
StudentService studentService;
@GetMapping("/acct")
@ResponseBody
public Student getBystuno(@RequestParam("stuno") Long stuno){return   studentService.getStudentBystuno(stuno);
}

访问/acct并且给值结果

配置 private Configuration configuration; mybatis.configuration下面的所有,就是相当于改mybatis全局配置文件中的值

config-location configuration不能同时存在

 #mybatis全局配置
mybatis:
#  config-location: classpath:mybatis/mybatis-config.xml #指定mybatis-config.xmlmapper-locations: classpath:mybatis/mapper/*.xml #扫面mapper.xmlconfiguration:map-underscore-to-camel-case: true#可以不写全局;配置文件,所有全局配置文件的配置都放在configuration配置项中即可

导入mybatis官方starter

编写mapper接口。标准@Mapper注解

编写sql映射文件并绑定mapper接口

在application.yaml中指定Mapper配置文件的位置,以及指定全局配置文件的信息 (建议;配置在mybatis.configuration

注解模式的mybatis配置

@Mapper
public interface CityMapper {@Select("select * from city where id=#{id}")public City getById(Long id);public void insert(City city);}

混合模式

@Mapper
public interface CityMapper {@Select("select * from city where id=#{id}")public City getById(Long id);public void insert(City city);}

最佳实战:

  • 引入mybatis-starter
  • 配置application.yaml中,指定mapper-location位置即可
  • 编写Mapper接口并标注@Mapper注解
  • 简单方法直接注解方式
  • 复杂方法编写mapper.xml进行绑定映射
  • @MapperScan(“com.atguigu.admin.mapper”) 简化,其他的接口就可以不用标注@Mapper注解

Mybatis-plus

 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version></dependency>

自动配置

  • 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能力

实战:(创建一个user表)并且增加一些数据

CREATE TABLE user
(id BIGINT(20) NOT NULL COMMENT '主键ID',name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',age INT(11) NULL DEFAULT NULL COMMENT '年龄',email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',PRIMARY KEY (id)
);
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

创建一个实例类

package com.springboot.admin.bean;import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.context.annotation.Configuration;@AllArgsConstructor//lombok插件中的注解
@NoArgsConstructor
@Data
@ToString
//@TableName("user_tbl")//默认通过类去找名字一样的表名此注解是指定表名
public class User {/** 所有属性都应该在数据库中* */@TableField(exist = false)//表中不存在private String username;@TableField(exist = false)private String password;//数据库字段private Long id;private String name;private Integer age;private String email;}

mapper接口继承BaseMapper很多方法已经封装到BaseMapper

package com.springboot.admin.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.springboot.admin.bean.User;public interface UserMapper extends BaseMapper<User> {}

service接口(IService很多方法已经封装到Iservice里面无需再写)

package com.springboot.admin.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.springboot.admin.bean.User;public interface Userservice extends IService<User> {}

service实现类(虽然service中没有写方法但是Iservice中就有很多方法这些方法我们不能所有都实现继承ServiceImpl<操作的mapper对象,操作的实现类>就无需实现这些方法)

package com.springboot.admin.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.springboot.admin.bean.User;
import com.springboot.admin.mapper.UserMapper;
import com.springboot.admin.service.Userservice;
import org.springframework.stereotype.Service;@Service
public class UserServiceimpl extends ServiceImpl<UserMapper, User> implements Userservice {}

controller

 @AutowiredUserservice userservice;@GetMapping("/dynamic_table")public  String  dynamic_table(Model model){//表格内容的遍历
//        List<User> users = Arrays.asList(new User("zhangsan", "123456"), new User("ls", "456789"),
//                new User("llkk", "555442"));//       model.addAttribute("users",users);//查出user表中的数据进行展示mybatis-plusList<User> list = userservice.list();model.addAttribute("users",list);return "table/dynamic_table";}

前端使用Thymeleaf来进行拿值

<table  class="display table table-bordered table-striped" id="dynamic-table">
<thead>
<tr><th>#</th><th>id</th><th>name</th><th>age</th><th class="hidden-phone">email</th><th class="hidden-phone">操作</th>
</tr>
</thead><tbody><tr class="gradeX" th:each="user,stat:${users}"><td th:text="${stat.count}">Trident</td><td th:text="${user.id}">id</td><td th:text="${user.name}"></td><td th:text="${user.age}">Win 95+</td><td class="center hidden-phone" >[[${user.email}]]</td><td class="center hidden-phone">X</td></tr>

效果

分页

mybatis-plus分页插件

创建一个comfig

package com.springboot.admin.config;import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyBatisConfig {@Configuration@MapperScan("scan.your.mapper.package")public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor paginationInterceptor() {MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false// paginationInterceptor.setOverflow(false);// 设置最大单页限制数量,默认 500 条,-1 不受限制// paginationInterceptor.setLimit(500);// 开启 count 的 join 优化,只针对部分 left join//分页拦截器PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();paginationInnerInterceptor.setOverflow(true);paginationInnerInterceptor.setMaxLimit(500L);mybatisPlusInterceptor.addInnerInterceptor(paginationInnerInterceptor);return mybatisPlusInterceptor;}}
}

controller(IPage里面包含了所有user类里面的数据所以去掉model.addAttribute(“users”,list)所有方法请参照IPage(Idea按两次shift输入IPage下载源码即可查看注解))

  Userservice userservice;@GetMapping("/dynamic_table")public  String  dynamic_table(@RequestParam(value = "pn",defaultValue ="1")Integer pn,Model model){//表格内容的遍历
//        List<User> users = Arrays.asList(new User("zhangsan", "123456"), new User("ls", "456789"),
//                new User("llkk", "555442"));
//        model.addAttribute("users",users);//查出user表中的数据进行展示mybatis-plusList<User> list = userservice.list();//model.addAttribute("users",list);//分页实现Page<User> userPage = new Page<>(pn, 2);//分页查询的结果Page<User> page = userservice.page(userPage, null);//当前第几页long current = page.getCurrent();//总记录数long pages = page.getPages();//所有数据List<User> records = page.getRecords();model.addAttribute("users",page);return "table/dynamic_table";}

分页条

 <div class="row-fluid"><div class="span6"><div class="dataTables_info" id="dynamic-table_info">当前第[[${users.current}]]页 总计 [[${users.pages}]]页  共[[${users.total}]]条数据</div></div><div class="span6"><div class="dataTables_paginate paging_bootstrap pagination"><ul><li class="prev disabled"><a th:href="@{/dynamic_table(pn=${users.current}-1)}" >← 上一页</a></li><!--th:each="num:${#numbers.sequence(1,users.pages)}"参照thymeleaf numbers--><li th:class="${num == users.current?'active':''}"th:each="num:${#numbers.sequence(1,users.pages)}"><a th:href="@{/dynamic_table(pn=${num})}">[[${num}]]</a></li><li class="next"><a th:href="@{/dynamic_table(pn=${users.current}+1)}" >下一页 → </a></li></ul></div></div></div>

删除功能

html页面准备一个按钮参考thymeleaf官网文档4.4Link URLs可知**@{/order/{orderId}/details(orderId=${orderId})}动态带值**

<tr class="gradeX" th:each="user,stat:${users.records}"><td th:text="${stat.count}">Trident</td><td th:text="${user.id}">id</td><td th:text="${user.name}"></td><td th:text="${user.age}">Win 95+</td><td class="center hidden-phone" >[[${user.email}]]</td><td class="center hidden-phone"><a th:href="@{/user/delete/{id}(id=${user.id},pn=${users.current})}" class="btn btn-danger btn-sm" type="button">删除</a></td>
</tr>

controller

@GetMapping("/user/delete/{id}")
public String  deleteUser(@PathVariable("id") Long id, @RequestParam(value = "pn",defaultValue = "1")Integer pn,RedirectAttributes ra){userservice.removeById(id);ra.addAttribute("pn",pn);return "redirect:/dynamic_table";
}

单元测试

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的测试引擎。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xLUi1Fp2-1618298400533)(C:\Users\lbw\AppData\Roaming\Typora\typora-user-images\image-20210412011751745.png)]

注意:

SpringBoot 2.4 以上版本移除了默认对 Vintage 的依赖。如果需要兼容junit4需要自行引入(不能使用junit4的功能 @Test)

JUnit 5’s Vintage Engine Removed from spring-boot-starter-test,如果需要继续兼容junit4需要自行引入vintage

<dependency><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.hamcrest</groupId><artifactId>hamcrest-core</artifactId></exclusion></exclusions>
</dependency>

现在引用再springboot创建时就自动帮我们创建了一个测试类默认是Junit5 @SpringBootTest(此注解就是Junit的测试注解)

@SpringBootTest
class Boot05WebAdminApplicationTests {@Testvoid contextLoads() {}
}

以前:

@SpringBootTest + @RunWith(SpringTest.class)

SpringBoot整合Junit以后。

  • 编写测试方法:@Test标注(注意需要使用junit5版本的注解)
  • Junit类具有Spring的功能,@Autowired、比如 @Transactional 标注测试方法,测试完成后自动回滚

Junit5的常用注解

​ 官网地址:https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations