SpringBoot2
SpringBoot2基础入门
Spring与SpringBoot
SpringBoot优点
-
Create stand-alone Spring application
- 创建独立Spring应用
-
Embed Tomcat, Jetty or Undertow directly(no need to deploy WAR files)
- 内嵌Web服务器
-
Provide opinionated ‘starter’ dependencies to simplify your build configuration
- 自动starter依赖,简化构建配置
-
Automatically configure Spring and 3rd party libraries whenever possible
- 自动配置Spring以及第三方功能
-
Provide production-ready features such as metrics, health checks, and externalized configuration
- 提供生产级别的监控、健康检查及外部化配置
-
Absolutely no code generation and no requirement for XML configuration
- 无代码生成、无需编写XML
SpringBoot是整合Spring技术栈的一站式框架、是简化Spring技术栈的快速开发
SpringBoot缺点
- 版本迭代快
- 封装深,內部原理复杂,不易精通
SpringBoot使用基础示例
maven依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.2</version>
</dependency>
@SpringBootApplication
open class MainApplication
fun main(args: Array<String>) {
//返回IOC容器
val runApplication = runApplication<MainApplication>(*args)
//查看容器中的组件
val names = runApplication.beanDefinitionNames
for (name in names) {
println(name)
}
}
//@ResponseBody //直接返回字符串,而非跳转页面
//@Controller
//@RestController = @ResponseBody + @Controller
@RestController
class HelloController {
@RequestMapping("/hello")
fun helloHandler(): String = "hello spring boot"
}
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplicationJava.class, args);
}
}
得到:Tomcat started on port(s): 8080 (http) with context path ‘’
可直接访问localhost:8080/hello显示hello spring boot
简化配置
所有属性配置均放入application.properties中
简化部署
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.2</version>
</plugin>
</plugins>
把项目打成jar包,直接在目标服务器执行
注意点
- 取消cmd的快速编辑模式
自动配置原理
SpringBoot特点
依赖管理
- 父项目做依赖管理
依赖管理
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/>
</parent>
其父项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.4.2</version>
</parent>
几乎声明了所有开发中常用的依赖的版本号,即为自动版本仲裁机制
- 开发导入starter场景启动器
1.见到很多spring-boot-starter-*,*就是某种场景
2.只要引入starter,这个场景的所有常规需要的依赖都会自动引入
3.见到的*-spring-boot-starter是第三方提供的场景启动器
4.所有场景启动器最底层的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>xxx</version>
<scope>compile</scope>
</dependency>
- 无需关注版本号,自动版本仲裁
1.引入依赖默认都可以不写版本号
2.引入非版本仲裁的jar需写版本号
- 可以修改版本号
1.查看spring-boot-dependencies里面规定当前依赖的版本用的key。
2.在当前项目中重写配置
<properties>
<mysql.version>xxx</mysql.version>
</properties>
自动配置
-
自动配置Tomcat
-
引入Tomcat依赖
-
配置Tomcat
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>xxx</version> <scope>compile</scope> </dependency>
-
-
自动配置SpringMVC
- 引入SpringMVC全套组件
- 自动配置SpringMVC常用组件(功能)
-
自动配置Web常见功能,如字符编码问题
- SpringBoot配置好所有web开发的常见场景
-
默认包结构
-
主程序所在包及其下所有子包中的组件都会被默认扫描进来
-
使用@SpringBootApplication(scanBasePackages = ["cn.kazunto"])将扫描范围放大 或@ComponentScan指定扫描路径 @SpringBootApplication等同于@SpringBootConfiguration、@EnableAutoConfiguration和ComponentScan("cn.kazunto")
-
-
各种配置拥有默认值
- 默认配置最终都是映射到MultipartProperties
- 配置文件的值最终会绑定到每个类上,类会在容器中创建对象
-
按需加载所有自动配置项
- 场景的自动配置只要被引入后才会开启
- SpringBoot所有的自动配置功能都在spring-boot-autoconfigure包里
-
…
a
容器功能
组件添加
@Configuration
-
基本使用
//1.这是一个配置类,等同于配置文件; //2.自身也是组件 //3.proxyBeanMethods:代理Bean的方法,默认为true // Full(proxyBeanMethods=true) 保持单实例 // Lite(proxyBeanMethods=false) // 组件依赖 @Configuration class Config { //给容器中添加组件,以方法名作为组件id;返回类型就是组件类型;返回的值就是组件在容器中的实例 @Bean("自定义名字") fun user(): User = User("kazunto", "24") } //从容器中获取组件 val kazunto1 = runApplication.getBean("user", User::class.java) val kazunto2 = runApplication.getBean("user", User::class.java) println(kazunto1) println(kazunto2) //User(id=null, name=kazunto, gender=24) //User(id=null, name=kazunto, gender=24)
-
Full模式与Lite模式
- 示例
- 实战
- 配置类组件之间无依赖管理用Lite模式加速容器启动过程,减少判断
- 配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式
@Bean、@Component、@Controller、@Service和@Repository
@ComponentScan和@Import
@Import({User.class, Config.class}) //java
@Import(User::class, Config::class) //kotlin
给容器中自动创建出指定类型的组件,默认组件的名字是全类名
@Conditional
条件装配:满足Conditional指定的条件,则进行组件注入
原生配置文件引入
@ImportResource
@ImportResource(“classpath:beans.xml”)导入Spring的配置文件
配置绑定
@ConfigurationProperties
application.properties配置文件
mycar.brand=HONDA
mycar.price=10000
bean类
//只要在容器中的组件,才有SpirngBoot提供的功能
@Component
@ConfigurationProperties(prefix="mycar")
class Car() {
var brand: String? = null
var price: Int? = null
}
config类
@EnableConfigurationProperties(Car::class.java)
//1.开启Car配置绑定功能
//2.把Car组件自动注册到容器中
//3.此时Car类上不需要@Component
class MyConfig {
}
@EnableConfigurationProperties + @ConfigurationProperties
@Component + @ConfigurationProperties
自动配置原理入门
引导加载自动配置类
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponenetScan(excludeFilters = {@Filter(type=FilterType.CUSTOM, classes=TypeExculdFilter.class), @Filter(type=FilterType.CUSTOM, classes=AutoConfigurationExcludeFilter.class)})
public @interface SpringBootApplication {}
@SpringBootConfiguration
@Configuration代表当前是一个配置类
@ComponentScan
指定扫描,Spring注解
@EnableAutoConfiguration
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.java)
public @interface EnableAutoConfiguration {}
@AutoConfigurationPackage
自动配置包,指定了默认的包规则
@Import(AutoConfigurationPackages.Registrar.class) //给容器中导入组件
@public @interface AutoConfigurationPackage {}
//利用Registrar给容器中导入一系列组件
@Import(AutoConfigurationImportSelector.class)
1.利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
2.调用List<String> configuration = getCandidateConfigurations(annotationMetadata, attributes)获取所有需要导入到容器中的配置类
3.利用工厂加载Map<string, List<string>> loadStringFactories(@Nullable ClassLoader classLoader);得到所有的组件
4.从META-INF/spring.factories位置来加载一个文件。
默认扫描当前系统里所有META-INF/spring.factoories位置的文件
spring-boot-autoconfigure-x.x.x.jar里也有META-INF/spring.factoories
spirng-boot中规定了给容器加载的所有配置类:
#Auto Configure
org.springframework.boot.autoconfigure.EnableAutofiguration=\
org.springframework.boot.autoconfigure.***Autofiguration=\
......
按需开启自动配置项
按照条件装配规则(@Conditional),最终会按需配置
修改默认配置
@Bean
@ConditionalOnBean(MultipartResolver.class) //容器中有该组件
@ConditionalOnMissingBean(name=DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中无该multipartResolver名字的组件
//给@Bean标注的方法传入对象参数,该参数值会从容器中寻找
//SpringMVC multipartResolver防止用户配置的文件上传解析器不符合规范
public MultipartResolver multipartResolver(MultipartReslver resolver) {
// Defect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
给容器中加入文件上传解析器
SpringBoot默认会在底层配置所有组件,如果用户进行配置,则以用户配置为准
总结:
-
SpringBoot先加载所有的自动配置类 xxxAutoConfiguration
-
每个自动配置类按照条件进行生效,默认绑定配置文件指定的值;从xxxProeprties中获取;xxxProperties和配置文件进行绑定
-
生效的配置类给容器中装配组件
-
容器中存在组件后,组件的功能生效
-
定制配置:
-
直接@Bean替换底层的组件
@Bean @ConditionalOnMissingBean public CharacterEncodingFilter characterEncodingFilter() {}
-
在配置文件中修改组件获取的参数值
application.properties server.servlet.encoding.charset=utf-8
xxxAutoConfiguration–>组件–>xxxProperties–>application.properties
-
最佳实践
-
引入场景依赖
- https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
-
查看自动配置哪些(选做)
- 配置文件中添加debug=true开启自动配置报告;Negative,不生效;Positive,生效
-
是否需要定制化
-
参照文档修改配置项
- https://docs.spring.io/spring-boot/docs/current/refence/html/appendix-application-properties.html#common-application-properrties
- 手动分析在xxxProperties绑定了哪些配置
-
自定义加入或替换组件
- @Bean、@Component…
-
自定义器 XxxCustomizer
-
…
-
开发小技巧
Lombok
简化JavaBean开发。自动生成get和set方法
-
配置依赖
pom.xml <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
-
安装插件
Settings–>Plugins–>Lombok
-
使用
@Data //Getter & Setter @ToString //ToString @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode public class Pet { private String name; } @Slf4j public class Log { public String write() { log.info("...log message..."); } }
dev-tools
-
配置依赖
pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
-
使用
Ctrl+F9,热更新,自动重启
Spring Initalizier
项目初始化向导
SpringBoot2核心技术-核心功能
配置文件、Web开发、数据访问、单元测试、指标监控、原理解析
配置文件
properties
yaml
简介
YAML是“YAML Ain’t Markup Language”(YAML不是一种标记语言)的递归缩写;在开发的这种语言时,意思是“Yet Another Markup Language”(仍是一种标记语言)。
非常适合用来做以数据为中心的配置文件。
基本语法
- key: value;key和value间有空格
- 大小写敏感
- 使用缩进表示层级关系
- 缩进不允许使用tab,只允许空格
- 所需的空格数不重要,只要相同层级的元素左对齐即可
- '#'表示注释
- ''与""表示字符串内容,会被转义/不转义
数据类型
-
字面量:单个的、不可再分的值。date、boolean、string、number、null
k: v
-
对象:键值对的集合。map、hash、set、object
#行内写法: k: {k1:v1,k2:v2,k3:v3} #或 k: k1: v1 k2: v2 k3: v3
-
数组:一组按次序排列的值。array、list、queue
#行内写法: k: [v1,v2,v3] #或 k: - v1 - v2 - v3
示例
class Person() {
var userName: String? = null
var boss: Boolean? = null
var birth: Date? = null
var age: Int? = null
var per: Pet? = null
var interests: Array<String>? = null
var animal: List<String>? = null
var score: Map<String, Any>? = null
var salarys: Set<Double>? = null
var allPets: Map<String, List<Pet>>? = null
}
class Pet() {
var name: String? = null
var weight: Double? = null
}
xxx.yaml || xxx.yml
person:
userName: kazunto
boss: true
birth: 1997/3/11
age: 24
#interests: [日语,那西隆语]
interests:
- 日语
- 那西隆语
animal: [ 中华田园猫,缅因猫 ]
# score:
# English: 70
# Math: 100
score: { English:70,Math:100 }
salarys:
- 2000
- 1968
pet:
name: mimi
weight: 20
allPets:
sick:
- { name: xie, weight: 10 }
- name: mimi
weight: 20
- name: ji
weight: 5
health:
- { name: hana, weight: 10 }
- { name: won, weight: 10 }
Web开发
深入Web原理
- SpringMVC自动配置概览
- 简单功能分析
- 请求参数处理
- 数据响应与内容协商
- 视图解析与模板引擎
- 拦截器
- 跨域
- 异常处理
- 原生Servlet组件
- 嵌入式Web容器
- 定制化原理
SpringMVC自动配置概览
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.
- 内容协商视图解析器和BeanName视图解析器。
- Support for serving static resources, including support for WebJars(coverd later in this document).
- 静态资源(包括webjars)
- Automatic registration or Converter, GenericConverter, and Formatter beans.
- 自动注册Converter,GenericConverter,Formatter
- Support for HttpMessageConverters(converd later in this document)
- 支持HttpMessageConverters(后来我们配合内容协商理解原则)
- Automatic registration of MessageCodesResolver (converd later in this document)
- 自动注册MessageCodesResolver (国际化用)
- Static index.html support
- 静态index.html页支持
- Custom Favicon support (converd later in this document)
- 自定义Favicon
- Automatic use of a ConfigurableWebBindingInitializer bean (converd 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 + WebMvcConfiguration 自定义规则
If you want to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, and still keep this Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations and use it to provide custom instanes of those components.
声明 WebMvcRegistrations 改变默认底层
简单功能分析
静态资源访问
静态资源目录
只要静态资源放在类路径:/static or /public or /resources or /META-INF/resources
访问:当前目录根路径/ + 静态资源名
原理:静态映射/**;请求先在Controller中处理,无法处理的请求交给静态资源处理器。
改变默认的静态资源路径:
spring:
web:
resources:
static-locations: [classpath:/file_path/,...]
#src/main/resources/file_path/file_name.xx
静态资源访问前缀
默认无前缀
spring:
mvc:
static-path-pattern: /res/**
当前项目 + static-path-pattern + 静态资源名 = 静态资源文件下寻找
webjars
将JQuery等打包
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>
访问地址:http://localhost:8080/webjars/jquery/3.5.1/jquery.js
欢迎页支持
-
静态资源路径下index.html
- 可以配置静态资源路径
- 不可以配置静态资源的访问前缀,否则导致indx.html不能被默认访问
spring: # mvc: # static-path-pattern: /res/** # 会导致welcome page功能失效 web: resources: static-locations: [classpath:/file_path/,...]
-
controller能处理/index
自定义Favicon
网页图标,将favicon.ico放入static即可。
配置静态资源的访问前缀也会使该功能失效。
请求参数处理
- SpringBoot启动默认加载,xxxAutoConfiguration类(自动配置类)
- SpringMVC功能的自动配置类WebMVCAutoConfiguration,生效
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PERCEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {}
- 配置文件的相关属性和xxx进行了绑定。WebMvcProperties == spring.mvc,ResourceProperties == spring.resources
配置类只要一个有参构造器
org.springframework.boot:spring-boot-autoconfigure/web/servlet/WebMvcAutoConfiguration //有参构造器所有参数的值都会从容器中确定 //ResourceProperties resourceProperties:获取和spring.resources绑定的所有的值的对象 //WebMvcProperties mvcProperties:获取和spring.mvc绑定的所有的值的对象 //ListableBeanFactory beanFactory:Spring的BeanFactory //HttpMessageConverters:找到所有的HttpMessageConverters //ResourceHandlerRegistrationCustomizer:找到资源处理器的自定义器 //DispatcherServletPath //ServletRegistrationBean:给应用注册Servlet、Filter public WebMvcAutoConfigurationAdapter(WebProperties webProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ObjectProvider<DispatcherServletPath> dispatcherServletPath, ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) { this.mvcProperties = mvcProperties; this.beanFactory = beanFactory; this.messageConvertersProvider = messageConvertersProvider; this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable(); this.dispatcherServletPath = dispatcherServletPath; this.servletRegistrations = servletRegistrations; this.mvcProperties.checkConfiguration(); }
资源处理的默认规则
org.springframework.boot:spring-boot-autoconfigure/web/servlet/WebMvcAutoConfiguration protected void addResourceHandlers(ResourceHandlerRegistry registry) { super.addResourceHandlers(registry); if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); } else { ServletContext servletContext = this.getServletContext(); this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/"); this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> { registration.addResourceLocations(this.resourceProperties.getStaticLocations()); if (servletContext != null) { registration.addResourceLocations(new Resource[]{new ServletContextResource(servletContext, "/")}); } }); } }
org.springframework.boot:spring-boot-autoconfigure/web/WebProperties/Resources private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"}; private String[] staticLocations;
请求映射
-
@xxxMapping;
-
Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
- 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
- 现在:/user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
- 核心Filter:HiddenHttpMethodFilter
- 用法:表单method=post,隐藏域_method=put
- SpringBoot中手动开启
//@RequestMapping(value=["/user"], method=[RequestMethod.GET]) @GetMapping("/user") fun getUser(): String = "GET-Kazunto" //@RequestMapping(value=["/user"], method=[RequestMethod.POST]) @PostMapping("/user") fun saveUser(): String = "POST-Kazunto" //@RequestMapping(value=["/user"], method=[RequestMethod.PUT]) @PutMapping("/user") fun putUser(): String = "PUT-Kazunto" //@RequestMapping(value=["/user"], method=[RequestMethod.DELETE]) @DeleteMapping("/user") fun deleteUser(): String = "DELETE-Kazunto"
<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="PUT"/> <input value="PUT" type="submit"/> </form> <form action="/user" method="post"> <input name="_method" type="hidden" value="DELETE"/> <input value="DELETE" type="submit"/> </form>
spring: mvc: hiddenmethod: filter: enabled: true
Rest原理(表单提交要使用RESST的时候)
- 表单提交会带上**_method=PUT**
- 请求被HiddenHttpMethodFilter拦截
- 请求是否正常,且是POST
- 获取到_method的值
- 兼容以下请求:PUT、DELETE、PATCH
- 原生request(post),包装模式requestWrapper重写了getMethod方法,返回的是传入的值
- 过滤器链放行时用wrapper,以后的方法调用getMethod是调用requestWrapper的方法
- 请求是否正常,且是POST
Rest使用客户端工具
- 如PostMain直接发送PUT、DELETE等方式请求,无需Filter
-
请求映射原理
普通参数与基本注解
-
注解:
@PathVariable(路径变量)、@RequestHeader(获取请求头)、@ModeAttribute、@RequestParam获取请求参数)、@MatrixVariable(矩阵变量)、@CookieValue(获取cookie值)、@RequestBody(获取请求头[POST])
@GetMapping("/car/{id}/owner/{username}") fun get(@PathVariable("id") id: Int, @PathVariable("username") name: String, @PathVariable pv: Map<String, String>, @RequestHeader("User-Agent") userAgent: String, @RequestHeader header: Map<String, String>, @RequestParam("age") age: Int, @RequestParam("inters") inters: List<String>, @RequestParam params: Map<String, String>, @CookieValue("Idea-a2c17425") _ga: String, @CookieValue("Idea-a2c17425") cookie:Cookie): Map<String, Any>? { val map = HashMap<String, Any>() // map.put("id", id) // map.put("name", name) // map.put("pv", pv) // map.put("userAgent", userAgent) // map.put("headers", header) map.put("age", age) map.put("inters", inters) map.put("params", params) map.put("Idea-a2c17425", _ga) println(cookie.name) println(cookie.value) return map } <a href="car/0/owner/Kazunto?age=24&lan=Здравствыйте&inters=game">car</a>
@PostMapping("/save") fun postMethod(@RequestBody content: String): Map<Any, Any>? { val map = HashMap<Any, Any>() map.put("content", content) return map } <form action="/save" method="post"> 用户名:<input name="username"/> 备注:<input name="other"/> <input type="submit" value="提交"/> </form>
@Controller class RequestController { @GetMapping("/goto") fun goToPage(request: HttpServletRequest): String { request.setAttribute("msg", "Здравствыйте") request.setAttribute("code", 1024) //转发到 /success请求 return "forward:/success" } @ResponseBody @GetMapping("/success") fun success(@RequestAttribute("msg") msg: String, @RequestAttribute("code") code: Int, request: HttpServletRequest): Map<Any, Any>? { val map = HashMap<Any, Any>() map.put("msg1", request.getAttribute("msg")) map.put("msg", msg) return map } }
矩阵变量需要在SpringBoot中手动开启,根据RFC3986规范,矩阵变量应当绑定在路径变量中。若有多个矩阵变量,应使用英文符号;进行分割;若一个矩阵变量有多个值,应使用英文符号,进行分割,或命名多个重复的key即可。
如:/cars/sell;low=34;brand=byd,audi,auto
session.set()–>jsessionid–>cookie–>每次发请求携带
url重写:/abc;jsessionid=xxxx 把cookie的值使用矩阵变量的方式进行传递
手动开启矩阵变量原理:对于路径的处理:UrlPathHelper进行解析。removeSemicolonContent(移除分号内容)支持矩阵变量
@Configuration(proxyBeanMethods = false) class WebConfig : WebMvcConfigurer { override fun configurePathMatch(configurer: PathMatchConfigurer) { val urlPathHelper = UrlPathHelper(); urlPathHelper.setRemoveSemicolonContent(false) configurer.setUrlPathHelper(urlPathHelper); } }
<a href="cars/sell;low=34;brand=honda,toyota,das auto">Matrix Variable First</a> <br/> <a href="cars/sell;low=34;brand=honda;brand=toyota;brand=das auto">Matrix Variable Second</a> <br/> <a href="boss/1;age=20/2;age=10">Matrix Variable Thirdly/boss/{bossId}/{empId}</a>
//cars/sell;low=34;brand=honda,toyota,das auto @GetMapping("/cars/{path}") fun carsSell(@MatrixVariable("low") low: Int, @MatrixVariable("brand") brand: List<String>, @PathVariable("path") path: String): Map<Any, Any>? { val map = HashMap<Any, Any>() map.put("low", low) map.put("brand", brand) map.put("path", path) return map } //boss/1;age=20/2;age=10 @GetMapping("/boss/{bossId}/{empId}") fun boss(@MatrixVariable(value = "age", pathVar = "bossId") bossAge: Int, @MatrixVariable(value = "age", pathVar = "empId") empAge: Int): Map<Any, Any>? { val map = HashMap<Any, Any>() map.put("bossAge", bossAge) map.put("empAge", empAge) return map }
-
Servlet API:
WebRequest、ServletRequest、MultipartRequest、HttpSession、Javax,servlet.http.PushBuilder、Principal、InputStrean、Reader、HttpMethod、Locale、TimeZone、ZoneId
-
复杂参数:
Map、Errors/BindingResult、Model、RedirectAttributes、ServletResponse、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder
-
自定义对象参数:
可以自动类型转换与格式化,可以级联封装
/* <input name="username"/><br/> <input name="age"/><br/> <input name="birthday"/><br/> <input name="pet.name"/><br/> <input name="pet.age"/><br/> */ @Date class Person() { var userName: String? = null var age: Int? = null var birth: Date? = null var pet: Pet? = null } @Date class Pet() { var name: String? = null var age: String? = null }
POJO封装过程
<form action="/saveuser" method="post">
姓名:<input name="userName"/><br/>
年龄:<input name="age"/><br/>
生日:<input name="birth"/><br/>
<!-- 宠物姓名:<input name="pet.name"/><br/>-->
<!-- 宠物年龄:<input name="pet.age"/><br/>-->
<input name="pet" value="neko,7"/><br/>
<input type="submit" value="提交"/>
</form>
WebConfig.kt
@Bean
fun webMvcConfigurer(): WebMvcConfigurer {
return object : WebMvcConfigurer {
override fun configurePathMatch(configurer: PathMatchConfigurer) {
val urlPathHelper = UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false)
configurer.setUrlPathHelper(urlPathHelper);
}
override fun addFormatters(registry: FormatterRegistry) {
registry.addConverter(Converter<String, Pet?> { source ->
if (!source.isEmpty()) {
val pet = Pet()
pet.name = source.split(",").toTypedArray()[0]
pet.age = source.split(",").toTypedArray()[1].toInt()
return@Converter pet
}
null
})
}
}
}
参数处理原理
-
HandlerMapping中找到能处理请求的Handler(Controller.method)
-
为当前Handler找一个适配器HandlerAdapter
HandlerAdapter:
-
RequestMappingHandlerAdapter
支持方法上标注@RequestMapping
-
HandkerFunctionAdapter
支持函数式编程
-
HttpRequestHandlerAdapter
-
SimpleControllerHandlelrAdapter
执行目标方法
//Actually invoke the handler //DispatcherServlet -- doDispatch mv = ha.handler(processedRequest, responese, mappedHandler.getHandler());
参数解析器
确定将要执行的目标方法每一个参数的值是什么
SpringMVC目标方法能写多少种参数类型,取决于参数解析器
-
HandlerMethodArgumentResolver
- resolveArgument
- supportsParameter
当前解析器是否支持该参数,如支持则调用resolveArgument
返回值处理器
解析该参数值:resolveArgument
自定义类型参数 封装POJO
ServletModelAttributeMethodProcessor
目标方法执行完成
将所有的数据都放在ModelAndViewContainer中,包含目的页面地址View、Model数据
处理派发结果
-
响应数据与内容协商
数据响应
- 响应页面
- 响应数据
- JSON
- XML
- xls
- 图片、视频
- 自定义协议数据
响应JSON
jackson.jar + @ResponseBody
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
web场景自动引入JSON场景
<dependecncy>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>xxx</version>
<scope>compile</scope>
</dependecncy>
给前端自动返回JSON数据
HTTPMessageConverter原理
内容协商
根据客户端接收能力不同,返回不同媒体类型的数据
-
引入XML依赖
<dependency> <groupId>org.springframework.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency>
-
postman分别测试返回JSON和XML
只需要改变请求头Accept字段。Http协议中规定的,告诉服务器可以接收的数据类型
-
开启浏览器参数方式内容协商功能
为方便内容协商,开启基于请求参数的内容协商功能。
spring: mvc: contentnegotiation: favor-parameter: true
http://localhost:8080/text/person?format=json
http://localhost:8080/text/person?format=xml
-
内容协商原理
- 判断当前响应头中我是否已有确定的媒体类型。MediaType
- 获取客户端(PostMan、浏览器)支持接收的内容类型(获取客户端Accept请求头字段)[application/xml]
- contentNegotiationManager内容协商管理器,默认使用基于请求头的策略
- HeaderContentNegotiationStrategy确定客户端可以接收的内容类型
- 遍历循环所有当前系统的MessageConverter
- 获得所有支持指定类型的Converter
- 客户端需要[application/xml],服务器能力
- 进行内容协商的最佳匹配
- 用 支持 将对象转为最佳匹配媒体类型的Converter,调用进行转化
自定义MessageConverter
实现多协议数据兼容。json、xml、x-xxx
@ReponseBody响应数据调用RequestResponseBodyMethodProcessor处理
- Processor处理方法返回值,通过MessageConverter处理
- 所有MessageConverter整体可以支持各种媒体类型数据的操作(读、写)
- 内容协商找到最终的messageConverter
自定义Converter
class KazuntoMessageConverter : HttpMessageConverter<Person> {
override fun canRead(clazz: Class<*>, mediaType: MediaType?): Boolean {
TODO("Not yet implemented")
}
override fun canWrite(clazz: Class<*>, mediaType: MediaType?): Boolean {
return clazz.isAssignableFrom(Person::class.java)
}
override fun getSupportedMediaTypes(): MutableList<MediaType> {
return MediaType.parseMediaTypes("application/x-kazunto")
}
override fun read(clazz: Class<out Person>, inputMessage: HttpInputMessage): Person {
TODO("Not yet implemented")
}
override fun write(t: Person, contentType: MediaType?, outputMessage: HttpOutputMessage) {
val data = "${t.userName};${t.age};${t.birth}"
val body = outputMessage.body
body.write(data.toByteArray())
}
}
自定义内容协商策略
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new KazuntoMessageConverter());
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
Map<String, MediaType> mediaType = new HashMap<>();
mediaType.put("json", MediaType.APPLICATION_JSON);
mediaType.put("xml", MediaType.APPLICATION_XML);
mediaType.put("kzt", MediaType.parseMediaType("application/x-kazunto"));
ParameterContentNegotiationStrategy parameterStrategy = new ParameterContentNegotiationStrategy(mediaType);
HeaderContentNegotiationStrategy headerStategy = new HeaderContentNegotiationStrategy();
configurer.strategies(Arrays.asList(parameterStrategy, headerStategy));
}
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
};
}
@Bean
fun webMvcConfigurer(): WebMvcConfigurer {
return object : WebMvcConfigurer {
override fun configurePathMatch(configurer: PathMatchConfigurer) {
val urlPathHelper = UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false)
configurer.setUrlPathHelper(urlPathHelper);
}
override fun addFormatters(registry: FormatterRegistry) {
registry.addConverter(Converter<String, Pet?> { source ->
if (!source.isEmpty()) {
val pet = Pet()
pet.name = source.split(",").toTypedArray()[0]
pet.age = source.split(",").toTypedArray()[1].toInt()
return@Converter pet
}
null
})
}
override fun extendMessageConverters(converters: MutableList<HttpMessageConverter<*>?>) {
converters.add(KazuntoMessageConverter())
}
override fun configureContentNegotiation(configurer: ContentNegotiationConfigurer){
val mediaType = HashMap<String, MediaType>()
mediaType["json"] = MediaType.APPLICATION_JSON
mediaType["xml"] = MediaType.APPLICATION_XML
mediaType["kzt"] = MediaType.parseMediaType("application/x-kazunto")
val parameterStrategy = ParameterContentNegotiationStrategy(mediaType)
val headerStategy = HeaderContentNegotiationStrategy()
configurer.strategies(listOf(parameterStrategy, headerStategy))
}
}
}
视图解析与模板引擎
视图解析:SpringBoot默认不支持JSP,需引入第三方模板引擎技术实现页面渲染。
视图解析
模板引擎-Thymeleaf
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
-
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模板引擎
页面须放在/templates/下,且为.html
-
基本语法
表达式名字 语法 用途 变量取值 ${…} 获取请求域、session域、对象等 选择变量 *{…} 获取上下文对象值 消息 #{…} 获取国际化等值 链接 @{…} 生成链接 片段表达式 ~{…} jsp:include作用,引入公共页面 字面量
文本值:’…’
数字
布尔值:true, false
空值:null
变量:one, …变量不能有空格
文本操作
字符串拼接:+
变量替换:|The name is ${name}|
数学运算
运算符:+ - * / %
布尔运算
运算符:and or
一元运算:!, not
比较运算
比较:> < >= <= (gt lt ge le)
等式:== != (eq ne)
条件运算
If-then:(if) ? (then) : (false)
If-then-else:(if) ? (then) : (else)
Default:(value) ?: (defaultValue)
特殊操作
无操作: _
-
设置属性值-th:attr
设置单个值
<form> <fieldset> <input type="text" name="email"/> <input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/> </fieldset> </form>
设置多个值
<img src="../../images/qwe.png" th:attr="src=@{/images/qwe.png},title=#{logo}"/>
以上代替写法th:xxx
<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/> <form action="subscribe.html" th:action="@{/subscribe}"> </form>
-
迭代
<tr class="gradeX" th:each="user,status:${userList}"> <td th:text="${status.count}">0</td> <td th:text="${user.username}">null</td> <td th:text="${user.password}">null</td> </tr>
@GetMapping("/dynamic_table") public String dynamicTable(Model model) { List<User> userList = Arrays.asList(new User("ka", "123456"), new User("ka2", "123456"), new User("ka3", "123456"), new User("ka4", "123456")); model.addAttribute("userList", userList); return "table/dynamic_table"; }
拦截器
HandlerInterceptor接口
preHandler 目标方法完成以前、postHandler 目标方法完成以后、afterCompletion 页面渲染以后
配置拦截器
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
if (loginUser != null)
return true;
request.setAttribute("msg", "请先登录");
// response.sendRedirect("/");
request.getRequestDispatcher("/").forward(request, response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**")
.excludePathPatterns("/", "/login", "/css/**", "/fonts/**", "/images/**", "/js/**");
}
}
验证拦截器
文件上传
页面表单
<div class="form-group">
<label for="exampleInputFile">头像</label>
<input type="file" name="headImage" id="exampleInputFile">
</div>
<div class="form-group">
<label for="exampleInputFile">生活照</label>
<input type="file" name="photos" multiple>
</div>
上传代码
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("username") String username,
@RequestPart("headImage") MultipartFile headImage,
@RequestPart("photos") MultipartFile[] photos) throws IOException {
//默认单文件不超过1MB,总大小不超过10MB,可在配置文件中修改
if (!headImage.isEmpty()) {
headImage.transferTo(new File("D:\\" + headImage.getOriginalFilename()));
}
if (photos.length > 0) {
for (MultipartFile photo : photos) {
if (!photo.isEmpty()) {
photo.transferTo(new File("D:\\" + photo.getOriginalFilename()));
}
}
}
return "index";
}
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=100MB
异常处理
错误处理
1、默认规则
- 默认情况下,SpringBoot提供/error处理所以的错误的映射
- 对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个"whitelabel"错误视图,以HTML格式呈现相同的数据
- 进行自定义:添加view解析为error
- 完全替换默认行为:实现ErrorController并注册该类型的Bean定义,或添加ErrorAttributes类型的组件以使用现有机制但替换其内容。
- templates/error/下的4xx,5xx页面会被自动解析
2、定制错误处理逻辑
- 自定义错误页
- error/404.html error/5xx.html
- @ControllerAdvice + @ExceptionHandler处理异常
- 实现HandlerExceptionResolver处理异常
3、异常处理原理
Web原生组件注入(Servlet、Filter、Listener)
1、使用Servlet API
//直接响应,不经过Spring拦截器
@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("6666666666");
}
}
@WebFilter(urlPatterns = {"/css/*", "/images/*"})
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
@Slf4j
@WebListener
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
log.info("监听到项目初始化完成");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
log.info("监听到项目销毁");
}
}
//主配置类
@ServletComponentScan(basePackages = "cn.kazunto.admin")
@SpringBootApplication
public class Boot05WebAdminApplication {
public static void main(String[] args) {
SpringApplication.run(Boot05WebAdminApplication.class, args);
}
}
2、使用RegistrationBean
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();
//只拦截myServlet中的路径,my my02
//return new FilterRegistrationBean(myFilter, myServlet());
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/my", "/css/*"));
return filterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean myListener() {
MyServletContextListener myServletContextListener = new MyServletContextListener();
return new ServletListenerRegistrationBean(myServletContextListener);
}
}
定制化原理
1、定制化的常见方式
-
@Bean替换、增加容器中默认组件;视图解析器
-
修改配置文件
-
xxxCustomizer
-
编写自定义的配置类 xxxConfiguration
-
web应用实现WebMvcConfigurer即可定制化Web功能
-
@EnableWebMvc + WebMvcConfigurer —— @Bean 可以全面接管SpringMVC,所以规则全部自己重新部署;实现定制和扩展功能
2、原理分析套路
场景starter - xxxAutoConfiguration - 导入xxx组件 - 绑定xxxProperties
数据访问
1、SQL
1、数据源的自动配置
-
导入JDBC场景
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> 默认版本8.0.22,SpringBoot已完成版本仲裁,无需手写版本信息。数据库版本应与驱动版本对应 <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
-
分析自动配置
- 自动配置的类
- DataSourceAutoConfiguration:数据源的自动配置
- 修改数据源相关的配置:spring.datasource
- 数据库连接池的配置,当容器中没有DataSource才自动配置
- 底层配置好的连接池是:HikariDataSource
- DataSourceTransactionManagerAutoConfiguration:事务管理的自动配置
- JdbcTemplateAutoConfiguration:JdbcTemplate的自动配置,可以对数据库进行crud(增删改查)
- 可以修改配置项@ConfigurationProperties(prefix=“spring.jdbc”)来修改JdbcTemplate
- @Bean @Primary JdbcTemplate;容器中该组件
- JndiDataSourceAutoConfiguration:jndi的自动配置
- XADataSourceAutoConfiguration:分布式事务相关的
- DataSourceAutoConfiguration:数据源的自动配置
- 自动配置的类
-
修改配置项
spring: datasource: url: jdbc:mysql://localhost:3306/tk?serverTimezone=Asia/Shanghai&userUnicode=true&characterEncoding=utf8&userSSL=true username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver jdbc: template: query-timeout: 3
2、使用Druid数据源
http://localhost:8080/druid/login.html
-
Druid官方github地址:https://github.com/alibaba/druid
整合第三方技术的两种方式:自定义、starter
-
自定义方式
- 创建数据源
-
starter
-
引入starter
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.17</version> </dependency>
-
分析自动配置
- 扩展配置项spring.datasource.druid
- DruidSpringAopConfiguration.class,监控SpringBoot的配置项: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/tk?serverTimezone=Asia/Shanghai&userUnicode=true&characterEncoding=utf8&userSSL=true username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver druid: filters: start,wall,slf4j #底层开启功能:stat(sql监控),wall(防火墙) aop-patterns: cn.kazunto.admin.* #监控SpringBean stat-view-servlet: #配置监控页功能 http://localhost:8080/druid/login.html enabled: true login-username: admin login-password: admin resetEnable: false web-stat-filter: #监控web enabled: true url-pattern: /* exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' filter: stat: slow-sql-millis: 1000 logSlowSql: true enabled: true wall: enabled: true config: drop-table-allow: false #禁止删表 jdbc: template: query-timeout: 3
-
3、整合MyBatis操作
https://github.com/mybatis
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
-
配置模式
-
全局配置文件
-
SqlSessionFactory:自动配置好
-
SqlSession:自动配置了SqlSessionTemplate组合了SqlSession
-
@Import(AutoConfiguredMapperScannerRegister.class)
-
Mapper:只要写的操作MyBatis的接口标注了@Mapper就会被自动扫描进来
@EnalbeConfigurationProperties(MyBatisProperties.class) //MyBatis配置项绑定类 @AutoConfigureAfter({DataSourceAutoConfiguration.class, MyBatisLanguageDriverAutoConfiguration.class}) public class MyBatisAutoConfiguration{} @ConfigurationProperties(prefix="mybatis") public class MyBatisProperties //通过修改配置文件中mybatis开头的
mybatis: config-location: classpath:mybatis/mybatis-config.xml mapper-locations: classpath:mybatis/mapper/*.xml
- 导入mybatis官方starter
- 编写mapper接口,标注@Mapper
- 编写sql映射文件并绑定mapper接口
- 在yml中指定mapper配置文件位置,和配置文件信息
-
-
注解模式
-
混合模式
4、整合MyBatis-Plus完成CRUD
需安装MyBatisX插件
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-jdbc</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.mybatis.spring.boot</groupId>-->
<!-- <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!-- <version>2.1.4</version>-->
<!-- </dependency>-->
<!--引入该内容后,不需要引入jdbc和mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
自动配置:
- MybatisPlusAutoConfiguration配置类,MybatisPlusProperties配置项绑定。mybatis-plus(yaml)。
- SqlSessionFactory:自动配置好
- mapperLoactions:自动配置好。默认为classpath*:/mapper/**/*.xml;任意包的类路径下所有mapper文件夹下任意路径下的所有xml。
- 自动配置了SqlSessionTemplate
- @Mapper标注的接口被自动扫描
- 只需要Mapper继承BaseMapper就拥有CRUD能力
2、NoSQL
Redis是开源的,内存中的数据结构存储系统,可以用作数据库、缓存和消息中间件。支持多种类型的数据结构:strings、hashes、lists、sets、sorted sets与范围查询、bitmaps、hyperloglogs和地理空间(geospatial)索引半径查询。Redis内置了复制(replication)、LUA脚本(Lua scripting)、LRU驱动事件(LRU eviction)、事务(transactions)和不同级别的磁盘持久化(persistence),并通过Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性。
1.Redis自动配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- RedisAutoConfiguration自动配置类。RedisProperties属性类–>spring.redis.xxx是对redis的配置
- 连接工厂是准备好的。LettuceConnectionConfiguration、JedisConnectionConfiguration
- 自动注入RedisTemplate<Object, Object>和StringRedisTemplate,前者key和value是Object类型,后者都是String类型
- 底层只要使用RedisTemplate和StringRedisTemplate就可以操作Redis
指标监控
1.SpringBoot Actuator
1.简介
每一个微服务在云上部署以后,都需要对其进行监控、追踪、审计和控制等。SpringBoot抽取了Actuator场景,使得每个微服务快速引用即可获得生产级别的应用监控和审计等功能。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2. 1.x和2.x的不同
SpringBoot Actuator 1.x | SpringBoot Actuator 2.x |
---|---|
支持SpringMVC | 支持SpringMVC、JAX-RS以及Webflux |
基于继承方式进行拓展 | 注解驱动进行拓展 |
层级Metrics配置 | 层级&名称空间Metrics |
自定义Metrics收集 | 底层使用MicroMeter,强大、便捷 |
默认较少的安全策略 | 默认丰富的安全策略 |
3.如何使用
- 引入场景
- 访问http://localhost:8080/actuator/health
- 暴露所有监控行为HTTP
#management是所有actuator的配置
management:
endpoints:
enabled-by-default: true #默认开启所有监控端点
web:
exposure:
include: '*' #以web方式暴露所有端点
endpoint:
health:
show-details: always
最常用的Endpoint:
- Health:健康状况
- Metrics:运行时指标
- Loggers:日志记录
2.Health Endpoint
健康检查端点,一般在使用云平台时会定期检查应用的健康状况,就通过Health Endpoint返回健康状况的集合。
- Health Endpoint返回的结果,是一系列健康检查后的汇总报告。
- 很多健康检查默认配置完毕,例如数据库和Redis等。
- 简单地添加自定义的健康检查机制。
3.Metrics Endpoint
提供详细的、层级的、空间指标信息,该信息可以被pull(主动推送)或push(被动获取)方式得到。
- 通过Metrics对接多种监控系统
- 简化核心Metrics开发
- 添加自定义Metrics或者扩展已有Metrics
4.管理Endpoints
1.开启与禁用Endpoints
- 默认所有的Endpoints除过shutdown都是开启的
2.定制info信息
-
编写配置文件
info: appName: Kazunto mavenProjectName: @project.artifactId@ #使用@@可以获取maven的pom文本值
-
编写Controller
@Controller public class KazuntoInfoContributor implements InfoContributor { @Override public void contribute(Info.Builder builder) { builder.withDetail(...); } }
5.定制Metrics信息
class KazuntoService {
Counter counter;
public KazuntoService(MeterRegistry meterRegistry) {
counter = meterRegistry.counter("kazuntoService.method.running.counter");
}
public void hello() {
counter.increment();
}
}
//或下面方式
@Bean
MeterBinder queueSize(Queue queue) {
return (registry) -> Gauge.builder("queueSize", queue::size).register(registry);
}
6.定制Endpoint
@Component
@Endpoint(id="container")
public class KazuntoEndpoint {
@ReadOperation
public Map getInfo() {
return Collection.singletonMap("", "");
}
@WriteOperation
private void restart() {
}
}
场景:开发ReadinessEndpoint管理程序是否就绪,或LivenessEndpoint管理程序是否存活;也可以直接使用:https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#produciton-ready-kubernetes-probes
7.可视化
https://github.com/codecentric/spring-boot-admin
-
新建项目(服务器):用于记录数据
<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-server</artifactId> <version>2.3.1</version> </dependency>
@Configuration @EnableAutoConfiguration @EnableAdminServer public class SpringBootAdminApplication { public static void main(String[] args) { SpringApplication.run(SpringBootAdminApplication.class, args); } }
server.port=8888
-
客户端:被记录数据
<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-security</artifactId> </dependency>
spring.boot.admin.client.url=http://localhost:8080 management.endpoints.web.exposure.include=*
或
spring: boot: admin: client: url: http://localhost:8888 instance: prefer-ip: true application: name: Kazunto management: endpoints: enabled-by-default: true web: exposure: include: '*'
Profile功能
为方便多环境适配,springboot简化了profile功能。
1. applicaiton-profile功能
-
默认配置文件 application.yaml 任何时候都会加载
-
指定环境配置文件 application-{env}.yaml
application.properties spring.profiles.active=prod #激活application-prod.properties或application-prod.yml
-
激活指定环境
-
配置文件激活
-
命令行激活
java -jar PROJECTNAME.jar --spring.profiles.active=prod2 --server.port=1234
- 修改配置文件的任意值,命令行优先
-
-
默认配置与环境配置同时生效
-
同名配置项,profile配置优先
2. @Profile条件装配功能
@Profile(“prod”)
className or functionName
决定类或方法在什么配置文件生效时启用
3、Profile分组
application.properties
spring.profiles.active=name
spring.profiles.group.name[0]=yaml1
spring.profiles.group.name[1]=yaml2