1. 配置文件 配置文件的编写有两种形式:
properties 1 2 3 4 driverClassName =com.mysql.cj.jdbc.Driver url =jdbc:mysql:///test username =root password =123456
这种方式一直伴随我们,不再赘述
yaml YAML 是 “YAML Ain’t Markup Language”(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:”Yet Another Markup Language”(仍是一种标记语言)。
非常适合用来做以数据为中心的配置文件
以 .yaml 或 .yml 为后缀都可
基本语法
key: value;kv之间有空格
大小写敏感
使用缩进表示层级关系
缩进不允许使用tab,只允许空格(在 idea 中使用Tab键也不报错)
缩进的空格数不重要,只要相同层级的元素左对齐即可
‘#’表示注释
字符串无需加引号,如果要加,’’与””表示字符串内容会被转义/不转义
比如 ‘\n’ 输出在控制台就是字符串\n;”\n”输出在控制台是换行
数据类型
字面量:单个的、不可再分的值。date、boolean、string、number、null
对象:键值对的集合。map、hash、object
1 2 3 4 5 6 7 k: {k1: v1 ,k2: v2 ,k3: v3 }k: k1: v1 k2: v2 k3: v3
数组:一组按次序排列的值。array、list、queue、set
1 2 3 4 5 6 7 k: [v1 ,v2 ,v3 ]k: - v1 - v2 - v3
属性导入示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 person: name: John boss: false birth: 1998 /12/12 20 :12:33 age: 25 pet: name: wang age: 2 interests: [ 篮球 ,唱歌 ,跳舞 ] animal: - jerry - tom score: math: first: 90 second: 95 english: [ 85 ,88 ] salarys: [ 9000 ,10000.66 ,11000 ] allPets: cat: - name: jerry age: 3 dog: [ { name: tom ,age: 4 } ] myString: test: hello,world!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Data @Component @ConfigurationProperties(prefix = "person") public class Person { private String name; private Boolean boss; private Date birth; private Integer age; private Pet pet; private String[] interests; private List<String> animal; private Map<String, Object> score; private Set<Double> salarys; private Map<String, List<Pet>> allPets; @Value("${myString.test}") private String stringTest; } ============================================================ @Data public class Pet { private String name; private Integer age; }
注释处理器 当我们在配置文件中添加参数时,SpringBoot内部的参数会有提示,但我们设置自定义的类和配置文件时没有提示,我们可以添加SpringBoot官方声明的注释处理器:
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-configuration-processor</artifactId > <optional > true</optional > </dependency >
得先运行以便才能生效
官方给的注意项:
如果在项目中使用AspectJ,则需要确保注释处理器只运行一次。有几种方法可以做到这一点。使用Maven,您可以显式配置maven-apt-plugin,并仅在那里将依赖项添加到注释处理器。您还可以让AspectJ插件运行所有处理,并在maven-compiler-plugin配置中禁用注释处理,如下所示:
1 2 3 4 5 6 7 <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-compiler-plugin</artifactId > <configuration > <proc > none</proc > </configuration > </plugin >
如果您在项目中使用Lombok,则需要确保其注释处理器在spring-boot-configuration-processor之前运行。使用Maven时,可以使用Maven编译器插件的annotationProcessors属性以正确的顺序列出注释处理器。如果您没有使用此属性,并且注释处理器是由类路径上可用的依赖项获取的,请确保在spring-boot-configuration-processor依赖项之前定义了lombok依赖项。
2. Web开发
官方给我们自定义配置的建议:
If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.
不用@EnableWebMvc注解。使用 @Configuration + WebMvcConfigurer 自定义规则
If you want to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, and still keep the Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations and use it to provide custom instances of those components.
声明 WebMvcRegistrations 改变默认底层组件
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc, or alternatively add your own @Configuration-annotated DelegatingWebMvcConfiguration as described in the Javadoc of @EnableWebMvc.
使用 @EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration 全面接管SpringMVC
使用 Spring Initailizr 创建一个 web工程:
①静态资源访问 1 2 By default, Spring Boot serves static content from a directory called /static (or /public or /resources or /META-INF/resources) in the classpath or from the root of the ServletContext. 默认情况下,Spring Boot从类路径中名为/static(或/public或/resources或/META-INF/resources)的目录或ServletContext的根目录提供静态内容。
直接使用项目根路径/+静态资源名就可以访问,例http://localhost:8080/1.jpg
1 private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String []{"classpath:/META-INF/resources/" , "classpath:/resources/" , "classpath:/static/" , "classpath:/public/" };
可以在配置文件中修改spring.web.static-locations:
1 2 3 4 By default, resources are mapped on /**, but you can tune that with the spring.mvc.static-path-pattern property. For instance, relocating all resources to /resources/** can be achieved as follows: 默认情况下,资源映射在/**上,但您可以使用spring.mvc进行调整。静态路径模式属性。例如,将所有资源重新定位到/resources/**可以通过以下方式实现: spring.mvc.static-path-pattern=/resources/**
资源路径不需要改变,只是添加了静态资源的访问前缀
webjar WebJars是打包到JAR(Java存档)文件中的客户端web库(例如jQuery和Bootstrap)。
自动映射 /webjars/**
例:http://localhost:8080/webjars/jquery/3.5.1/jquery.js,后面地址要按照依赖里的包路径
欢迎页
静态资源路径下的 index.html,SpringBoot会自动识别为欢迎页。
注意:如果给静态资源添加了前缀不能直接访问欢迎页
Favicon 网络图标;直接将 favicon.ico 放在静态资源目录下即可。
静态资源添加前缀也会使 Favicon 功能失效
Favicon 默认是session级别资源,谷歌浏览器可以使用 ctrl+F5 去除缓存重新请求
运行logo Spring Boot启动打印默认logo的类是SpringApplicationBannerPrinter类,SpringBoot 默认寻找 Banner的顺序是: 首先依次在 Classpath下找文件banner.gif,banner.jpg和 banner.png ,使用优先找到的 若没找到上面文件的话,继续 Classpath下找 banner.txt 若上面都没有找到的话, 用默认的 SpringBootBanner,也就是上面输出的 Spring Boot logo 一般是把banner.txt文件放在 src/main/resources/目录下。
静态资源配置原理 SpringBoot 的自动配置类:org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
1 2 3 4 @Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class}) @EnableConfigurationProperties({WebMvcProperties.class, WebProperties.class}) @Order(0) public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer , ServletContextAware {
1 2 3 4 5 6 @ConfigurationProperties( prefix = "spring.mvc" ) public class WebMvcProperties {...}@ConfigurationProperties("spring.web") public class WebProperties {...}
不同版本会有细微区别,就不在此展示详细规则
②请求参数处理 请求映射 restful 风格:
SpringBoot 中默认是关闭的
1 2 3 4 5 6 7 8 9 @Bean @ConditionalOnMissingBean({HiddenHttpMethodFilter.class}) @ConditionalOnProperty( prefix = "spring.mvc.hiddenmethod.filter", name = {"enabled"} ) public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter () { return new OrderedHiddenHttpMethodFilter (); }
手动开启:
1 spring.mvc.hiddenmethod.filter.enabled =ture
当然我们也可以自定义配置:
1 2 3 4 5 6 7 @Bean public HiddenHttpMethodFilter hiddenHttpMethodFilter () { HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter (); methodFilter.setMethodParam("_m" ); return methodFilter; }
HiddenHttpMethodFilter 还兼容 PUT、DELETE、PATCH 请求;其内部使用了包装模式 requestWrapper
当使用客户端工具时,不需要开启 filter,如PostMan可以直接发送put,delete请求。
请求映射原理: org.springframework.web.servlet.DispatcherServlet#doDispatch
1 2 3 mappedHandler = getHandler(processedRequest); HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
RequestMappingHandlerMapping :保存了所有@RequestMapping 和handler的映射规则
如图,所有的请求映射都在 HandlerMapping 中。收到请求后,会遍历 HandlerMapping 看是否有请求信息,有就找到对应的handler
普通参数与基本注解 @PathVariable(路径变量)、@RequestHeader(请求头)、@RequestBody(请求体)、@RequestParam(请求参数)、@ModelAttribute、@MatrixVariable(矩阵变量)、@CookieValue(获取cookie值)、@RequestAttribute(可以将request中的值直接赋值给方法中的参数)
其中 @ModelAttribute 注解在方法上,会在每一个@RequestMapping标注的方法前执行, 如果有返回值,则自动将该返回值加入到ModelMap中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @GetMapping("/person/{name}") public Map<String, Object> test (@PathVariable("name") String name, @PathVariable Map<String, Object> map, @RequestParam(value = "age", required = false) Integer age, @RequestParam("hobby") List<String> hobby, @RequestParam Map<String, Object> map1, @RequestHeader(value = "User-Agent", required = false) String rh, @RequestHeader Map<String, Object> map2, Cookie cookie, @CookieValue(value = "ga", required = false) String ga) { System.out.println(cookie.getName()+cookie.getValue()); System.out.println(ga); return map; }
其中有个问题,浏览器没显示cookie,但服务器收到了
1 2 3 4 @PostMapping("/rb") public String test3 (@RequestBody String rb) { return rb; }
只有post请求有请求体,其它(put、delete..)都没有
矩阵变量(较少使用):
问题引入:当浏览器禁用了 cookie ,怎么使用session中的数据?
1 2 3 4 5 session.set(a,b) -> jsessionid -> cookie -> 每次请求携带 解决方式:把cookie的值使用矩阵变量的方式进行传递 url重写:/abc;jsessionid=xxx 我们甚至可以使用矩阵变量携带参数: /1;name=John;age=20;hobby=1,2/2;name=Tom;age=30
SpringBoot 默认是禁用了矩阵变量的功能
1 2 private boolean removeSemicolonContent = true ;
分号后面的内容都删除
手动开启方式一:
1 2 3 4 5 6 7 8 @Configuration( proxyBeanMethods = false ) @Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class}) @EnableConfigurationProperties({WebMvcProperties.class, WebProperties.class}) @Order(0) public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer , ServletContextAware {
我们点进 WebMvcConfigurer 可以看到它内部声明了很多默认方法。
1 2 3 4 5 6 7 8 9 10 11 @Configuration(proxyBeanMethods = false) public class MyConfiguration implements WebMvcConfigurer { @Override public void configurePathMatch (PathMatchConfigurer configurer) { UrlPathHelper up=new UrlPathHelper (); up.setRemoveSemicolonContent(false ); configurer.setUrlPathHelper(up); } }
方式二:
1 2 3 4 5 6 7 8 9 10 11 @Bean public WebMvcConfigurer webMvcConfigurer () { return new WebMvcConfigurer () { @Override public void configurePathMatch (PathMatchConfigurer configurer) { UrlPathHelper up = new UrlPathHelper (); up.setRemoveSemicolonContent(false ); configurer.setUrlPathHelper(up); } }; }
测试:
1 2 3 4 5 6 7 8 9 10 11 @GetMapping("/emp/{id}") public Map<String, Object> test3 (@PathVariable("id") Integer id, @MatrixVariable("name") String name, @MatrixVariable("hobby") List<String> list) { Map<String, Object> map = new HashMap <>(); map.put("id" , id); map.put("name" , name); map.put("hobby" , list); return map; }
1 2 3 4 5 6 7 8 9 @GetMapping("/emp/{emp1}/{emp2}") public Map<String, Object> test3 (@MatrixVariable(value = "name",pathVar = "emp1") String emp1, @MatrixVariable(value = "name",pathVar = "emp2") String emp2) { Map<String, Object> map = new HashMap <>(); map.put("emp1" , emp1); map.put("emp2" , emp2); return map; }
矩阵变量是绑定在路径变量中的,矩阵变量必须有url路径变量才能被解析
Servlet API WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId
ServletRequestMethodArgumentResolver 将以上的参数进行了处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override public boolean supportsParameter (MethodParameter parameter) { Class<?> paramType = parameter.getParameterType(); return (WebRequest.class.isAssignableFrom(paramType) || ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType) || HttpSession.class.isAssignableFrom(paramType) || (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) || Principal.class.isAssignableFrom(paramType) || InputStream.class.isAssignableFrom(paramType) || Reader.class.isAssignableFrom(paramType) || HttpMethod.class == paramType || Locale.class == paramType || TimeZone.class == paramType || ZoneId.class == paramType); }
复杂参数 Map 、Model 、Errors/BindingResult、RedirectAttributes( 重定向携带数据) 、ServletResponse 、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder
自定义对象参数 可以自动类型转换与格式化,可以级联封装
pojo 底层都是使用 ServletModelAttributeMethodProcessor (类)来封装的
1 WebDataBinder:web数据绑定器,将请求的参数的值绑定到指定的 JavaBean 中;其内部使用了 Converters 将请求转成指定的数据类型。
自定义 Converter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Bean public WebMvcConfigurer webMvcConfigurer () { return new WebMvcConfigurer () { @Override public void addFormatters (FormatterRegistry registry) { registry.addConverter(new Converter <String, Pet>() { @Override public Pet convert (String source) { if (!StringUtils.isEmpty(source)) { Pet pet = new Pet (); String[] split = source.split("," ); pet.setName(split[0 ]); pet.setAge(Integer.parseInt(split[1 ])); return pet; } return null ; } }); } }; }
③参数处理原理 SpringBoot底层对控制器方法的注解循环遍历,根据注解类型决定处理方式:
1 2 3 4 5 6 7 8 9 10 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); return this .handleInternal(request, response, (HandlerMethod)handler);mav = this .invokeHandlerMethod(request, response, handlerMethod); invocableMethod.setHandlerMethodArgumentResolvers(this .argumentResolvers); invocableMethod.setHandlerMethodReturnValueHandlers(this .returnValueHandlers); invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object [0 ]);
获取方法所有的参数: 1 2 3 4 5 6 7 8 9 invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object [0 ]); Object returnValue = this .invokeForRequest(webRequest, mavContainer, providedArgs);Object[] args = this .getMethodArgumentValues(request, mavContainer, providedArgs); mavContainer.setRequestHandled(true );
map 和 model 类型参数底层调用的方法都是 mavContainer.getModel();最终调用的还是 request.setAttribute
目标方法执行完成: 目标方法执行完成后,所有的数据都会放在 ModelAndViewContainer,包含要去的页面地址 View 和 Model 数据。
控制器是内部转发请求所以 view 为 null
最终将数据传回 DispatcherServlet 中的 mv属性,再使用 processDispatchResult 方法处理派发结果
④数据响应与内容协商
响应JSON jackson.jar+@ResponseBody
SpringBoot的web场景自动引入了json场景
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-json</artifactId > <version > 2.7.6</version > </dependency >
从此处开始原理部分不清不楚,自己debug还老跑丢,以下涉及源码部分不一定对,以后回来补充
返回值解析器逻辑:
遍历所有的返回值处理器,内部判断是否支持当前类型的返回值;找到合适的返回值处理器后根据请求头中的accept信息进行内容协商(浏览器能接受且服务器支持),内部循环遍历得到合适的 HttpMessageConverter (转换器)
构造器中初始化:
1 2 3 4 5 6 7 8 9 10 0 - 只支持Byte类型的 1 - String 2 - String 3 - Resource 4 - ResourceRegion 5 - DOMSource.class \ SAXSource.class \ StAXSource.class \StreamSource.class \Source.class 6 - MultiValueMap 7 - true 8 - true 9 - 支持注解方式xml处理的。
内容协商 引入xml依赖:
1 2 3 4 <dependency > <groupId > com.fasterxml.jackson.dataformat</groupId > <artifactId > jackson-dataformat-xml</artifactId > </dependency >
SpringBoot底层会将浏览器的接受方式和服务器的转换方式进行匹配(双层循环),得到所有符合浏览器条件的媒体类型,再循环转换器转换数据,最后将所有转换完成的数据根据媒体权重等信息进行排序取首位(最佳匹配媒体类型)
当我们引入xml依赖,由于浏览器中xml数据的权重比较高,最终以xml形式返回
1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
开启浏览器参数方式内容协商功能 1 spring.mvc.contentnegotiation.favor-parameter =true
底层添加了 parameter 策略(优先级更高),默认只有请求头一个策略
在地址栏中添加 format 参数,例:http://localhost:8080/person?format=json
默认参数只支持 json 和 xml
自定义 MessageConverter 1 2 3 4 5 6 7 8 9 10 @Bean public WebMvcConfigurer webMvcConfigurer () { return new WebMvcConfigurer () { @Override public void extendMessageConverters (List<HttpMessageConverter<?>> converters) { converters.add(new MymesssageConverter ()); } } }
1 2 public class MymesssageConverter implements HttpMessageConverter <T>{...}
⑤视图解析与模板引擎 SpringBoot默认不支持 JSP,需要引入第三方模板引擎技术实现页面渲染。
SpringBoot工程的打包结果是一个jar包,是压缩包,JSP不支持在压缩包中被编译运行,所以SpringBoot默认不支持JSP。
thymeleaf 1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-thymeleaf</artifactId > </dependency >
基本语法(此处仅作补充):7. Thymeleaf
表达式名字
语法
用途
变量取值
${…}
获取请求域、session域、对象等值
选择变量
*{…}
获取上下文对象值
消息
#{…}
获取国际化等值
链接
@{…}
生成链接
片段表达式
~{…}
jsp:include 作用,引入公共页面片段
设置属性值:th:attr
1 2 3 4 5 6 <form action ="subscribe.html" th:attr ="action=@{/subscribe}" > <fieldset > <input type ="text" name ="email" /> <input type ="submit" value ="Subscribe!" th:attr ="value=#{subscribe.submit}" /> </fieldset > </form >
多个属性用逗号分割:th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}"
以上两个的代替写法 th:xxx;此方式常用不再赘述
⑥拦截器 HandlerInterceptor 接口:
创建拦截器需要继承此接口
1 public class MyInterceptor implements HandlerInterceptor {..}
配置拦截器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @Configuration public class AdminWebConfig implements WebMvcConfigurer { @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor ()) .addPathPatterns("/**" ) .excludePathPatterns("/" ,"/login" ,"/css/**" ,"/fonts/**" ,"/images/**" ,"/js/**" ); } } @Slf4j public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("执行控制器前。。。" ); return HandlerInterceptor.super .preHandle(request, response, handler); } @Override public void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("执行控制器后。。。" ); HandlerInterceptor.super .postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("渲染视图后。。。" ); HandlerInterceptor.super .afterCompletion(request, response, handler, ex); } }
⑦文件上传 1 2 3 4 5 6 <form th:action ="@{/form}" method ="post" enctype ="multipart/form-data" > 姓名:<input type ="text" name ="name" > <br > 头像:<input type ="file" name ="img" > <br > 照片:<input type ="file" name ="photos" multiple > <br > <input type ="submit" name ="submit" > </form >
multiple 属性可以支持多文件上传
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Slf4j @Controller public class MyController { @PostMapping("/form") public String test8 (@RequestParam("name") String name, @RequestPart("img") MultipartFile img, @RequestPart("photos") MultipartFile[] photos) throws IOException { log.info("接收的信息:name={},img={},photos={}" , name, img.getSize(), photos.length); UUID uuid = UUID.randomUUID(); if (!img.isEmpty()) { String fileName=img.getOriginalFilename(); String hz = fileName.substring(fileName.lastIndexOf("." )); img.transferTo(new File ("H:\\" +uuid + hz)); File file = new File ("H:\\photos" ); if (!file.exists()) file.mkdir(); for (int i=0 ;i<photos.length;i++) { uuid = UUID.randomUUID(); photos[i].transferTo(new File (file.getName() + "\\" + uuid + hz)); } } return "hello" ; } }
Tomcat 接受到的文件会先默认放在一个指定的临时文件夹,误删后上传会报错
1 2 3 4 5 6 spring.servlet.multipart.max-file-size =10MB spring.servlet.multipart.max-request-size =100MB spring.servlet.multipart.location =H:\\temp
文件上传自动配置类:MultipartAutoConfiguration -> MultipartProperties
⑧异常处理 默认情况下,SpringBoot 提供/error 处理所有错误映射,对于浏览器客户端,以HTML格式呈现错误数据,对于其它客户端生成JSON响应相同错误数据
我们可以在templates目录下创建 error 文件夹,SpringBoot会自动解析其目录下的4xx、5xx页面(有精确的状态码页面就匹配精确,没有就找 4xx.html)
⑨Web原生组件注入(Servlet、Filter、Listener) 官方文档表示SpringBoot提供嵌入式Servlet容器支持,不但可以自动配置多种服务器(Tomcat , Jetty , and Undertow ),还支持使用Servlet API
方式一(推荐):
1 2 3 When using an embedded container, automatic registration of classes annotated with @WebServlet, @WebFilter, and @WebListener can be enabled by using @ServletComponentScan. 当使用嵌入式容器时,可以使用@ServletComponentScan启用用@WebServlet、@WebFilter和@WebListener注释的类的自动注册。
@ServletComponentScan(basePackages = “com”) :指定原生Servlet组件位置
@WebServlet(urlPatterns = “/my”):效果:直接响应,不经过Spring的拦截器
@WebFilter(urlPatterns={“/css/*“,”/images/*“})
@WebListener
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 @ServletComponentScan(basePackages = {"com"}) @SpringBootApplication(scanBasePackages = "com") public class Demo2Application { public static void main (String[] args) { SpringApplication.run(Demo2Application.class, args); } } @WebServlet(urlPatterns = "/servlet") public class MyServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getRequestDispatcher("/hello" ).forward(req,resp); } } @Slf4j @WebFilter("/*") public class MyFilter implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException { log.info("MyFilter init 初始化" ); } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { log.info("执行过滤操作" ); filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy () { log.info("过滤器销毁" ); } } @Slf4j @WebListener public class MyListener implements ServletContextListener { @Override public void contextInitialized (ServletContextEvent sce) { log.info("监听器初始化" ); ServletContextListener.super .contextInitialized(sce); } @Override public void contextDestroyed (ServletContextEvent sce) { log.info("监听器销毁" ); ServletContextListener.super .contextDestroyed(sce); } }
“/*” 是原生servlet的写法,”/**”是Spring生态的写法
我们运行后会发现:原生select不经过拦截器
此时服务器中有两个servlet,DispatcherServlet 走Spring流程,而MyServlet 由Tomcat处理。
DispatcherServlet :filter -> interceptor -> handler
MyServlet :filter -> servlet
都能处理到同一层路径,精确优选原则
方式二:
1 2 3 If convention-based mapping is not flexible enough, you can use the ServletRegistrationBean, FilterRegistrationBean, and ServletListenerRegistrationBean classes for complete control. 如果基于约定的映射不够灵活,则可以使用ServletRegistrationBean、FilterRegistrationBean和ServletListenerRegistrationBeans类来实现完全控制。
不推荐这种方式,官方给的注意项较多,用不好容易出bug
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @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); } }
SpringBoot底层也是通过 ServletRegistrationBean<DispatcherServlet>把 DispatcherServlet 配置进来,默认映射的是 / 路径
⑩切换嵌入式容器 默认支持的webServer
Tomcat, Jetty, or Undertow
ServletWebServerApplicationContext容器启动寻找ServletWebServerFactory并引导创建服务器
先在web场景包中排除tomcat依赖,再引入其它服务器依赖:
1 2 3 4 5 6 7 8 9 10 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > <exclusions > <exclusion > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-tomcat</artifactId > </exclusion > </exclusions > </dependency >
原理:
web应用会创建一个web版的ioc容器 ServletWebServerApplicationContext
该容器启动时会寻找 ServletWebServerFactory(Servlet 的web服务器工厂---> Servlet 的web服务器)
SpringBoot底层默认有很多的WebServer工厂:TomcatServletWebServerFactory, JettyServletWebServerFactory, UndertowServletWebServerFactory
底层有一个自动配置类:ServletWebServerFactoryAutoConfiguration
它导入了ServletWebServerFactoryConfiguration(配置类)
配置类动态判断导入了哪个Web服务器的包。(默认是web-starter导入tomcat包),容器中就有 TomcatServletWebServerFactory
TomcatServletWebServerFactory创建出Tomcat服务器并启动;TomcatWebServer的构造器拥有初始化方法initialize---this.tomcat.start();
内嵌服务器,其实就是把启动服务器的代码调用
定制化Servlet容器 方式一:
实现 WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>
把配置文件的值和ServletWebServerFactory进行绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import org.springframework.boot.web.server.WebServerFactoryCustomizer;import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;import org.springframework.stereotype.Component;@Component public class CustomizationBean implements WebServerFactoryCustomizer <ConfigurableServletWebServerFactory> { @Override public void customize (ConfigurableServletWebServerFactory server) { server.setPort(9000 ); ... } }
方式二: 修改配置文件 server.xxx 方式三: 直接自定义ConfigurableServletWebServerFactory
定制化原理 定制化常见方式:
修改配置文件
xxxxxCustomizer:定制化器,可以改变xxxx的默认规则
编写自定义的配置类:xxxConfiguration;+ @Bean替换、增加容器中默认组件
如Web应用编写一个配置类实现 WebMvcConfigurer(即可定制化web功能)+ @Bean给容器中扩展一些组件
@EnableWebMvc + WebMvcConfigurer
原理
WebMvcAutoConfiguration 默认的SpringMVC的自动配置功能类。静态资源、欢迎页…..
一旦使用 @EnableWebMvc 。它会 @Import(DelegatingWebMvcConfiguration.class)
DelegatingWebMvcConfiguration 的作用:只保证SpringMVC最基本的使
把所有系统中的 WebMvcConfigurer 拿过来。所有功能的定制都是这些 WebMvcConfigurer 合起来一起生效
自动配置了一些非常底层的组件。RequestMappingHandlerMapping、这些组件依赖的组件都是从容器中获取
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
WebMvcAutoConfiguration 里面的配置要能生效必须满足@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@EnableWebMvc 导致了 WebMvcAutoConfiguration 没有生效。
3. 数据访问 SQL 1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-jdbc</artifactId > </dependency >
SpringBoot在我们导入依赖时会自动给我们配置一个数据源:HikariDataSource
注意:其中没有导入数据库驱动
1 2 3 4 <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > </dependency >
想要修改版本 1、直接依赖引入具体版本(maven的就近依赖原则) 2、重新声明版本(maven的属性的就近优先原则)
自动配置 DataSourceAutoConfiguration : 数据源的自动配置
修改数据源相关的配置:spring.datasource
数据库连接池的配置,是自己容器中没有DataSource才自动配置的
底层配置好的连接池是:HikariDataSource
1 2 3 4 5 6 7 @Configuration( proxyBeanMethods = false ) @Conditional({DataSourceAutoConfiguration.PooledDataSourceCondition.class}) @ConditionalOnMissingBean({DataSource.class, XADataSource.class}) @Import({Hikari.class, Tomcat.class, Dbcp2.class, OracleUcp.class, Generic.class, DataSourceJmxConfiguration.class}) protected static class PooledDataSourceConfiguration {
DataSourceTransactionManagerAutoConfiguration: 事务管理器的自动配置
JdbcTemplateAutoConfiguration: JdbcTemplate的自动配置,可以来对数据库进行crud
可以修改这个配置项@ConfigurationProperties(prefix = “spring.jdbc”) 来修改JdbcTemplate
@Bean@Primary JdbcTemplate;容器中有这个组件
JndiDataSourceAutoConfiguration: jndi的自动配置
XADataSourceAutoConfiguration: 分布式事务相关的
修改配置项 1 2 3 4 5 6 spring: datasource: url: jdbc:mysql://localhost:3306/test username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver
更换数据源 druid官方github地址:https://github.com/alibaba/druid
整合第三方技术的两种方式:自定义、starter
自定义:
1 2 3 4 5 <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.2.8</version > </dependency >
创建数据源:
1 2 3 4 5 6 7 8 @Configuration public class MyConfig (){ @Bean @ConfigurationProperties("spring.datasource") public DataSource dataSource () { return new DruidDataSource (); } }
druid 内置了监控页面:StatViewServlet
StatViewServlet的用途包括:
提供监控信息展示的html页面
提供监控信息的JSON API
1 2 3 4 5 @Bean public ServletRegistrationBean<StatViewServlet> statViewServlet () { StatViewServlet statViewServlet = new StatViewServlet (); return new ServletRegistrationBean <>(statViewServlet, "/druid/*" ); }
WebStatFilter:用于采集web-jdbc关联监控的数据
1 2 3 4 5 6 7 8 @Bean public FilterRegistrationBean<WebStatFilter> webStatFilter () { WebStatFilter webStatFilter = new WebStatFilter (); FilterRegistrationBean<WebStatFilter> bean = new FilterRegistrationBean <>(webStatFilter); bean.setUrlPatterns(Arrays.asList("/*" )); bean.addInitParameter("exclusions" , "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" ); return bean; }
根据官方介绍配置就好,其它还有很多功能,不在此赘述
starter
1 2 3 4 5 <dependency > <groupId > com.alibaba</groupId > <artifactId > druid-spring-boot-starter</artifactId > <version > 1.2.8</version > </dependency >
分析自动配置:
扩展配置项 spring.datasource.druid
DruidSpringAopConfiguration.class, 监控SpringBean的;配置项:spring.datasource.druid.aop-patterns
DruidStatViewServletConfiguration.class, 监控页的配置:spring.datasource.druid.stat-view-servlet;默认开启
DruidWebStatFilterConfiguration.class, web监控配置;spring.datasource.druid.web-stat-filter;默认开启
DruidFilterConfiguration.class 所有Druid自己filter的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 spring: datasource: url: jdbc:mysql://localhost:3306/db_account username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver druid: aop-patterns: com.atguigu.admin.* filters: stat,wall stat-view-servlet: enabled: true login-username: admin login-password: 123456 resetEnable: false web-stat-filter: enabled: true urlPattern: /* 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
有需要直接按照官方文档配置即可,这里的一小部分仅供参考
整合 MyBatis MyBatis官方github地址:https://github.com/mybatis
1 2 3 4 5 <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > <version > 2.1.4</version > </dependency >
配置模式
全局配置文件:mybatis-config.xml
Mapper 映射文件
底层自动配置了 SqlSessionFactory 和 SqlSessionTemplate (组合了SqlSession)
1 2 3 4 5 6 @EnableConfigurationProperties(MybatisProperties.class) @AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class }) public class MybatisAutoConfiguration {}@ConfigurationProperties(prefix = "mybatis") public class MybatisProperties
注解模式
编写mapper接口,@Mapper注解或配置类上@MapperScan(“com.mapper”)
编写sql映射文件并绑定mapper接口
在application.yaml中指定Mapper配置文件的位置,以及指定全局配置文件的信息 (建议配置在mybatis.configuration)
@Import(AutoConfiguredMapperScannerRegistrar.class) -> @Mapper
只要我们在接口上注明了@Mapper,底层就会扫描并将代理类放在容器中
MybatisProperties 中也可以看到,我们在配置文件中的内容都可以迁移到application.properties中
1 2 3 4 mybatis: config-location: classpath:mybatis/mybatis-config.xml mapper-locations: classpath:mybatis/mapper/*.xml
Spring配置文件中的配置和mybatis全局配置文件不能重复设置
示例:
1 2 3 4 5 6 7 8 9 @Mapper public interface CityMapper { @Select("select * from city where id=#{id}") public City getById (Long id) ; public void insert (City city) ; }
当然我们也可以使用混合模式,如上的insert方法
@Options 注释可以设置标签的设置项,例如insert标签中的useGeneratedKeys
整合 MyBatis-Plus 引入依赖:
1 2 3 4 5 <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.4.1</version > </dependency >
自动配置:
MybatisPlusAutoConfiguration 配置类,MybatisPlusProperties 配置项绑定。mybatis-plus:xxx 就是对mybatis-plus的定制
自动配置好SqlSessionFactory 。底层是容器中默认的数据源
自动配置好mapperLocations 。有默认值:classpath*:/mapper/**/*.xml任意路径下的所有xml都是sql映射文件。 建议以后sql映射文件,放在 mapper下
容器中自动配置了 SqlSessionTemplate
@Mapper 标注的接口也会被自动扫描;建议直接使用@MapperScan
Mapper继承 BaseMapper 就可以拥有crud功能,详情见8. MyBatisPlus
NoSQL 引入依赖:
1 2 3 4 <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**> : xxxTemplate;
自动注入了StringRedisTemplate;k:v都是String
底层只要我们使用 StringRedisTemplate、RedisTemplate就可以操作redis
1 2 3 4 5 6 7 8 @Test void testRedis () { ValueOperations<String, String> operations = redisTemplate.opsForValue(); operations.set("hello" ,"world" ); String hello = operations.get("hello" ); System.out.println(hello); }
如果使用指定泛型为<String, Object>只能使用@Resource,或者使用对应的工厂类自定义模板
切换 Jedis 1 2 3 4 5 6 7 8 9 10 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > </dependency > <dependency > <groupId > redis.clients</groupId > <artifactId > jedis</artifactId > </dependency >
配置:
1 2 3 4 5 6 7 8 9 spring: redis: host: 192.168 .1 .100 port: 6379 password: 123456 client-type: jedis jedis: pool: max-active: 10