文档地址: https://www.yuque.com/atguigu/springboot
视频地址: http://www.gulixueyuan.com/ https://www.bilibili.com/video/BV19K4y1L7MT?p=1
源码地址:https://gitee.com/leifengyang/springboot2
Create stand-alone Spring applications
Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
Provide opinionated ‘starter’ dependencies to simplify your build configuration
Automatically configure Spring and 3rd party libraries whenever possible
Provide production-ready features such as metrics, health checks, and externalized configuration
Absolutely no code generation and no requirement for XML configuration
SpringBoot是整合Spring技术栈的一站式框架
SpringBoot是简化Spring技术栈的快速开发脚手架
<mirrors>
<mirror>
<id>nexus-aliyunid>
<mirrorOf>centralmirrorOf>
<name>Nexus aliyunname>
<url>http://maven.aliyun.com/nexus/content/groups/publicurl>
mirror>
mirrors>
<profiles>
<profile>
<id>jdk-1.8id>
<activation>
<activeByDefault>trueactiveByDefault>
<jdk>1.8jdk>
activation>
<properties>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
<maven.compiler.compilerVersion>1.8maven.compiler.compilerVersion>
properties>
profile>
profiles>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.4.RELEASEversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
/**
* 主程序类
* @SpringBootApplication:这是一个SpringBoot应用
*/
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class,args);
}
}
@RestController
public class HelloController {
@RequestMapping("/hello")
public String handle01(){
return "Hello, Spring Boot 2!";
}
}
直接运行main方法
application.properties, 直接在这个文件修改配置就可,下面的就是修改了 服务器的端口号,改成 8888
server.port=8888
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
把项目打成jar包,直接在目标服务器执行即可。
依赖管理
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.4.RELEASEversion>
parent>
他的父项目
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>2.3.4.RELEASEversion>
parent>
几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制
1、见到很多 spring-boot-starter-* : *就某种场景
2、只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
3、SpringBoot所有支持的场景
https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
4、见到的 *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。
5、所有场景启动器最底层的依赖
org.springframework.boot
spring-boot-starter
2.3.4.RELEASE
compile
1、引入依赖默认都可以不写版本
2、引入非版本仲裁的jar,要写版本号。
1、查看spring-boot-dependencies里面规定当前依赖的版本 用的 key。
2、在当前项目里面重写配置
5.1.43
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
<version>2.3.4.RELEASEversion>
<scope>compilescope>
dependency>
@SpringBootApplication
等同于
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.nguyenxb.boot")
基本使用
Full模式与Lite模式
配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式
###############Configuration使用示例 #################################
/**
* 1. 配置类里面使用 @Bean 标注在方法上给容器注册组件,默认也是单实例的.
* 2. 配置类也是本身也是组件
* 3. proxyBeanMethods : 代理 bean 的方法
* Full: @Configuration(proxyBeanMethods = true) , 单例模式
* 外部无论对配置类中的这个注册方法调用多少次, 获取的都是 之前注册容器中的单实例对象.
* Lite : @Configuration(proxyBeanMethods = false) , 多实例模式.
* 外部无论对配置类中的这个注册方法调用多少次, 获取的都是 新创建的实例对象.
*
*/
// @Configuration:告诉springboot 这是一个配置类, 即配置文件
@Configuration(proxyBeanMethods = false)
public class MyConfig {
/*
外部无论对配置类中的这个注册方法调用多少次, 获取的都是 之前注册容器中的单实例对象.
*
* */
/**
* @Bean 给容器中添加组件. 以方法名作为组件的id,返回类型是组件的类型,
* 返回值就是组件在容器中的实例
*/
@Bean
public User user01(){
User zhangsan = new User("zhangsan",20);
// zhangsan.setPet();
return zhangsan;
}
@Bean("tomcatPet") // 给容器中添加组件. 以tomcatPet作为组件的id
* 返回值就是组件在容器中的实例
public Pet tomcat(){
return new Pet("tom");
}
}
####################### MainApplication使用 ##############################
/**
* 主程序类
* @SpringBootApplication:这是一个SpringBoot应用
*/
//@SpringBootApplication // 这个注解包含了下面三个注解, 用于声明这个是springboot应用
@EnableAutoConfiguration
@SpringBootConfiguration
@ComponentScan("com.nguyenxb.boot") // 这是组件扫描的注解,指定扫描的包为 com.nguyenxb.boot
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("tomcatPet", Pet.class);
Pet tom02 = run.getBean("tomcatPet", Pet.class);
System.out.println("Pet 组件 :" +(tom01==tom02)); // Pet 组件 :true
// 4.如果 设置@Configuration(proxyBeanMethods = true)代理对象调用方法
// springboot 总是会检查容器中是否存在该组件.保证组件的唯一性
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
User user01 = bean.user01();
User user = bean.user01();
System.out.println("user :"+(user==user01));
// 5. 获取容器中的组件
String[] beansOfType = run.getBeanNamesForType(User.class);
for (String name :
beansOfType) {
System.out.println("user:"+name);
}
String[] beansOfType1 = run.getBeanNamesForType(Pet.class);
for (String name : beansOfType1){
System.out.println("pet:"+name);
}
}
}
// @Import({User.class,Pet.class }) : 给容器中自动创建出两个类型的组件,默认的组件名字就是全类名, 即springboot容器中创建了 两个实例 com.nguyenxb.boot.bean.User, com.nguyenxb.boot.bean.Pet.
@Import({User.class, Pet.class})
@Configuration(proxyBeanMethods = false)
public class MyConfig {}
@Import 高级用法: https://www.bilibili.com/video/BV1gW411W7wy?p=8
条件装配:满足Conditional指定的条件,则进行组件注入
// ConditionalOnMissingBean 当容器中没有 某个组件时才执行某些事情
// ConditionalOnSingleCandidate 当容器中的 某个组件只有一个实例时
// ConditionalOnBean 当容器中有某个组件时
// ConditionalOnProperty 当容器中配置文件配置了某个属性时
// ConditionalOnCloudPlatform
// ConditionalOnJndi
// Profile
// ConditionalOnWarDeployment
// ConditionalOnNotWebApplication 当项目不是web应用时
// ConditionalOnMissingClass 当容器中没有某个类时
// ConditionalOnClass 当容器中有某一个类时
// ConditionalOnResource 当项目的类路径存在某些资源时
// ConditionalOnWebApplication 当项目是web应用时
// ConditionalOnRepositoryType
// ConditionalOnEnabledResourceChain
// ConditionalOnJava 当我们项目时某个版本号时
// ConditionalOnExpression
@ ConditionalOnBean示例
@ConditionalOnBean(name="tom22") // 当springboot容器中 存在 组件名为 tom22时, springboot才能创建这个MyConfig1 里面的所有组件 , 即容器中有 tom22时, 会创建组件,其id为 tom和user01.
// @ConditionalOnMissingBean (name = "tom22") // 当springboot容器中 不存在 组件名为 tom22时, springboot才能创建这个MyConfig1 里面的所有组件, 即容器中 没有tom22时候,不会创建该类的任何组件
@Configuration
public class MyConfig1 {
@Bean
public Pet tom(){
return new Pet() ;
}
@ConditionalOnBean(name="tom11") // 当容器中存在 tom11时, 会执行该方法,并向容器中注入 user01的组件
@Bean
public User user01(){
return new User() ;
}
}
导入配置文件中的组件
beans.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user02" class="com.nguyenxb.boot.bean.User">
<property name="name" value="user02">property>
bean>
beans>
使用方法:在类上方使用注解,并指定文件的路径名称即可导入user02组件到springboot容器中.
@ImportResource("classpath:beans.xml")
@Configuration(proxyBeanMethods = true)
public class MyConfig {}
application.properties
mycar.brand=saasd
mycar.price=99999
如何使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用;
public class getProperties {
public static void main(String[] args) throws FileNotFoundException, IOException {
Properties pps = new Properties();
pps.load(new FileInputStream("application.properties"));
Enumeration enum1 = pps.propertyNames();//得到配置文件属性的名字
while(enum1.hasMoreElements()) {
String strKey = (String) enum1.nextElement();
String strValue = pps.getProperty(strKey);
System.out.println(strKey + "=" + strValue);
//封装到JavaBean。
}
}
}
/**
* 只有在容器中的组件,才会拥有SpringBoot提供的强大功能
*/
@Component
@ConfigurationProperties(prefix = "mycar")// 即读取springboot的核心配置文件中mycar.brand=saasd 的前缀
public class Car {
private String brand;
private Integer price;
// 已经省略set,get,toString方法
}
这个用来解决当方式一的存在的问题, 即当 引入其他包时, 他对于的某个类没有使用 @Component 注解 , 就能使用这个方式.
############### MyConfig.java (配置类) #########################
@EnableConfigurationProperties(Car.class) // 这个注解必须写在配置注解这里, 开启Car类的属性配置功能
//1、开启Car配置绑定功能
//2、把这个Car这个组件自动注册到容器中
@Configuration(proxyBeanMethods = true)
public class MyConfig {}
################## bean包下 的实体类 #####################
@ConfigurationProperties(prefix = "mycar")
public class Car {
private String brand;
private Integer price;
}
@SpringBootApplication // 这个注解包含了下面三个注解, 他是springboot的核心注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication{}
这个注解里面还封装了 @Configuration , 代表当前类是一个配置类
指定扫描包,即封装的Spring注解的扫描包;
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
自动配置包, 指定了默认的包规则
@Import(AutoConfigurationPackages.Registrar.class) //给容器中导入一个组件
public @interface AutoConfigurationPackage {}
//利用Registrar给容器中导入一系列组件
//将指定的一个包下的所有组件导入进来?即MainApplication 所在包下。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);// 此方法导入组件
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
2、调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
3、利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
4、从META-INF/spring.factories位置来加载一个文件。
默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
从 spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories 的文件中 导入 127 个自动配置类, 这个是文件写死的.
虽然我们127个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration
按照条件装配规则(@Conditional),最终会按需配置。
@Bean
@ConditionalOnBean(MultipartResolver.class) //容器中有这个类型组件时候 满足条件
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件
public MultipartResolver multipartResolver(MultipartResolver resolver) {
//给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
//SpringMVC multipartResolver。防止有些用户配置的文件上传解析器的名字不符合规范
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
// 给容器中加入了文件上传解析器;
SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先
// 比如 用户配置了字符集过滤器, springboot就默认使用, 用户配置的过滤器
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {}
SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
生效的配置类就会给容器中装配很多组件
只要容器中有这些组件,相当于这些功能就有了
定制化配置
xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 ----> application.properties
引入场景依赖
查看自动配置了哪些(选做)
是否需要修改
简化javaBean的开发
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
用法 :
package com.nguyenxb.boot.bean;
import lombok.*;
@ToString // 声明 toString 方法
@Data // 声明 set,get方法 , 包含toString方法
@AllArgsConstructor // 声明 全参数的构造器
@NoArgsConstructor // 声明 无参数构造器
@EqualsAndHashCode // 声明 hashcode方法.
public class User {
private String name;
private Integer age;
}
import com.nguyenxb.boot.bean.Car;
import lombok.extern.slf4j.Slf4j;
@Slf4j // 用于输出日志的注解
@RestController // 控制器对象注解
public class HelloController {
@RequestMapping("/hello")
public String handle01(){
log.info("请求进来了... ");
return "Hello, Spring Boot 2!";
}
}
自动更新工具, 实际就是自动重启 , spring也提供了不重启的热更新工具 jrebel,但是要付费
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
与以前的properties用法相同
YAML 是 “YAML Ain’t Markup Language”(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)。
非常适合用来做以数据为中心的配置文件
key: value;kv之间有空格
大小写敏感
使用缩进表示层级关系
缩进不允许使用tab,只允许空格 (idea 可以忽略这个情况)
缩进的空格数不重要,只要相同层级的元素左对齐即可
'#'表示注释
字符串无需加引号,如果要加,’'与""表示字符串内容 会被 转义/不转义
k: v
行内写法: k: {k1:v1,k2:v2,k3:v3}
# 或者缩进式写法
k:
k1: v1
k2: v2
k3: v3
行内写法: k: [v1,v2,v3]
# 或者缩进式写法: 从上往下 依次存入的集合的数据.
k:
- v1
- v2
- v3
// ==============Person.java==================
@Data
public class Person {
private String userName;
private Boolean boss;
private Date birth;
private Integer age;
private Pet pet;
private String[] interests;
private List<String> animal;
private Map<String, Object> score;
private Set<Double> salarys;
private Map<String, List<Pet>> allPets;
}
// ===============Pet.java====================
@Data
public class Pet {
private String name;
private Double weight;
}
application.yaml
# yaml表示以上对象
person:
userName: zhangsan
# userName: "zhangsan \n 张三"
# userName: 'zhangsan \n 张三'
# 单引号会将 \n 作为字符串输出, 双引号会将\n 作为 换行输出
# 即 双引号不会转义, 单引号会转义
boss: false
birth: 2020/1/1
age: 18
pet:
# 使用缩进式给 pet 对象赋值
name: tomcat
weight: 23.4
# 行内给 interests 字符串数组赋值
interests: [篮球,游泳]
# 使用缩进式给 animal list集合赋值
animal:
- jerry
- mario
# 使用缩进式给 score map集合赋值
score:
english:
first: 30
second: 40
third: 50
math: [131,140,148]
chinese: {first: 128,second: 136}
salarys: [3999,4999.98,5999.99]
# 使用缩进式给 allPets map>集合赋值
allPets:
sick:
- {name: tom}
- {name: jerry,weight: 47}
health: [{name: mario,weight: 47}]
给yaml文件添加提示功能, 自定义的类和配置文件绑定一般没有提示.
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
exclude>
excludes>
configuration>
plugin>
plugins>
build>
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.(大多场景我们都无需自定义配置)
The auto-configuration adds the following features on top of Spring’s defaults:
Inclusion of ContentNegotiatingViewResolver
and BeanNameViewResolver
beans.
Support for serving static resources, including support for WebJars (covered later in this document)).
Automatic registration of Converter
, GenericConverter
, and Formatter
beans.
Converter,GenericConverter,Formatter
Support for HttpMessageConverters
(covered later in this document).
HttpMessageConverters
(后来我们配合内容协商理解原理)Automatic registration of MessageCodesResolver
(covered later in this document).
MessageCodesResolver
(国际化用)Static index.html
support.
Custom Favicon
support (covered later in this document).
Favicon
Automatic use of a ConfigurableWebBindingInitializer
bean (covered later in this document).
ConfigurableWebBindingInitializer
,(DataBinder负责将请求数据绑定到JavaBean上)If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration
class of type WebMvcConfigurer
but without @EnableWebMvc
.
不用@EnableWebMvc注解。使用 @Configuration
+ WebMvcConfigurer
自定义规则
If you want to provide custom instances of RequestMappingHandlerMapping
, RequestMappingHandlerAdapter
, or ExceptionHandlerExceptionResolver
, and still keep the Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations
and use it to provide custom instances of those components.
声明 WebMvcRegistrations
改变默认底层组件
If you want to take complete control of Spring MVC, you can add your own @Configuration
annotated with @EnableWebMvc
, or alternatively add your own @Configuration
-annotated DelegatingWebMvcConfiguration
as described in the Javadoc of @EnableWebMvc
.
使用 @EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration 全面接管SpringMVC
默认情况下,Spring Boot 从类路径中的/static
(或/public
或/resources
或/META-INF/resources
)目录或 ServletContext
的根目录提供静态内容。
它使用 Spring MVC 中的 ResourceHttpRequestHandler
,因此你可以通过添加自己的 WebMvcConfigurer
和重写resourcehandlers
方法来修改该行为。
如何访问静态资源? : 当前项目的根路径 + 静态资源名.
访问原理: 静态资源映射的访问路径为 /**
请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面
# springboot的默认静态资源设置为
spring.mvc.static-path-pattern=/resources/**
改变默认的静态资源路径
spring:
# 设置访问静态资源的前缀为 localhost:8080/res/
mvc:
static-path-pattern: /res/**
# 设置静态资源的存储路径 为 haha 目录下的文件
resources:
static-locations: [classpath:/haha/]
#即通过修改这个之后, 他的访问静态资源的地址 为
# localhost:8080/res/haha/文件名.jpg
springboot 默认访问是无前缀的, 即访问静态资源的方式就是 localhost:8080/文件名.jpg
spring:
mvc:
static-path-pattern: /resources/**
当前项目 + static-path-pattern + 静态资源名 , 注 : 静态资源名在静态资源文件夹下找,按照路径写上.
自动映射 : /webjars/**
https://www.webjars.org/
<dependency>
<groupId>org.webjarsgroupId>
<artifactId>jqueryartifactId>
<version>3.5.1version>
dependency>
访问地址:http://localhost:8080/webjars/jquery/3.5.1/jquery.js 后面地址要按照依赖里面的包路径
静态资源路径下 index.html
spring:
# mvc:
# static-path-pattern: /res/** 这个会导致welcome page功能失效
resources:
static-locations: [classpath:/haha/]
Favicon
favicon.ico 放在静态资源目录下即可。就是网站的图标
@xxxMapping;
Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
扩展:如何把_method 这个名字换成我们自己喜欢的。
package com.nguyenxb.boot.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 {
@Bean
public HiddenHttpMethodFilter methodFilter(){
HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
// 设置隐藏的表单访问方式 为 _m 即
/*
*
* */
// 修改的就是
methodFilter.setMethodParam("_m");
return methodFilter;
}
}
请求页面
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>aaaaatitle>
head>
<body>
<h1>indexh1>
<form action="/user" method="get">
<input type="hidden" name="_method" value="GET">
<input type="submit" value="发起get请求">
form>
<form action="/user" method="post">
<input type="hidden" name="_method" value="POST">
<input type="submit" value="发起POST请求">
form>
<form action="/user" method="post">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="发起delete请求">
form>
<form action="/user" method="post">
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="发起put请求">
form>
body>
html>
Controller
@GetMapping("/user")
//@RequestMapping(value = "/user",method = RequestMethod.GET)
public String getUser(){
return "GET-张三";
}
@PostMapping("/user")
// @RequestMapping(value = "/user",method = RequestMethod.POST)
public String saveUser(){
return "POST-张三";
}
@DeleteMapping("/user")
//@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String putUser(){
return "PUT-张三";
}
@PutMapping("/user")
//@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){
return "DELETE-张三";
}
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
//自定义filter
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
methodFilter.setMethodParam("_m");
return methodFilter;
}
Rest原理(表单提交要使用REST的时候)
表单提交会带上**_method=PUT**
请求过来被HiddenHttpMethodFilter拦截
_method
**的值。Rest使用客户端工具,
spring:
mvc:
hiddenmethod:
filter:
enabled: true #开启页面表单的Rest功能
SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet-》doDispatch()
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 找到当前请求使用哪个Handler(Controller的方法)处理
mappedHandler = getHandler(processedRequest);
//HandlerMapping:处理器映射。/xxx->>xxxx
所有的请求映射都在HandlerMapping中。
SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping
@PathVariable
@PathVariable
无属性值时,获取的时 get路径下的所有参数, 并且参数用Map@PathVariable Map pv
@PathVariable
有属性值时,获取的时 get路径下的相对应的参数,如: @PathVariable("id") Integer id, @PathVariable("username") String username
// 发起请求的方式:
// 发起请求:/user/car/1/owner/张三
@RestController
public class ParameterTestController {
@GetMapping("/user/car/{id}/owner/{username}")
public Map<String,Object> test1(@PathVariable("id") Integer id,
@PathVariable("username") String username,
@PathVariable Map<String,String> pv){
System.out.println("id : "+id);
System.out.println("username: "+username);
for (String key : pv.keySet()) {
String value = pv.get(key);
System.out.println("pv == "+key+":::"+value);
}
/*
* 控制台输出 :
id : 1
username: 张三
pv == id:::1
pv == username:::张三
* */
// 将数据输出到浏览器页面上.
Map<String,Object> map = new HashMap<>();
map.put("id",id);
map.put("username",username);
map.put("pv",pv);
return map;
}
@RequestHeader
用法与pathVariable相同// 1: 发起请求:/user/car1/2/owner/李四
@GetMapping("/user1/car/{id}/owner/{username}")
public Map<String,Object> test2(@PathVariable("id") Integer id,
@PathVariable("username") String username,
@PathVariable Map<String,String> pv,
@RequestHeader("User-Agent") String user_agent,
@RequestHeader Map<String,String> reqHeader){
System.out.println("id : "+id);
System.out.println("username: "+username);
for (String key : pv.keySet()) {
String value = pv.get(key);
System.out.println("pv == "+key+":::"+value);
}
System.out.println("==========================");
System.out.println("user_agent : "+user_agent);
for (String key : reqHeader.keySet()) {
String value = reqHeader.get(key);
System.out.println("reqHeader == "+key+":::"+value);
}
Map<String,Object> map = new HashMap<>();
map.put("id",id);
map.put("username",username);
map.put("pv",pv);
map.put("user-agent",user_agent);
map.put("request_header",reqHeader);
return map;
}
@RequestParam
List
类型的数据来接收Map
类型来接收.// 3:发起请求
@GetMapping("/user3/car/{id}/owner/{username}")
public void test3(@PathVariable("id") Integer id,
@PathVariable("username") String username,
@PathVariable Map<String,String> pv,
@RequestParam("age") Integer age,
@RequestParam("inters") List<String> inters,
@RequestParam Map<String,String> reqParam){
System.out.println("id : "+id);
System.out.println("username: "+username);
for (String key : pv.keySet()) {
String value = pv.get(key);
System.out.println("pv == "+key+":::"+value);
}
System.out.println("==========================");
System.out.println("age : "+age);
inters.forEach(inter -> System.out.println("inter : "+inter));
for (String key : reqParam.keySet()) {
String value = reqParam.get(key);
System.out.println("reqParam == "+key+":::"+value);
}
/*
* 输出 :
id : 3
username: 王五
pv == id:::3
pv == username:::王五
==========================
age : 20
inter : basketball
inter : game
reqParam == age:::20
reqParam == inters:::basketball
* */
}
@CookieValue
// a href="/user4/car/4/owner/赵六">发起请求: 4
@GetMapping("/user4/car/{id}/owner/{username}")
public void test4(@PathVariable("id") Integer id,
@PathVariable("username") String username,
@PathVariable Map<String,String> pv,
// 这里获取的是 浏览器的Cookie 键 , 必须要先查看 浏览器发起的cookie名称是否一致.
@CookieValue("Idea-480fa3ba") String cookieStr,
@CookieValue("Idea-480fa3ba") Cookie cookie){
System.out.println("id : "+id);
System.out.println("username: "+username);
for (String key : pv.keySet()) {
String value = pv.get(key);
System.out.println("pv == "+key+":::"+value);
}
System.out.println("==========================");
System.out.println("cookieStr : "+ cookieStr);
System.out.println("cookie :"+ cookie.toString());
/*
* 输出 :
id : 4
username: 赵六
pv == id:::4
pv == username:::赵六
==========================
cookieStr : 4d5cbe6a-5240-401f-b963-e5f8a5c271cc
cookie :javax.servlet.http.Cookie@5fee8fa1
* */
}
@RequestBody
, 只有post 才有请求体./*
*/
@PostMapping("/user5")
public void postTest5(@RequestBody String context){
// 获取请求体的数据
System.out.println(context);
// 输出: name=reqBody&age=30
}
@RequestAttribute
HttpServletRequest
对象来获取 存入 request作用域中的数据@RequestAttribute
来获取 request作用域中的数据.// 浏览器地址栏 输入 : http://localhost:8080/reqGoto 发起请求.
@Controller
public class RequestController {
@GetMapping("/reqGoto")
public String reqGoto(HttpServletRequest request){
request.setAttribute("msg","成功处理请求");
request.setAttribute("code",200);
return "forward:/success";
}
@GetMapping("/success")
@ResponseBody
public Map success(@RequestAttribute("msg") String msg,
@RequestAttribute("code") Integer code,
HttpServletRequest request){
String msg1 = (String) request.getAttribute("msg");
Integer code1 = (Integer) request.getAttribute("code");
System.out.println("msg : "+msg);
System.out.println("code : "+code);
System.out.println("=================");
System.out.println("msg1 : "+msg1);
System.out.println("code1 : "+code1);
Map<String,Object> map = new HashMap<>();
map.put("msg",msg);
map.put("code",code);
map.put("msg1",msg1);
map.put("code1",code1);
return map;
}
}
/* 输出结果 :
msg : 成功处理请求
code : 200
=================
msg1 : 成功处理请求
code1 : 200
*/
@MatrixVariable
/*1. 矩阵变量语法:
请求路径: 矩阵变量请求 1
* 2. springboot默认禁用了矩阵变量的功能.
需要手动开启, 其原理是 使用 UrlPathHelper 进行路径解析
removeSemicolonContent(移除分号内容)用来控制矩阵变量是否开启的.
3. 矩阵变量必须有url路径变量才能被解析
* */
@GetMapping("/cars/{path}")
public Map carsSell1(@MatrixVariable("low") Integer low,
@MatrixVariable("brand") List<String> brands,
@PathVariable("path") String path){
System.out.println("low : "+low);
brands.forEach(brand -> System.out.println("brand : "+brand));
HashMap<String, Object> map = new HashMap<>();
map.put("low",low);
map.put("brand",brands);
map.put("path",path);
return map;
}
// 当请求路径中存在多个相同的值时,如何获取对于的值? ,可以通过指定pathVar属性来获取相应的值.
//请求方式: 矩阵变量 2
@GetMapping("/boss/{bossId}/{empId}")
public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,
@MatrixVariable(value = "age",pathVar = "empId") Integer empAge){
System.out.println("bossAge : "+bossAge);
System.out.println("empAge :" + empAge);
Map<String,Object> map = new HashMap<>();
map.put("bossAge",bossAge);
map.put("empAge",empAge);
return map;
}
方式一: 实现WebMvcConfigurer 接口, 重写configurePathMatch方法
@Configuration(proxyBeanMethods = false)
public class WebConfigTest1 implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
// 获取路径帮助器
UrlPathHelper pathHelper = new UrlPathHelper();
// 设置不移除 分号后面的内容, 这样才能让矩阵变量功能生效
pathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(pathHelper);
}
}
方式二 : 向springboot 容器中注入 重写了 configurePathMatch 方法的 WebMvcConfigurer 对象
@Configuration(proxyBeanMethods = false)
public class WebConfigTest2 {
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
// 获取路径帮助器
UrlPathHelper urlPathHelper = new UrlPathHelper();
// 设置不移除 分号后面的内容, 这样才能让矩阵变量功能生效
urlPathHelper.setRemoveSemicolonContent(false);
// 配置路径帮助器
configurer.setUrlPathHelper(urlPathHelper);
}
};
}
}
WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId
Map、**Model(map、model里面的数据会被放在request的请求域 request.setAttribute)、**Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder
Map<String,Object> map, Model model, HttpServletRequest request 都是可以给request域中放数据,
然后统一通过 request.getAttribute(); 获取请求域中的数据.
Map、Model类型的参数,会返回 mavContainer.getModel();—> BindingAwareModelMap 是Model 也是Map
mavContainer.getModel(); 获取到值的
可以自动类型转换与格式化,可以级联封装。
/**
* 姓名:
* 年龄:
* 生日:
* 宠物姓名:
* 宠物年龄:
*/
@Data
public class Person {
private String userName;
private Integer age;
private Date birth;
private Pet pet;
}
@Data
public class Pet {
private String name;
private String age;
}
ServletModelAttributeMethodProcessor
会把请求中携带的数据 , 在内存中创建一个临时的对象, 用来存放请求中的所有数据. 即WebDataBinder 对象.
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
WebDataBinder :web数据绑定器,将请求参数的值绑定到指定的JavaBean里面
WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中
GenericConversionService:在设置每一个值的时候,找它里面的所有converter那个可以将这个数据类型(request带来参数的数据)转换到指定的类型(JavaBean – Integer)
未来我们可以给WebDataBinder里面放自己的Converter;
// 通过自定义该方法 可以 将字符串类型 转换为我们想要的类型
private static final class StringToNumber<T extends Number> implements Converter<String, T>
/** 需求 : 自定义上传 数据的逻辑, 上传宠物数据的时候, 只需要通过逗号 就能区分数据的类型.
* 姓名:
* 年龄:
* 生日:
* // 宠物姓名:
* // 宠物年龄:
宠物 即对应的就是 name,age
*/
//1、WebMvcConfigurer定制化SpringMVC的功能
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
// 不移除;后面的内容。矩阵变量功能就可以生效
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
// 添加格式化方法
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new Converter<String, Pet>() {
// 把 String 变成 宠物对象的 转换方法.
// source 是请求 提交的值.
@Override
public Pet convert(String source) {
// name,age :: 啊猫,3
if(!StringUtils.isEmpty(source)){
Pet pet = new Pet();
String[] split = source.split(",");
pet.setName(split[0]);
pet.setAge(Integer.parseInt(split[1]));
return pet;
}
return null;
}
});
}
};
}
json响应使用的是 : jackson.jar+@ResponseBody
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jsonartifactId>
<version>2.3.4.RELEASEversion>
<scope>compilescope>
dependency>
只有引入json依赖和添加@ResponseBody就能给前端自动返回json数据.
其底层的返回值处理器 : RequestResponseBodyMethodProcessor
返回值解析器原理
SpringMVC到底支持哪些返回值
ModelAndView
Model
View
ResponseEntity
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
有 @ModelAttribute 且为对象类型的
返回值标注了 @ResponseBody注解 就会使用RequestResponseBodyMethodProcessor这个处理器
HTTPMessageConverter原理
HttpMessageConverter: 消息转换器: 即判断是否支持将 此 Class类型的对象,转为MediaType类型的数据。
例子:Person对象转为JSON。或者 JSON转为Person
默认的MessageConverter
其实封装的就是springMVC中的HttpMessageConverter 对象.
根据客户端接收能力不同,返回不同媒体类型的数据。
<dependency>
<groupId>com.fasterxml.jackson.dataformatgroupId>
<artifactId>jackson-dataformat-xmlartifactId>
dependency>
只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。
为了方便内容协商,开启基于请求参数的内容协商功能。
spring:
contentnegotiation:
favor-parameter: true #开启请求参数内容协商模式
发请求: http://localhost:8080/test/person?format=json , 发送json数据
http://localhost:8080/test/person?format=xml , 发送xml类型的数据.
实现多协议数据兼容。json、xml、x-nguyenx
// 自定义 messageConverter
public class NguyenMessageConverter implements HttpMessageConverter<Person> {
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return false;
}
// 当 传入的数据类型是 person类时, 开放写入权限.
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return clazz.isAssignableFrom(Person.class);
}
/*
服务器要统计所有MessageConverter 都能写出哪些内容类型
服务器调用这个方法后 会知道可以写出 这个类型 application/x-nguyen 的数据.
*
* */
@Override
public List<MediaType> getSupportedMediaTypes() {
return MediaType.parseMediaTypes("application/x-nguyen");
}
@Override
public List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
return null;
}
@Override
public Person read(Class<? extends Person> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
@Override
public void write(Person person, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
// 自定义协议将数据写出.
String data = person.getId()+";"+person.getName()+";"+person.getAge()+";"+person.getEmail()+";";
// 将数据写出去
OutputStream body = outputMessage.getBody();
body.write(data.getBytes());
}
}
将自定义的converter添加到容器中, 并自定义内容协商策略
@Configuration(proxyBeanMethods = false)
public class WebConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
// 自定义内容协商策略
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
Map<String, MediaType> mediaTypes = new HashMap<>();
mediaTypes.put("json",MediaType.APPLICATION_JSON);
mediaTypes.put("xml",MediaType.APPLICATION_XML);
mediaTypes.put("x-nguyen",MediaType.parseMediaType("application/x-nguyen"));
// 指定支持解析 参数对应的媒体类型.
ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(mediaTypes);
HeaderContentNegotiationStrategy headerStrategy = new HeaderContentNegotiationStrategy();
configurer.strategies(Arrays.asList(strategy,headerStrategy));
}
// 添加 消息转换器
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new NguyenMessageConverter());
}
};
}
}
除了自定义这些内容,也可以参照SpringBoot官方文档web开发内容协商章节直接使用springboot提供的配置方法.
视图解析:SpringBoot默认不支持 JSP,需要引入第三方模板引擎技术实现页面渲染。
1、目标方法处理的过程中,所有数据都会被放在 ModelAndViewContainer 里面。包括数据和视图地址
2、方法的参数是一个自定义类型对象(从请求参数中确定的),把他重新放在 ModelAndViewContainer
3、任何目标方法执行完成以后都会返回 ModelAndView(数据和视图地址)。
**4、**processDispatchResult 处理派发结果(页面改如何响应)
1、render(mv, request, response); 进行页面渲染逻辑
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模板引擎
表达式名字 | 语法 | 用途 |
---|---|---|
变量取值 | ${…} | 获取请求域、session域、对象等值 |
选择变量 | *{…} | 获取上下文对象值 |
消息 | #{…} | 获取国际化等值 |
链接 | @{…} | 生成链接 |
片段表达式 | ~{…} | jsp:include 作用,引入公共页面片段 |
文本值: ‘one text’ , ‘Another one!’ **,…**数字: 0 , 34 , 3.0 , 12.3 **,…**布尔值: true , false
空值: null
变量: one,two,… 变量不能有空格
字符串拼接: +
变量替换: |The name is ${name}|
运算符: + , - , * , / , %
运算符: and , or
一元运算: ! , not
比较: > , < , >= , <= ( gt , lt , ge , le **)**等式: == , != ( eq , ne )
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
无操作: _
设置单个值
<form action="subscribe.html" th:attr="action=@{/subscribe}">
<fieldset>
<input type="text" name="email" />
<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
fieldset>
form>
设置多个值
<img src="../../images/gtvglogo.png" th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
以上两个的代替写法 th:xxxx
<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
<tr th:each="prod : ${prods}">
<td th:text="${prod.name}">Onionstd>
<td th:text="${prod.price}">2.41td>
<td th:text="${prod.inStock}? #{true} : #{false}">yestd>
tr>
<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
<td th:text="${prod.name}">Onionstd>
<td th:text="${prod.price}">2.41td>
<td th:text="${prod.inStock}? #{true} : #{false}">yestd>
tr>
view
User is an administrator
User is a manager
User is some other thing
1.引入Starter
// 声明使用 <html xmlns:th="http://www.thymeleaf.org">
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
2.springboot自动配置了thymeleaf,直接使用即可
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ThymeleafProperties.class)
@ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class })
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })
public class ThymeleafAutoConfiguration { }
其配置规则:
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html"; //xxx.html
通过实现接口实现拦截功能. 具体内容查看springmvc知识点
/**
* 登录检查
* 1、配置好拦截器要拦截哪些请求
* 2、把这些配置放在容器中
*/
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
/**
* 目标方法执行之前
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
log.info("preHandle拦截的请求路径是{}",requestURI);
//登录检查逻辑
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
if(loginUser != null){
//放行
return true;
}
//拦截住。未登录。跳转到登录页
request.setAttribute("msg","请先登录");
// re.sendRedirect("/");
request.getRequestDispatcher("/").forward(request,response);
return false;
}
/**
* 目标方法执行完成以后
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle执行{}",modelAndView);
}
/**
* 页面渲染以后
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion执行异常{}",ex);
}
/**
* 1、编写一个拦截器实现HandlerInterceptor接口
* 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
* 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
*/
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //所有请求都被拦截包括静态资源
.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的登录页面和静态资源的请求
}
}
1、根据当前请求,找到**HandlerExecutionChain【**可以处理请求的handler以及handler的所有 拦截器】
2、先来顺序执行 所有拦截器的 preHandle方法
3、如果任何一个拦截器返回false。直接跳出不执行目标方法
4、所有拦截器都返回True。执行目标方法
5、倒序执行所有拦截器的postHandle方法。
6、前面的步骤有任何异常都会直接倒序触发 afterCompletion
7、页面成功渲染完成以后,也会倒序触发 afterCompletion
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form th:action="/upload">
用户名: <input type="text" name="username"><br/>
邮箱:<input type="text" name="email"><br/>
头像: <input name="headerImg" type="file"><br/>
多图片:<input name="photos" type="file" multiple><br/>
<input type="submit" value="提交">
form>
body>
html>
/**
* 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={},headerImg={},photos={}",
email,username,headerImg.getSize(),photos.length);
// 当上传的文件不为空时,将文件保存到服务器
if(!headerImg.isEmpty()){
//保存到文件服务器,OSS服务器
String originalFilename = headerImg.getOriginalFilename();
headerImg.transferTo(new File("H:\\cache\\"+originalFilename));
}
if(photos.length > 0){
for (MultipartFile photo : photos) {
if(!photo.isEmpty()){
String originalFilename = photo.getOriginalFilename();
photo.transferTo(new File("H:\\cache\\"+originalFilename));
}
}
}
return "main";
}
# 设置上传单个文件的最大为10MB,所有文件的最大 100MB
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=100MB
**文件上传自动配置类-MultipartAutoConfiguration-**MultipartProperties
自动配置好了 StandardServletMultipartResolver 【文件上传解析器】
原理步骤
FileCopyUtils。实现文件流的拷贝
/error
处理所有错误的映射src/
+- main/
+- java/
| +
ErrorController
并注册该类型的Bean定义,或添加ErrorAttributes类型的组件
以使用现有机制但替换其内容。自定义错误页
@ControllerAdvice+@ExceptionHandler处理全局异常;底层是 ExceptionHandlerExceptionResolver 支持的
@ResponseStatus+自定义异常 ;底层是 ResponseStatusExceptionResolver ,把responsestatus注解的信息底层调用 response.sendError(statusCode, resolvedReason);tomcat发送的/error
Spring底层的异常,如 参数类型转换异常;DefaultHandlerExceptionResolver 处理框架底层的异常。
ErrorMvcAutoConfiguration 自动配置异常处理规则
如果想要返回页面;就会找error视图【StaticView】。(默认是一个白页)
1、执行目标方法,目标方法运行期间有任何异常都会被catch、而且标志当前请求结束;并且用 dispatchException
2、进入视图解析流程(页面渲染?)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
3、mv = processHandlerException;处理handler发生的异常,处理完成返回ModelAndView;
1、遍历所有的 handlerExceptionResolvers,看谁能处理当前异常**[HandlerExceptionResolver处理器异常解析器]**
2、系统默认的 异常解析器;
1、DefaultErrorAttributes先来处理异常。把异常信息保存到rrequest域,并且返回null;
2、默认没有任何人能处理异常,所以异常会被抛出
// 处理 整个web controller的异常.
@ControllerAdvice
public class GlobalExceptionHandler {
// 处理异常,空指针异常,类未找到异常.
@ExceptionHandler({NullPointerException.class,ClassCastException.class})
public String myException(){
return "err";// 返回的是视图地址,也可以使用ModelAndView
}
}
@ServletComponentScan(basePackages = “com.atguigu.admin”) :指定原生Servlet组件的位置, 这个注解写在springboot主方法那边.
@WebServlet(urlPatterns = “/my”):效果:直接响应,没有经过Spring的拦截器?
@WebServlet(urlPatterns = "/my")
public class servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
}
@WebFilter(urlPatterns={"/css/*","/images/*"})
@WebListener
扩展:DispatchServlet 如何注册进来
容器中自动配置了 DispatcherServlet 属性绑定到 WebMvcProperties;对应的配置文件配置项是 spring.mvc。
通过 ServletRegistrationBean 把 DispatcherServlet 配置进来。
默认映射的是 / 路径。
ServletRegistrationBean, FilterRegistrationBean, and ServletListenerRegistrationBean
@Configuration
public class MyRegistConfig {
@Bean
public ServletRegistrationBean myServlet(){
MyServlet myServlet = new MyServlet();
return new ServletRegistrationBean(myServlet,"/my","/my02");
}
@Bean
public FilterRegistrationBean myFilter(){
MyFilter myFilter = new MyFilter();
// return new FilterRegistrationBean(myFilter,myServlet());
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
return filterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean myListener(){
MySwervletContextListener mySwervletContextListener = new MySwervletContextListener();
return new ServletListenerRegistrationBean(mySwervletContextListener);
}
}
默认支持的webServer
Tomcat
, Jetty
, or Undertow
ServletWebServerApplicationContext 容器启动寻找ServletWebServerFactory 并引导创建服务器
切换服务器
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
exclusion>
exclusions>
dependency>
实现 WebServerFactoryCustomizer
把配置文件的值和ServletWebServerFactory 进行绑定
修改配置文件 server.xxx
直接自定义 ConfigurableServletWebServerFactory
xxxxxCustomizer:定制化器,可以改变xxxx的默认规则
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000);
}
}
修改配置文件;
xxxxxCustomizer;
编写自定义的配置类 xxxConfiguration;+ @Bean替换、增加容器中默认组件;视图解析器
Web应用 编写一个配置类实现 WebMvcConfigurer 即可定制化web功能;+ @Bean给容器中再扩展一些组件
@Configuration
public class AdminWebConfig implements WebMvcConfigurer
@EnableWebMvc + WebMvcConfigurer —— @Bean 可以全面接管SpringMVC,所有规则全部自己重新配置; 实现定制和扩展功能
… …
场景starter - xxxxAutoConfiguration - 导入xxx组件 - 绑定xxxProperties – 绑定配置文件项
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jdbcartifactId>
dependency>
数据库要和驱动的版本对应.
默认版本:<mysql.version>8.0.22mysql.version>
想要修改版本
1、直接依赖引入具体版本(maven的就近依赖原则)
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.49version>
dependency>
2、重新声明版本(maven的属性的就近优先原则)
<properties>
<java.version>1.8java.version>
<mysql.version>5.1.49mysql.version>
properties>
自动配置的类
DataSourceAutoConfiguration : 数据源的自动配置
// 导入默认的数据源
@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
JndiDataSourceAutoConfiguration: jndi的自动配置
XADataSourceAutoConfiguration: 分布式事务相关的
spring:
datasource:
url: jdbc:mysql://localhost:3306/db_account
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
@Slf4j
@SpringBootTest
class Boot05WebAdminApplicationTests {
@Autowired
JdbcTemplate jdbcTemplate;
@Test
void contextLoads() {
// jdbcTemplate.queryForObject("select * from account_tbl")
// jdbcTemplate.queryForList("select * from account_tbl",)
Long aLong = jdbcTemplate.queryForObject("select count(*) from account_tbl", Long.class);
log.info("记录总数:{}",aLong);
}
}
https://github.com/alibaba/druid
整合第三方技术的两种方式
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.17version>
dependency>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxActive" value="20" />
<property name="initialSize" value="1" />
<property name="maxWait" value="60000" />
<property name="minIdle" value="1" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="20" />
StatViewServlet的用途包括:
提供监控信息展示的html页面
提供监控信息的JSON API
<servlet>
<servlet-name>DruidStatViewservlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>DruidStatViewservlet-name>
<url-pattern>/druid/*url-pattern>
servlet-mapping>
用于统计监控信息;如SQL监控、URI监控
需要给数据源中配置如下属性;可以允许多个filter,多个用,分割;如:
系统中所有filter:
别名 | Filter类名 |
---|---|
default | com.alibaba.druid.filter.stat.StatFilter |
stat | com.alibaba.druid.filter.stat.StatFilter |
mergeStat | com.alibaba.druid.filter.stat.MergeStatFilter |
encoding | com.alibaba.druid.filter.encoding.EncodingConvertFilter |
log4j | com.alibaba.druid.filter.logging.Log4jFilter |
log4j2 | com.alibaba.druid.filter.logging.Log4j2Filter |
slf4j | com.alibaba.druid.filter.logging.Slf4jLogFilter |
commonlogging | com.alibaba.druid.filter.logging.CommonsLogFilter |
慢SQL记录配置
<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter">
<property name="slowSqlMillis" value="10000" />
<property name="logSlowSql" value="true" />
bean>
使用 slowSqlMillis 定义慢SQL的时长
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.17version>
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/jdbc
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
druid:
aop-patterns: com.atguigu.admin.* #监控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
SpringBoot配置示例
https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
配置项列表https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8
https://github.com/mybatis
SpringBoot官方的Starter:spring-boot-starter-*
第三方的: *-spring-boot-starter
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.4version>
dependency>
全局配置文件
SqlSessionFactory: 自动配置好了
SqlSession:自动配置了 SqlSessionTemplate 组合了SqlSession
@Import(AutoConfiguredMapperScannerRegistrar.class);
Mapper: 只要我们写的操作MyBatis的接口标准了 @Mapper 就会被自动扫描进来
@EnableConfigurationProperties(MybatisProperties.class) : MyBatis配置项绑定类。
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration{}
@ConfigurationProperties(prefix = "mybatis")
public class MybatisProperties
可以修改配置文件中 mybatis 的所有配置;
# 配置mybatis规则
mybatis:
config-location: classpath:mybatis/mybatis-config.xml #全局配置文件位置
mapper-locations: classpath:mybatis/mapper/*.xml #sql映射文件位置
Mapper接口--->绑定Xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.admin.mapper.AccountMapper">
<select id="getAcct" resultType="com.atguigu.admin.bean.Account">
select * from account_tbl where id=#{id}
select>
mapper>
配置 private Configuration configuration; mybatis.configuration下面的所有,就是相当于改mybatis全局配置文件中的值
# 配置mybatis规则
mybatis:
# config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
configuration:
map-underscore-to-camel-case: true
可以不写全局;配置文件,所有全局配置文件的配置都放在configuration配置项中即可
导入mybatis官方starter
编写mapper接口。标准@Mapper注解
编写sql映射文件并绑定mapper接口
在application.yaml中指定Mapper配置文件的位置,以及指定全局配置文件的信息 (建议;配置在mybatis.configuration)
@Mapper
public interface CityMapper {
@Select("select * from city where id=#{id}")
public City getById(Long id);
public void insert(City city);
}
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
mybatis plus 官网
建议安装 MybatisX 插件
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.1version>
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能力
3、CRUD功能
@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";
}
@GetMapping("/dynamic_table")
public String dynamic_table(@RequestParam(value="pn",defaultValue = "1") Integer pn,Model model){
//表格内容的遍历
// response.sendError
// List users = Arrays.asList(new User("zhangsan", "123456"),
// new User("lisi", "123444"),
// new User("haha", "aaaaa"),
// new User("hehe ", "aaddd"));
// model.addAttribute("users",users);
//
// if(users.size()>3){
// throw new UserTooManyException();
// }
//从数据库中查出user表中的用户进行展示
//构造分页参数
Page<User> page = new Page<>(pn, 2);
//调用page进行分页
Page<User> userPage = userService.page(page, null);
// userPage.getRecords()
// userPage.getCurrent()
// userPage.getPages()
model.addAttribute("users",userPage);
return "table/dynamic_table";
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {
}
public interface UserService extends IService<User> {
}
2、NoSQL
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
perLocations 自动配置好的。有默认值。classpath*:/mapper/**/*.xml;任意包的类路径下的所有mapper文件夹下任意路径下的所有xml都是sql映射文件。 建议以后sql映射文件,放在 mapper下
●容器中也自动配置好了 SqlSessionTemplate
●@Mapper 标注的接口也会被自动扫描;建议直接 @MapperScan(“com.atguigu.admin.mapper”) 批量扫描就行
优点:
● 只需要我们的Mapper继承 BaseMapper 就可以拥有crud能力
3、CRUD功能
@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";
}
@GetMapping("/dynamic_table")
public String dynamic_table(@RequestParam(value="pn",defaultValue = "1") Integer pn,Model model){
//表格内容的遍历
// response.sendError
// List users = Arrays.asList(new User("zhangsan", "123456"),
// new User("lisi", "123444"),
// new User("haha", "aaaaa"),
// new User("hehe ", "aaddd"));
// model.addAttribute("users",users);
//
// if(users.size()>3){
// throw new UserTooManyException();
// }
//从数据库中查出user表中的用户进行展示
//构造分页参数
Page<User> page = new Page<>(pn, 2);
//调用page进行分页
Page<User> userPage = userService.page(page, null);
// userPage.getRecords()
// userPage.getCurrent()
// userPage.getPages()
model.addAttribute("users",userPage);
return "table/dynamic_table";
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {
}
public interface UserService extends IService<User> {
}
2、NoSQL
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>