Create stand-alone Spring application
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技术栈的一站式框架、是简化Spring技术栈的快速开发
maven依赖:
org.springframework.boot
spring-boot-starter-parent
2.4.2
org.springframework.boot
spring-boot-starter-web
2.4.2
@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中
org.springframework.boot
spring-boot-maven-plugin
2.4.2
把项目打成jar包,直接在目标服务器执行
依赖管理
org.springframework.boot
spring-boot-starter-parent
2.4.2
其父项目
org.springframework.boot
spring-boot-dependencies
2.4.2
几乎声明了所有开发中常用的依赖的版本号,即为自动版本仲裁机制
1.见到很多spring-boot-starter-*,*就是某种场景
2.只要引入starter,这个场景的所有常规需要的依赖都会自动引入
3.见到的*-spring-boot-starter是第三方提供的场景启动器
4.所有场景启动器最底层的依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<version>xxxversion>
<scope>compilescope>
dependency>
1.引入依赖默认都可以不写版本号
2.引入非版本仲裁的jar需写版本号
1.查看spring-boot-dependencies里面规定当前依赖的版本用的key。
2.在当前项目中重写配置
<properties>
<mysql.version>xxxmysql.version>
properties>
自动配置Tomcat
引入Tomcat依赖
配置Tomcat
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
<version>xxxversion>
<scope>compilescope>
dependency>
自动配置SpringMVC
自动配置Web常见功能,如字符编码问题
默认包结构
主程序所在包及其下所有子包中的组件都会被默认扫描进来
使用@SpringBootApplication(scanBasePackages = ["cn.kazunto"])将扫描范围放大
或@ComponentScan指定扫描路径
@SpringBootApplication等同于@SpringBootConfiguration、@EnableAutoConfiguration和ComponentScan("cn.kazunto")
各种配置拥有默认值
按需加载所有自动配置项
…
a
基本使用
//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模式
@Import({User.class, Config.class}) //java
@Import(User::class, Config::class) //kotlin
给容器中自动创建出指定类型的组件,默认组件的名字是全类名
条件装配:满足Conditional指定的条件,则进行组件注入
@ImportResource(“classpath:beans.xml”)导入Spring的配置文件
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 {
}
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponenetScan(excludeFilters = {@Filter(type=FilterType.CUSTOM, classes=TypeExculdFilter.class), @Filter(type=FilterType.CUSTOM, classes=AutoConfigurationExcludeFilter.class)})
public @interface SpringBootApplication {}
@Configuration代表当前是一个配置类
指定扫描,Spring注解
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.java)
public @interface EnableAutoConfiguration {}
自动配置包,指定了默认的包规则
@Import(AutoConfigurationPackages.Registrar.class) //给容器中导入组件
@public @interface AutoConfigurationPackage {}
//利用Registrar给容器中导入一系列组件
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
引入场景依赖
查看自动配置哪些(选做)
是否需要定制化
参照文档修改配置项
自定义加入或替换组件
自定义器 XxxCustomizer
…
简化JavaBean开发。自动生成get和set方法
配置依赖
pom.xml
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
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...");
}
}
配置依赖
pom.xml
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
使用
Ctrl+F9,热更新,自动重启
项目初始化向导
配置文件、Web开发、数据访问、单元测试、指标监控、原理解析
YAML是“YAML Ain’t Markup Language”(YAML不是一种标记语言)的递归缩写;在开发的这种语言时,意思是“Yet Another Markup Language”(仍是一种标记语言)。
非常适合用来做以数据为中心的配置文件。
字面量:单个的、不可再分的值。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 }
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:
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 + 静态资源名 = 静态资源文件下寻找
将JQuery等打包
<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功能失效
web:
resources:
static-locations: [classpath:/file_path/,...]
controller能处理/index
网页图标,将favicon.ico放入static即可。
配置静态资源的访问前缀也会使该功能失效。
@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 {}
配置类只要一个有参构造器
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请求方式动词来表示对资源的操作)
//@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的时候)
Rest使用客户端工具
请求映射原理
注解:
@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 Firsta>
<br/>
<a href="cars/sell;low=34;brand=honda;brand=toyota;brand=das auto">Matrix Variable Seconda>
<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
自定义对象参数:
可以自动类型转换与格式化,可以级联封装
/*
*/
@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
}
<form action="/saveuser" method="post">
姓名:<input name="userName"/><br/>
年龄:<input name="age"/><br/>
生日:<input name="birth"/><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
返回值处理器
解析该参数值:resolveArgument
自定义类型参数 封装POJO
ServletModelAttributeMethodProcessor
目标方法执行完成
将所有的数据都放在ModelAndViewContainer中,包含目的页面地址View、Model数据
处理派发结果
数据响应
jackson.jar + @ResponseBody
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
web场景自动引入JSON场景
<dependecncy>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jsonartifactId>
<version>xxxversion>
<scope>compilescope>
dependecncy>
给前端自动返回JSON数据
HTTPMessageConverter原理
根据客户端接收能力不同,返回不同媒体类型的数据
引入XML依赖
<dependency>
<groupId>org.springframework.dataformatgroupId>
<artifactId>jackson-dataformat-xmlartifactId>
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
内容协商原理
实现多协议数据兼容。json、xml、x-xxx
@ReponseBody响应数据调用RequestResponseBodyMethodProcessor处理
自定义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,需引入第三方模板引擎技术实现页面渲染。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
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}">0td>
<td th:text="${user.username}">nulltd>
<td th:text="${user.password}">nulltd>
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";
}
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、默认规则
2、定制错误处理逻辑
3、异常处理原理
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);
}
}
@Bean替换、增加容器中默认组件;视图解析器
修改配置文件
xxxCustomizer
编写自定义的配置类 xxxConfiguration
web应用实现WebMvcConfigurer即可定制化Web功能
@EnableWebMvc + WebMvcConfigurer —— @Bean 可以全面接管SpringMVC,所以规则全部自己重新部署;实现定制和扩展功能
场景starter - xxxAutoConfiguration - 导入xxx组件 - 绑定xxxProperties
1、数据源的自动配置
导入JDBC场景
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jdbcartifactId>
dependency>
默认版本8.0.22,SpringBoot已完成版本仲裁,无需手写版本信息。数据库版本应与驱动版本对应
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
分析自动配置
修改配置项
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.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.17version>
dependency>
分析自动配置
配置示例
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.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.4version>
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
注解模式
混合模式
4、整合MyBatis-Plus完成CRUD
需安装MyBatisX插件
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.1version>
dependency>
自动配置:
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自动配置
org.springframework.boot
spring-boot-starter-data-redis
每一个微服务在云上部署以后,都需要对其进行监控、追踪、审计和控制等。SpringBoot抽取了Actuator场景,使得每个微服务快速引用即可获得生产级别的应用监控和审计等功能。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
SpringBoot Actuator 1.x | SpringBoot Actuator 2.x |
---|---|
支持SpringMVC | 支持SpringMVC、JAX-RS以及Webflux |
基于继承方式进行拓展 | 注解驱动进行拓展 |
层级Metrics配置 | 层级&名称空间Metrics |
自定义Metrics收集 | 底层使用MicroMeter,强大、便捷 |
默认较少的安全策略 | 默认丰富的安全策略 |
#management是所有actuator的配置
management:
endpoints:
enabled-by-default: true #默认开启所有监控端点
web:
exposure:
include: '*' #以web方式暴露所有端点
endpoint:
health:
show-details: always
最常用的Endpoint:
健康检查端点,一般在使用云平台时会定期检查应用的健康状况,就通过Health Endpoint返回健康状况的集合。
提供详细的、层级的、空间指标信息,该信息可以被pull(主动推送)或push(被动获取)方式得到。
编写配置文件
info:
appName: Kazunto
mavenProjectName: @project.artifactId@ #使用@@可以获取maven的pom文本值
编写Controller
@Controller
public class KazuntoInfoContributor implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
builder.withDetail(...);
}
}
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);
}
@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
https://github.com/codecentric/spring-boot-admin
新建项目(服务器):用于记录数据
<dependency>
<groupId>de.codecentricgroupId>
<artifactId>spring-boot-admin-starter-serverartifactId>
<version>2.3.1version>
dependency>
@Configuration
@EnableAutoConfiguration
@EnableAdminServer
public class SpringBootAdminApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootAdminApplication.class, args);
}
}
server.port=8888
客户端:被记录数据
<dependency>
<groupId>de.codecentricgroupId>
<artifactId>spring-boot-admin-starter-clientartifactId>
<version>2.3.1version>
dependency>
<dependency>
<groupId>de.codecentricgroupId>
<artifactId>spring-boot-admin-starter-securityartifactId>
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: '*'
为方便多环境适配,springboot简化了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配置优先
@Profile(“prod”)
className or functionName
决定类或方法在什么配置文件生效时启用
application.properties
spring.profiles.active=name
spring.profiles.group.name[0]=yaml1
spring.profiles.group.name[1]=yaml2