Spring官网
覆盖了:
web开发
数据访问
安全控制
分布式
消息服务
移动开发
批处理
......
基于Java8的一些新特性,如:接口默认实现。重新设计源码架构。
官方文档
优点:
nexus-aliyun
central
Nexus aliyun
http://maven.aliyun.com/nexus/content/groups/public
jdk-1.8
true
1.8
1.8
1.8
1.8
...
org.springframework.boot
spring-boot-starter-parent
2.3.4.RELEASE
...
@SpringBootApplication
public class ApplicationMain {
public static void main(String[] args) {
SpringApplication.run(ApplicationMain.class, args);
}
}
@RestController
public class HelloController {
@GetMapping("hello")
public String hello(){
return "Hello world !";
}
}
直接运行main方法
application.properties
server.port=8888
引入springboot打包插件
org.springframework.boot
spring-boot-maven-plugin
打成jar包并运行
1、springboot项目要声明父项目是spring-boot-starter-parent
org.springframework.boot
spring-boot-starter-parent
2.3.4.RELEASE
2、而spring-boot-starter-parent的父项目是spring-boot-dependencies
org.springframework.boot
spring-boot-dependencies
2.3.4.RELEASE
3、spring-boot-dependencies中声明了所有可能用到的依赖的版本号
4、springboot项目导入starter场景启动器
org.springframework.boot
spring-boot-starter-web
此处无需关注版本号,根据父项目版本号自动引入
5、可以修改默认版本号
5.1.43
org.springframework.boot
spring-boot-starter-web
声明父项目并配置此启动器后,springboot做了许多自动配置
@SpringBootApplication等同于:
------------------------------------
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.atguigu.boot")
1、@Configuration
此注解声明类为配置项类,类中声明的@Bean等都会注册成为Spring的bean
/**
* 1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
* 2、配置类本身也是组件
* 3、proxyBeanMethods:代理bean的方法
* Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
* Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
* 组件依赖必须使用Full模式默认。其他默认是否Lite模式
*/
@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {
@Bean
public Person person(){
return new Person("xiang", 18, pet());
}
@Bean
public Pet pet(){
return new Pet("cat");
}
}
示例:
@SpringBootApplication
public class ApplicationMain {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ApplicationMain.class, args);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinition : beanDefinitionNames) {
System.out.println(beanDefinition);
}
//通过@Bean注解添加的bean
Person person = applicationContext.getBean("person", Person.class);
Pet pet = applicationContext.getBean("pet", Pet.class);
System.out.println(person.getPet() == pet);
}
}
2、@Component、@Controller、@Service、@Repository
声明对象或类注册为bean
3、@Import
@Import导入其他配置类
@Configuration
public class MyImport {
@Bean
public Family family(){
return new Family();
}
}
@Import(MyImport.class)
@Configuration
public class MyConfig {
}
@SpringBootApplication
public class ApplicationMain {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ApplicationMain.class, args);
//通过@Import添加的bean
Family family = applicationContext.getBean(Family.class);
System.out.println(family);
}
}
4、@Conditional
@Conditional为注册bean添加了些条件限制
@ConditionalOnMissingBean(name = "personCondition")
@Configuration
public class MyConditionalConfig {
@Bean
public Person personCondition(){
return new Person("xiang", 23, null);
}
}
@SpringBootApplication
public class ApplicationMain {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ApplicationMain.class, args);
//通过@Configuration 和 @Conditional 注解添加的bean
Person personCondition = applicationContext.getBean("personCondition", Person.class);
System.out.println(personCondition);
}
}
@ImportResource
已有spring配置文件beans.xml, 若想将其配置注册到springboot中,则使用@ImportSource
beans.xml
@ImportResource("classpath:beans.xml")
@Configuration
public class MyImportResource {
}
@SpringBootApplication
public class ApplicationMain {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ApplicationMain.class, args);
//通过@ImportResource添加的bean
boolean person3 = applicationContext.containsBean("person3");
System.out.println("person3:" + person3);
}
}
@ConfigurationProperties
@ConfigurationPropertiesScan
若想为一个组件赋值并注册,则使用这两个注解,@ConfigurationProperties负责为组件赋值,@ConfigurationPropertiesScan则负责扫描包将其注册为spring组件
car.name=BYD
car.price=10000
@Data
@ConfigurationProperties(prefix = "car")
public class Car {
private String name;
private int price;
}
@Configuration
@ConfigurationPropertiesScan(basePackages = "com.xiang")
public class MyPropertiesConfig {
}
@SpringBootApplication
public class ApplicationMain {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ApplicationMain.class, args);
//通过@COnfigurationProperties注册的bean
String[] carNames = applicationContext.getBeanNamesForType(Car.class);
System.out.println(Arrays.toString(carNames));
}
}
@SpringBoootApplication 注解内容
@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 {}
1. @AutoConfigurationPackage
自动配置包,指定了默认的包规则。
@Import(AutoConfigurationPackages.Registrar.class) //给容器中导入一个组件
public @interface AutoConfigurationPackage {}
//利用Registrar给容器中导入一系列组件
//将指定的一个包下的所有组件导入进来?MainApplication 所在包下。
2. AutoCongigurationImportSelector
1、利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
2、调用List configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
3、利用工厂加载 Map> 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
// 文件里面写死了spring-boot一启动就要给容器中加载的所有配置类
// spring-boot-autoconfigure-2.3.4.RELEASE.jar/META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
# ...
虽然我们127个场景的所有自动配置启动的时候默认全部加载。
xxxxAutoConfiguration按照条件装配规则(@Conditional),最终会按需配置。
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
}
总结:
引入场景依赖: SpringBoot场景
参照文档修改配置项:配置项文档
简化JavaBean开发
org.projectlombok
lombok
idea中搜索安装lombok插件
===============================简化JavaBean开发===================================
@NoArgsConstructor
//@AllArgsConstructor
@Data
@ToString
@EqualsAndHashCode
public class User {
private String name;
private Integer age;
private Pet pet;
public User(String name,Integer age){
this.name = name;
this.age = age;
}
}
================================简化日志开发===================================
@Slf4j
@RestController
public class HelloController {
@RequestMapping("/hello")
public String handle01(@RequestParam("name") String name){
log.info("请求进来了....");
return "Hello, Spring Boot 2!"+"你好:"+name;
}
}
org.springframework.boot
spring-boot-devtools
true
项目或者页面修改以后:Ctrl+F9;实际上是重新启动。
要想真正热部署,还需要依赖Jrebel等插件。
1、选择我们需要的开发场景
2、自动依赖引入
3、自动创建项目结构
4、自动编写好主配置类
同之前properties用法
server.port=8088
YAML 是 "YAML Ain't Markup Language"(YAML 不是一种标记语言)的递归缩写。
在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)。
4.1.2.1 基本语法
4.1.2.2 数据类型
1、字面量:
单个的、不可再分的值。比如int,double,String
k: v
2、对象:键值对的集合。map,hash,object等
#行内写法
k: {k1: v1, k2: v2}
# 分行写法
k:
k1: v1
k2: v2
k3: v3
3、数组:一组数据,list、set、array
# 行内写法
k: [k1,k2,k3]
# 分行写法
k:
-k1
-k2
-k3
4.1.2.3 示例
@Data
@ToString
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private int age;
private String desc;
private List hobbies;
private Set addresses;
private List pets;
private Map> group;
}
@Data
@ToString
public class Pet {
private String name;
private double weight;
}
application.yml
##双引号允许转译,单引号禁用转译,不加引号等同于单引号
person:
name: xiang
age: 18
desc: 大家好 \n 我是希昂
hobbies: [打游戏,跑步]
addresses:
- 连云港
- 南京
- '江苏'
- "中国"
pets: [{name: 哈巴狗, weight: 23.33}]
group:
good:
- name: 哈巴狗
weight: 23.33
- name: 金毛
weight: 34.33
bad: [{name: 泰迪, weight: 3.33},{name: 博美, weight: 3.33}]
自定义的类和配置文件绑定一般没有提示。最好导入配置提示依赖。
org.springframework.boot
spring-boot-configuration-processor
true
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:
1、静态资源目录
静态资源目录默认为:【 "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" 】
即放在如下目录下的文件都会被认为是静态资源。
也可以通过如下配置项更改静态资源目录:
spring:
## 设置静态资源存放路径(只有这些路径下的资源才会被认为是静态资源)
web:
resources:
static-locations: ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"]
2、静态资源访问前缀
静态资源访问前缀默认为:/**
即所有请求在匹配不到控制器层路径时,都会来查询静态资源。
注意当路径匹配得到控制器层路径时,优先通过控制器层处理。
可以通过如下参数设置静态资源路径。
spring:
## 设置静态资源请求路径(只有这些请求才会被认为是静态资源请求)
mvc:
static-path-pattern: /static/**
3、webjar
webjar里的资源也可以当作静态资源访问。具体访问方式见上图。
其中webjar引入方式:
org.webjars.npm
jquery
3.6.0
webjar的目录结构:
4、自定义Favicon
自定义网站图标。
favicon.ico放在静态目录下即可。
Rest接口
SpringBoot符合REST风格,针对同一资源,增删改查分别对应如下的method
示例如下:
@RestController
public class UserController {
@GetMapping("/user")
public String getUser(){
return "getUser";
}
@PostMapping("/user")
public String postUser(){
return "postUser";
}
@PutMapping("/user")
public String putUser(){
return "putUser";
}
@DeleteMapping("/user")
public String deleteUser(){
return "deleteUser";
}
}
表单隐藏方法
表单提交时默认不支持DELETE和PUT方法,若想支持REST,则需要开启HiddenHttpMethodFilter
1.开启HiddenHttpMethodFilter
spring:
mvc:
hiddenmethod:
filter:
# 开启hidden方法处理
enabled: true
2.表单中添加隐藏参数_method
映射原理
1.注解
@PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue、@RequestBody
@RestController
@RequestMapping("/param")
public class ParamController {
@GetMapping("/pathVariable/{id}/{name}")
public Map pathTest(@PathVariable String id, @PathVariable String name,
@PathVariable Map map) {
HashMap result = new HashMap<>();
result.put("id", id);
result.put("name", name);
result.put("map", map);
return result;
}
@GetMapping("/requestParam")
public Map requestParam(@RequestParam String name,
@RequestParam Map map) {
HashMap result = new HashMap<>();
result.put("name", name);
result.put("map", map);
return result;
}
@GetMapping("/requestHeader")
public Map requestHeader(@RequestHeader("host") String host,
@RequestHeader Map map) {
HashMap result = new HashMap<>();
result.put("host", host);
result.put("map", map);
return result;
}
@GetMapping("/cookie")
public Map cookie(@CookieValue Cookie map) {
HashMap result = new HashMap<>();
result.put("map", map);
return result;
}
@PostMapping("/requestBody")
public Map requestBody(@RequestBody String content) {
HashMap result = new HashMap<>();
result.put("content", content);
return result;
}
}
2.自定义对象参数
可以自动类型转换与格式化,可以级联封装。
/**
* 姓名:
* 年龄:
* 生日:
* 宠物姓名:
* 宠物年龄:
*/
@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;
}
5.4.1.1、jackson.jar + @ResponseBody
web场景自动引入了json依赖
给前端自动返回json数据步骤:
1、返回解析器
try {
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
RequestResponseBodyMethodProcessor
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
// 使用消息转换器进行写出操作
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
2、返回解析器原理
5.4.1.2 SpringMVC支持哪些返回值
ModelAndView
Model
View
ResponseEntity
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
@ModelAttribute 且为对象类型的
@ResponseBody注解 ---> RequestResponseBodyMethodProcessor;
根据客户端接收能力不同,返回不同媒体类型的数据。
5.4.2.1 引入xml依赖
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
5.4.2.2 postman分别测试返回json和xml
只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。
5.4.2.3 开启浏览器参数方式内容协商功能
为了方便内容协商,开启基于请求参数的内容协商功能。
spring:
contentnegotiation:
favor-parameter: true #开启请求参数内容协商模式
发请求:
http://localhost:8080/test/person?format=json
http://localhost:8080/test/person?format=xml
确定客户端接收什么样的内容类型;
1、Parameter策略优先确定是要返回json数据(获取请求头中的format的值)
2、最终进行内容协商返回给客户端json即可。
5.4.2.4 内容协商原理
导入了jackson处理xml的包,xml的converter就会自动进来
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
if (jackson2XmlPresent) {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
}
5.4.2.5 自定义 MessageConverter
实现多协议数据兼容。json、xml
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void extendMessageConverters(List> converters) {
}
}
}
视图解析:SpringBoot默认不支持 JSP,需要引入第三方模板引擎技术实现页面渲染。
视图解析原理流程
1、thymeleaf简介
Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text.
现代化、服务端Java模板引擎
2、基本语法
1、表达式
表达式名字 |
语法 |
用途 |
变量取值 |
${...} |
获取请求域、session域、对象等值 |
选择变量 |
*{...} |
获取上下文对象值 |
消息 |
#{...} |
获取国际化等值 |
链接 |
@{...} |
生成链接 |
片段表达式 |
~{...} |
jsp:include 作用,引入公共页面片段 |
2、字面量
文本值: 'one text' , 'Another one!' ,…数字: 0 , 34 , 3.0 , 12.3 ,…布尔值: true , false
空值: null
变量: one,two,.... 变量不能有空格
3、文本操作
字符串拼接: +
变量替换: |The name is ${name}|
4、数学运算
运算符: + , - , * , / , %
5、布尔运算
运算符: and , or
一元运算: ! , not
6、比较运算
比较: > , < , >= ,
7、条件运算
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
8、特殊操作
无操作: _
3、设置属性值-th:attr
设置单个值
设置多个值
以上两个的代替写法 th:xxxx
所有h5兼容的标签写法
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#setting-value-to-specific-attributes
4、迭代
Onions
2.41
yes
Onions
2.41
yes
5、条件运算
view
User is an administrator
User is a manager
User is some other thing
6、属性优先级
1、引入Starter
org.springframework.boot
spring-boot-starter-thymeleaf
2、自动配置好了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
3、页面开发
Title
哈哈
去百度
去百度2
1、项目创建
thymeleaf、web-starter、devtools、lombok
2、静态资源处理
自动配置好,我们只需要把所有静态资源放到 static 文件夹下
3、路径构建
th:action="@{/login}"
4、模板抽取
th:insert/replace/include
5、页面跳转
@PostMapping("/login")
public String main(User user, HttpSession session, Model model){
if(StringUtils.hasLength(user.getUserName()) && "123456".equals(user.getPassword())){
//把登陆成功的用户保存起来
session.setAttribute("loginUser",user);
//登录成功重定向到main.html; 重定向防止表单重复提交
return "redirect:/main.html";
}else {
model.addAttribute("msg","账号密码错误");
//回到登录页面
return "login";
}
}
6、数据渲染
@GetMapping("/dynamic_table")
public String dynamic_table(Model model){
//表格内容的遍历
List users = Arrays.asList(new User("zhangsan", "123456"),
new User("lisi", "123444"),
new User("haha", "aaaaa"),
new User("hehe ", "aaddd"));
model.addAttribute("users",users);
return "table/dynamic_table";
}
1、HandlerInterceptor 接口
/**
* 登录检查
* 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","请先登录");
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);
}
}
2、配置拦截器
/**
* 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/**"); //放行的请求
}
}
3、拦截器原理
1、根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有 拦截器】
2、先来顺序执行 所有拦截器的 preHandle方法
3、如果任何一个拦截器返回false。直接跳出不执行目标方法
4、所有拦截器都返回True。执行目标方法
5、倒序执行所有拦截器的postHandle方法。
6、前面的步骤有任何异常都会直接倒序触发 afterCompletion
7、页面成功渲染完成以后,也会倒序触发 afterCompletion
1、页面表单
2、文件上传代码
/**
* MultipartFile 自动封装上传过来的文件
* @param email
* @param username
* @param headerImg
* @param photos
* @return
*/
@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";
}
3、自动配置原理
文件上传自动配置类-MultipartAutoConfiguration-MultipartProperties
FileCopyUtils。实现文件流的拷贝
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("username") String username,
@RequestPart("headerImg") MultipartFile headerImg,
@RequestPart("photos") MultipartFile[] photos)
1、默认规则
2、定制错误处理逻辑
3、异常处理步骤流程
@ServletComponentScan(basePackages = "com.atguigu.admin") : 指定原生Servlet组件都放在那里
@WebServlet(urlPatterns = "/my"):效果:直接响应,没有经过Spring的拦截器
@WebFilter(urlPatterns={"/css/*","/images/*"})
@WebListener
扩展:DispatchServlet 如何注册进来
容器中自动配置了 DispatcherServlet 属性绑定到 WebMvcProperties;对应的配置文件配置项是 spring.mvc。
通过 ServletRegistrationBean 把 DispatcherServlet 配置进来。
默认映射的是 / 路径。
Tomcat-Servlet;
多个Servlet都能处理到同一层路径,精确优选原则
A: /my/
B: /my/1
ServletRegistrationBean, FilterRegistrationBean, 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();
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);
}
}
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-tomcat
org.springframework.boot
spring-boot-starter-undertow
原理
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 {
@Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000);
}
}
场景starter - xxxxAutoConfiguration - 导入xxx组件 - 绑定xxxProperties -- 绑定配置文件项
1、导入JDBC场景
org.springframework.boot
spring-boot-starter-data-jdbc
mysql
mysql-connector-java
SpringBoot为什么不自动导入:
覆盖修改JDBC版本
-- 方式一:直接使用对应版本
mysql
mysql-connector-java
>5.1.49
-- 方式二:覆盖父类版本
5.1.49
mysql
mysql-connector-java
2、分析自动配置
自动配置的类:
3、修改配置项
spring:
datasource:
url: jdbc:mysql://localhost:3306/dbblog
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
4、测试
@Slf4j
@SpringBootTest
public class JdbcTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void test() {
Long aLong = jdbcTemplate.queryForObject("select count(1) from article", Long.class);
log.info("article总数:{}", aLong);
}
}
1、druid官方github地址
GitHub - alibaba/druid: 阿里云计算平台DataWorks(https://help.aliyun.com/document_detail/137663.html) 团队出品,为监控而生的数据库连接池
整合第三方技术的两种方式
2、自定义方式
引入依赖
@Slf4j
@SpringBootTest
public class JdbcTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void test() {
Long aLong = jdbcTemplate.queryForObject("select count(1) from article", Long.class);
log.info("article总数:{}", aLong);
}
}
创建数据源
配置StatViewServlet
配置StatFilter
配置慢SQL记录
@Configuration
public class MyConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setFilters("stat,wall");
return druidDataSource;
}
@Bean
public ServletRegistrationBean statViewServlet() {
StatViewServlet statViewServlet = new StatViewServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");
registrationBean.addInitParameter("loginUsername","admin");
registrationBean.addInitParameter("loginPassword","admin");
return registrationBean;
}
@Bean
public FilterRegistrationBean webStatFilter() {
WebStatFilter webStatFilter = new WebStatFilter();
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(webStatFilter);
filterRegistrationBean.setUrlPatterns(Collections.singletonList("/*"));
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
}
3、使用官方starter方式
引入druid-starter
com.alibaba
druid-spring-boot-starter
1.1.17
分析自动配置
private static final String FILTER_STAT_PREFIX = spring.datasource.druid.filter.stat;
private static final String FILTER_CONFIG_PREFIX = spring.datasource.druid.filter.config;
private static final String FILTER_ENCODING_PREFIX = spring.datasource.druid.filter.encoding;
private static final String FILTER_SLF4J_PREFIX = spring.datasource.druid.filter.slf4j;
private static final String FILTER_LOG4J_PREFIX = spring.datasource.druid.filter.log4j;
private static final String FILTER_LOG4J2_PREFIX = spring.datasource.druid.filter.log4j2;
private static final String FILTER_COMMONS_LOG_PREFIX = spring.datasource.druid.filter.commons-log;
private static final String FILTER_WALL_PREFIX = spring.datasource.druid.filter.wall;
4、配置示例
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/dbblog
username: root
password: 123456
# druid
druid:
filters: wall,stat
stat-view-servlet:
enabled: true
login-username: admin
login-password: admin
web-stat-filter:
enabled: true
url-pattern: /*
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
mybatis:
# config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
configuration:
map-underscore-to-camel-case: true
SpringBoot配置示例:druid/druid-spring-boot-starter at master · alibaba/druid · GitHub
配置项列表: https://github.com/alibaba/druid/wiki/DruidDataSource配置属性列表
官方地址:https://github.com/mybatis
引入starter
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.4
1、配置模式
@EnableConfigurationProperties(MybatisProperties.class) //MyBatis配置项绑定类。
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration{}
@ConfigurationProperties(prefix = mybatis)
public class MybatisProperties
Mapper接口--->绑定Xml
配置项值:mybatis
# 配置mybatis规则
mybatis:
config-location: classpath:mybatis/mybatis-config.xml #全局配置文件位置
mapper-locations: classpath:mybatis/mapper/*.xml #sql映射文件位置
配置 private Configuration configuration; mybatis.configuration下面的所有,就是相当于改mybatis全局配置文件中的值
# 配置mybatis规则
#可以不写全局;配置文件,所有全局配置文件的配置都放在configuration配置项中即可
mybatis:
# config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
configuration:
map-underscore-to-camel-case: true
2、注解模式
@Mapper
public interface CityMapper {
@Select("select * from city where id=#{id}")
public City getById(Long id);
public void insert(City city);
}
3、混合模式
@Mapper
public interface CityMapper {
@Select("select * from city where id=#{id}")
public City getById(Long id);
public void insert(City city);
}
1、是什么
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
mybatis plus 官网:https://baomidou.com/
建议安装 MybatisX 插件
2、怎么整合
com.baomidou
mybatis-plus-boot-starter
3.4.1
自动配置
优点:
3、CRUD示例
@Service
public class UserServiceImpl extends ServiceImpl implements UserService {
}
public interface UserService extends IService {
}
org.springframework.boot
spring-boot-starter-data-redis
自动配置:
@Test
void testRedis(){
ValueOperations operations = redisTemplate.opsForValue();
operations.set("hello","world");
String hello = operations.get("hello");
System.out.println(hello);
}
org.springframework.boot
spring-boot-starter-data-redis
redis.clients
jedis
spring:
redis:
host: localhost
port: 6379
client-type: jedis
jedis:
pool:
max-active: 10
Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库
作为最新版本的JUnit框架,JUnit5与之前版本的Junit框架有很大的不同。由三个不同子项目的几个不同模块组成。
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
注意:
SpringBoot 2.4 以上版本移除了默认对 Vintage 的依赖。如果需要兼容junit4需要自行引入(不能使用junit4的功能 @Test)
org.junit.vintage
junit-vintage-engine
test
org.hamcrest
hamcrest-core
org.springframework.boot
spring-boot-starter-test
test
现在示例代码:
@SpringBootTest
class Boot05WebAdminApplicationTests {
@Test
void contextLoads() {
}
}
以前:
@SpringBootTest + @RunWith(SpringTest.class)
SpringBoot整合Junit以后:
JUnit5的注解与JUnit4的注解有所变化
JUnit 5 User Guide
普通Test
@Slf4j
public class MySimpleTest {
@BeforeAll
static void beforeAll(){
log.info("beforeAll execute...");
}
@BeforeEach
public void beforeEach(){
log.info("beforeEach execute...");
}
@DisplayName("simpleTest")
@Test
public void test(){
log.info("test!");
}
@DisplayName("simpleTest2")
@Test
public void test2(){
log.info("test2!");
}
@Timeout(value = 5, unit = TimeUnit.MILLISECONDS)
@Test
public void test3() throws InterruptedException {
Thread.sleep(100);
log.info("test3!");
}
@AfterEach
public void afterEach(){
log.info("afterEach execute...");
}
@AfterAll
static void afterAll(){
log.info("afterAll execute...");
}
}
SpringBootTest
@Slf4j
@SpringBootTest
class MainApplicationTests {
@Autowired
StringRedisTemplate redisTemplate;
@Test
public void testRedis(){
ValueOperations operations = redisTemplate.opsForValue();
operations.set("name1","test");
log.info("names:{}", operations.get("name1"));
}
}
断言(assertions)是测试方法中的核心部分,用来对测试需要满足的条件进行验证。这些断言方法都是 org.junit.jupiter.api.Assertions 的静态方法。
对单个值进行简单判断
方法 |
说明 |
assertEquals |
判断两个对象或两个原始类型是否相等 |
assertNotEquals |
判断两个对象或两个原始类型是否不相等 |
assertSame |
判断两个对象引用是否指向同一个对象 |
assertNotSame |
判断两个对象引用是否指向不同的对象 |
assertTrue |
判断给定的布尔值是否为 true |
assertFalse |
判断给定的布尔值是否为 false |
assertNull |
判断给定的对象引用是否为 null |
assertNotNull |
判断给定的对象引用是否不为 null |
简单判断
@Test
public void assertEqualsTest() {
int sum = sum(5, 11);
Assertions.assertEquals(16, sum, sum + "不等于" + 12);
sum = sum(12, 13);
Assertions.assertEquals(12, sum, sum + "不等于" + 12);
}
通过 assertArrayEquals 方法来判断两个对象或原始类型的数组是否相等
@Test
@DisplayName("array assertion")
public void array() {
assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}
assertAll 方法接受多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易的提供这些断言
@Test
public void assertAllTest() {
Assertions.assertAll("test",
() -> Assertions.assertEquals(2, 1 + 1),
() -> Assertions.assertEquals(4, 2 + 2));
log.info("assert success");
}
在JUnit4时期,想要测试方法的异常情况时,需要用@Rule注解的ExpectedException变量还是比较麻烦的。而JUnit5提供了一种新的断言方式Assertions.assertThrows() ,配合函数式编程就可以进行使用。
@Test
@DisplayName("异常测试")
public void exceptionTest() {
ArithmeticException exception = Assertions.assertThrows(ArithmeticException.class, () -> System.out.println(1 % 0));
}
Junit5还提供了Assertions.assertTimeout() 为测试方法设置了超时时间
@Test
@DisplayName("超时测试")
public void timeoutTest() {
//如果测试方法时间超过1s将会异常
Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}
通过 fail 方法直接使得测试失败
@Test
@DisplayName("fail")
public void shouldFail() {
fail("This should fail");
}
JUnit 5 中的前置条件(assumptions【假设】)类似于断言,不同之处在于不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止。前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。
@Test
public void test(){
int a = sum(5,5);
int b = sum(1,2);
Assumptions.assumeTrue(a == 10);
Assumptions.assumeTrue(b == 4);
log.info("success");
}
assumeTrue 和 assumFalse 确保给定的条件为 true 或 false,不满足条件会使得测试执行终止。
assumingThat 的参数是表示条件的布尔值和对应的 Executable 接口的实现对象。只有条件满足时,Executable 对象才会被执行;当条件不满足时,测试执行并不会终止。
JUnit 5 可以通过 Java 中的内部类和@Nested 注解实现嵌套测试,从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用@BeforeEach 和@AfterEach 注解,而且嵌套的层次没有限制。
public class AStackTest {
Stack
参数化测试是JUnit5很重要的一个新特性,它使得用不同的参数多次运行测试成为了可能,也为我们的单元测试带来许多便利。
利用@ValueSource等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。
@ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
@NullSource: 表示为参数化测试提供一个null的入参
@EnumSource: 表示为参数化测试提供一个枚举入参
@CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参
@MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)
@Slf4j
public class ParamsTest {
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4})
public void testParameterized(int i) {
log.info("param:{}", i);
}
@ParameterizedTest
@MethodSource("strings")
public void testParameterized2(String str) {
log.info("param:{}", str);
}
static Stream strings() {
return Stream.of("apple", "banana");
}
}
在进行迁移的时候需要注意如下的变化:
未来每一个微服务在云上部署以后,我们都需要对其进行监控、追踪、审计、控制等。SpringBoot就抽取了Actuator场景,使得我们每个微服务快速引用即可获得生产级别的应用监控、审计等功能。
org.springframework.boot
spring-boot-starter-actuator
management:
endpoints:
enabled-by-default: true #暴露所有端点信息
web:
exposure:
include: '*' #以web方式暴露
测试
http://localhost:8080/actuator/beans
http://localhost:8080/actuator/configprops
http://localhost:8080/actuator/metrics
https://github.com/codecentric/spring-boot-admin
ID |
描述 |
auditevents |
暴露当前应用程序的审核事件信息。需要一个AuditEventRepository组件。 |
beans |
显示应用程序中所有Spring Bean的完整列表。 |
caches |
暴露可用的缓存。 |
conditions |
显示自动配置的所有条件信息,包括匹配或不匹配的原因。 |
configprops |
显示所有@ConfigurationProperties。 |
env |
暴露Spring的属性ConfigurableEnvironment |
flyway |
显示已应用的所有Flyway数据库迁移。需要一个或多个Flyway组件。 |
health |
显示应用程序运行状况信息。 |
httptrace |
显示HTTP跟踪信息(默认情况下,最近100个HTTP请求-响应)。需要一个HttpTraceRepository组件。 |
info |
显示应用程序信息。 |
integrationgraph |
显示Spring integrationgraph 。需要依赖spring-integration-core。 |
loggers |
显示和修改应用程序中日志的配置。 |
liquibase |
显示已应用的所有Liquibase数据库迁移。需要一个或多个Liquibase组件。 |
metrics |
显示当前应用程序的“指标”信息。 |
mappings |
显示所有@RequestMapping路径列表。 |
scheduledtasks |
显示应用程序中的计划任务。 |
sessions |
允许从Spring Session支持的会话存储中检索和删除用户会话。需要使用Spring Session的基于Servlet的Web应用程序。 |
shutdown |
使应用程序正常关闭。默认禁用。 |
startup |
显示由ApplicationStartup收集的启动步骤数据。需要使用SpringApplication进行配置BufferingApplicationStartup。 |
threaddump |
执行线程转储。 |
如果您的应用程序是Web应用程序(Spring MVC,Spring WebFlux或Jersey),则可以使用以下附加端点:
ID |
描述 |
heapdump |
返回hprof堆转储文件。 |
jolokia |
通过HTTP暴露JMX bean(需要引入Jolokia,不适用于WebFlux)。需要引入依赖jolokia-core。 |
logfile |
返回日志文件的内容(如果已设置logging.file.name或logging.file.path属性)。支持使用HTTPRange标头来检索部分日志文件的内容。 |
prometheus |
以Prometheus服务器可以抓取的格式公开指标。需要依赖micrometer-registry-prometheus。 |
最常用的Endpoint
健康检查端点,我们一般用于在云平台,平台会定时的检查应用的健康状况,我们就需要Health Endpoint可以为平台返回当前应用的一系列组件健康状况的集合。
重要的几点:
提供详细的、层级的、空间指标信息,这些信息可以被pull(主动推送)或者push(被动获取)方式得到;
1、开启与禁用Endpoints
management:
endpoint:
beans:
enabled: true
management:
endpoints:
enabled-by-default: false
endpoint:
beans:
enabled: true
health:
enabled: true
2、暴露Endpoints
支持的暴露方式
ID |
JMX |
Web |
auditevents |
Yes |
No |
beans |
Yes |
No |
caches |
Yes |
No |
conditions |
Yes |
No |
configprops |
Yes |
No |
env |
Yes |
No |
flyway |
Yes |
No |
health |
Yes |
Yes |
heapdump |
N/A |
No |
httptrace |
Yes |
No |
info |
Yes |
Yes |
integrationgraph |
Yes |
No |
jolokia |
N/A |
No |
logfile |
N/A |
No |
loggers |
Yes |
No |
liquibase |
Yes |
No |
metrics |
Yes |
No |
mappings |
Yes |
No |
prometheus |
N/A |
No |
scheduledtasks |
Yes |
No |
sessions |
Yes |
No |
shutdown |
Yes |
No |
startup |
Yes |
No |
threaddump |
Yes |
No |
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class MyHealthIndicator implements HealthIndicator {
@Override
public Health health() {
int errorCode = check(); // perform some specific health check
if (errorCode != 0) {
return Health.down().withDetail("Error Code", errorCode).build();
}
return Health.up().build();
}
}
//构建Health
Health build = Health.down()
.withDetail("msg", "error service")
.withDetail("code", "500")
.withException(new RuntimeException())
.build();
management:
health:
enabled: true
show-details: always #总是显示详细信息。可显示每个模块的状态信息
@Component
public class MyComHealthIndicator extends AbstractHealthIndicator {
/**
* 真实的检查方法
* @param builder
* @throws Exception
*/
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
//mongodb。 获取连接进行测试
Map map = new HashMap<>();
// 检查完成
if(1 == 2){
// builder.up(); //健康
builder.status(Status.UP);
map.put("count",1);
map.put("ms",100);
}else {
// builder.down();
builder.status(Status.OUT_OF_SERVICE);
map.put("err","连接超时");
map.put("ms",3000);
}
builder.withDetail("code",100)
.withDetails(map);
}
}
常用两种方式
1、编写配置文件
info:
appName: boot-admin
version: 2.0.1
mavenProjectName: @project.artifactId@ #使用@@可以获取maven的pom文件值
mavenProjectVersion: @project.version@
2、编写InfoContributor
import java.util.Collections;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;
@Component
public class ExampleInfoContributor implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
builder.withDetail("example",
Collections.singletonMap("key", "value"));
}
}
http://localhost:8080/actuator/info 会输出以上方式返回的所有info信息
1、SpringBoot支持自动适配的Metrics
2、增加定制Metrics
class MyService{
Counter counter;
public MyService(MeterRegistry meterRegistry){
counter = meterRegistry.counter("myservice.method.running.counter");
}
public void hello() {
counter.increment();
}
}
//也可以使用下面的方式
@Bean
MeterBinder queueSize(Queue queue) {
return (registry) -> Gauge.builder("queueSize", queue::size).register(registry);
}
@Component
@Endpoint(id = "container")
public class DockerEndpoint {
@ReadOperation
public Map getDockerInfo(){
return Collections.singletonMap("info","docker started...");
}
@WriteOperation
private void restartDocker(){
System.out.println("docker restarted....");
}
}
场景:开发ReadinessEndpoint来管理程序是否就绪,或者LivenessEndpoint来管理程序是否存活;
当然,这个也可以直接使用 Production-ready Features
为了方便多环境适配,SpringBoot简化了profile功能
9.1.1 application-profile 功能
java -jar boot-04-features-01-0.0.1-SNAPSHOT.jar --spring.profile
s.active=dev --person.name=王五
@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {
// ...
}
spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq
使用:--spring.profiles.active=production 激活
Core Features
常用:Java属性文件、YAML文件、环境变量、命令行参数;
(1) classpath 根路径
(2) classpath 根路径下config目录
(3) jar包当前目录
(4) jar包当前目录的config目录
(5) /config子目录的直接子目录
starter-pom引入 autoconfigurer 包
引入starter --- xxxAutoConfiguration --- 容器中放入组件 ---- 绑定xxxProperties ---- 配置项
atguigu-hello-spring-boot-starter(启动器)
atguigu-hello-spring-boot-starter-autoconfigure(自动配置包)
Spring原理【Spring注解】、SpringMVC原理、自动配置原理、SpringBoot原理
Core Features
ApplicationContextInitializer
ApplicationListener
SpringApplicationRunListener