SpringBoot2核心技术-核心功能 1.配置文件 1.1Properties 1.2yaml 1.2.1简介 YAML 是 "YAML Ain't Markup Language"(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)。 非常适合用来做以数据为中心的配置文件
1.2.2基本语法 ● key: value;kv之间有空格 ● 大小写敏感 ● 使用缩进表示层级关系 ● 缩进不允许使用tab,只允许空格 ● 缩进的空格数不重要,只要相同层级的元素左对齐即可 ● '#'表示注释 ● 字符串无需加引号,如果要加,''与""表示字符串内容 会被 转义/不转义
1.2.3数据类型 字面量:单个的、不可再分的值。date、boolean、string、number、null
对象:键值对的集合。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
1.2.4示例 @Data public class Person { private String userName; private Boolean boss; private Date birth; private Integer age; private Pet pet; private String[] interests; private List<String> animal; private Map<String, Object> score; private Set<Double> salarys; private Map<String, List<Pet>> allPets; } @Data public class Pet { private String name; private Double weight; }
person: userName: zhangsan boss: false birth: 2019 /12/12 20 :12:33 age: 18 pet: name: tomcat weight: 23.4 interests: [篮球 ,游泳 ] animal: - jerry - mario score: english: first: 30 second: 40 third: 50 math: [131 ,140 ,148 ] chinese: {first: 128 ,second: 136 } salarys: [3999 ,4999.98 ,5999.99 ] allPets: sick: - {name: tom } - {name: jerry ,weight: 47 } health: [{name: mario ,weight: 47 }]
1.3配置提示
<dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-configuration-processor</artifactId > <optional > true</optional > </dependency > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <configuration > <excludes > <exclude > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-configuration-processor</artifactId > </exclude > </excludes > </configuration > </plugin > </plugins > </build >
2.Web开发 2.1SpringMVC自动配置概览 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 (covered later in this document)). ○ 静态资源(包括webjars) ● Automatic registration of Converter, GenericConverter, and Formatter beans. ○ 自动注册 Converter,GenericConverter,Formatter ● Support for HttpMessageConverters (covered later in this document). ○ 支持 HttpMessageConverters (后来我们配合内容协商理解原理) ● Automatic registration of MessageCodesResolver (covered later in this document). ○ 自动注册 MessageCodesResolver (国际化用) ● Static index.html support. ○ 静态index.html 页支持 ● Custom Favicon support (covered later in this document). ○ 自定义 Favicon ● Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document). ○ 自动使用 ConfigurableWebBindingInitializer ,(DataBinder负责将请求数据绑定到JavaBean上)
2.2静态资源访问 只要静态资源放在类路径下: called /static (or /public or /resources or /META-INF/resources 访问 : 当前项目根路径/ + 静态资源名 原理: 静态映射/**。 请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面 改变默认的静态资源路径
spring: mvc: static-path-pattern: /res/** resources: static-locations: [classpath:/haha/ ]
2.3静态资源访问前缀 默认无前缀
spring: mvc: static-path-pattern: /res/**
2.4欢迎页支持 ● 静态资源路径下 index.html ○ 可以配置静态资源路径 ○ 但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问
spring: # mvc: # static-path-pattern: /res/** 这个会导致welcome page功能失效 resources: static-locations: [classpath:/haha/]
2.5静态资源配置原理 ● SpringBoot启动默认加载 xxxAutoConfiguration 类(自动配置类) ● SpringMVC功能的自动配置类 WebMvcAutoConfiguration,生效
@Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration {}
@Configuration(proxyBeanMethods = false) @Import(EnableWebMvcConfiguration.class) @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class }) @Order(0) public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {}
配置文件的相关属性和xxx进行了绑定。WebMvcProperties==spring.mvc、ResourceProperties==spring.resources
2.5.1配置类只有一个有参构造器 public WebMvcAutoConfigurationAdapter (ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ObjectProvider<DispatcherServletPath> dispatcherServletPath, ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) { this .resourceProperties = resourceProperties; this .mvcProperties = mvcProperties; this .beanFactory = beanFactory; this .messageConvertersProvider = messageConvertersProvider; this .resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable(); this .dispatcherServletPath = dispatcherServletPath; this .servletRegistrations = servletRegistrations; }
2.5.2资源处理的默认规则 @Override public void addResourceHandlers (ResourceHandlerRegistry registry) { if (!this .resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled" ); return ; } Duration cachePeriod = this .resourceProperties.getCache().getPeriod(); CacheControl cacheControl = this .resourceProperties.getCache().getCachecontrol().toHttpCacheControl(); if (!registry.hasMappingForPattern("/webjars/**" )) { customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**" ) .addResourceLocations("classpath:/META-INF/resources/webjars/" ) .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } String staticPathPattern = this .mvcProperties.getStaticPathPattern(); if (!registry.hasMappingForPattern(staticPathPattern)) { customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern) .addResourceLocations(getResourceLocations(this .resourceProperties.getStaticLocations())) .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); } }
spring: resources: add-mappings: false 禁用所有静态资源规则
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false) public class ResourceProperties { private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/" , "classpath:/resources/" , "classpath:/static/" , "classpath:/public/" }; private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
2.5.3欢迎页的处理规则 HandlerMapping:处理器映射。保存了每一个Handler能处理哪些请求。 @Bean public WelcomePageHandlerMapping welcomePageHandlerMapping (ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) { WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping ( new TemplateAvailabilityProviders (applicationContext), applicationContext, getWelcomePage(), this .mvcProperties.getStaticPathPattern()); welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider)); welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations()); return welcomePageHandlerMapping; } WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Optional<Resource> welcomePage, String staticPathPattern) { if (welcomePage.isPresent() && "/**" .equals(staticPathPattern)) { logger.info("Adding welcome page: " + welcomePage.get()); setRootViewName("forward:index.html" ); } else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) { logger.info("Adding welcome page template: index" ); setRootViewName("index" ); } }
2.6请求参数处理 2.6.1rest使用与原理 ● Rest风格支持(使用HTTP请求方式动词来表示对资源的操作) ○ 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户 ○ 现在: /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户 ○ 核心Filter;HiddenHttpMethodFilter ■ 用法: 表单method=post,隐藏域 _method=put ■ SpringBoot中手动开启 ○ 扩展:如何把_method 这个名字换成我们自己喜欢的。
@RequestMapping(value = "/user",method = RequestMethod.GET) public String getUser () { return "GET-张三" ; } @RequestMapping(value = "/user",method = RequestMethod.POST) public String saveUser () { return "POST-张三" ; } @RequestMapping(value = "/user",method = RequestMethod.PUT) public String putUser () { return "PUT-张三" ; } @RequestMapping(value = "/user",method = RequestMethod.DELETE) public String deleteUser () { return "DELETE-张三" ; } @Bean @ConditionalOnMissingBean(HiddenHttpMethodFilter.class) @ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false) public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter () { return new OrderedHiddenHttpMethodFilter (); } @Bean public HiddenHttpMethodFilter hiddenHttpMethodFilter () { HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter (); methodFilter.setMethodParam("_m" ); return methodFilter; }
Rest原理(表单提交要使用REST的时候) ● 表单提交会带上_method=PUT ● 请求过来被HiddenHttpMethodFilter拦截 ○ 请求是否正常,并且是POST ■ 获取到_ method的值。 ■ 兼容以下请求;PUT.DELETE.PATCH ■ 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。 ■ 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
Rest使用客户端工具
如PostMan直接发送Put、delete等方式请求,无需Filter。
spring: mvc: hiddenmethod: filter: enabled: true
2.6.2请求映射原理
protected void doDispatch (HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null ; boolean multipartRequestParsed = false ; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null ; Exception dispatchException = null ; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); mappedHandler = getHandler(processedRequest);
2.7普通参数与基本注解 1.1、注解: @PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue、@RequestBody
@RestController public class ParameterTestController { @GetMapping("/car/{id}/owner/{username}") public Map<String,Object> getCar (@PathVariable("id") Integer id, @PathVariable("username") String name, @PathVariable Map<String,String> pv, @RequestHeader("User-Agent") String userAgent, @RequestHeader Map<String,String> header, @RequestParam("age") Integer age, @RequestParam("inters") List<String> inters, @RequestParam Map<String,String> params, @CookieValue("_ga") String _ga, @CookieValue("_ga") Cookie cookie) { Map<String,Object> map = new HashMap <>(); map.put("age" ,age); map.put("inters" ,inters); map.put("params" ,params); map.put("_ga" ,_ga); System.out.println(cookie.getName()+"===>" +cookie.getValue()); return map; } @PostMapping("/save") public Map postMethod (@RequestBody String content) { Map<String,Object> map = new HashMap <>(); map.put("content" ,content); return map; } @GetMapping("/cars/{path}") public Map carsSell (@MatrixVariable("low") Integer low, @MatrixVariable("brand") List<String> brand, @PathVariable("path") String path) { Map<String,Object> map = new HashMap <>(); map.put("low" ,low); map.put("brand" ,brand); map.put("path" ,path); return map; } @GetMapping("/boss/{bossId}/{empId}") public Map boss (@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge, @MatrixVariable(value = "age",pathVar = "empId") Integer empAge) { Map<String,Object> map = new HashMap <>(); map.put("bossAge" ,bossAge); map.put("empAge" ,empAge); return map; } }
2.8数据响应与内容协商
2.8.1响应JSON 1.jackson.jar+@ResponseBody
<dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > web场景自动引入了json场景 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-json</artifactId > <version > 2.3.4.RELEASE</version > <scope > compile</scope > </dependency >
2.返回值解析器原理 ● 1、返回值处理器判断是否支持这种类型返回值 supportsReturnType ● 2、返回值处理器调用 handleReturnValue 进行处理 ● 3、RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的。 ○ 1. 利用 MessageConverters 进行处理 将数据写为json ■ 1、内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型) ■ 2、服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据, ■ 3、SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理? ● 1、得到MappingJackson2HttpMessageConverter可以将对象写为json ● 2、利用MappingJackson2HttpMessageConverter将对象转为json再写出去。
2.8.2SpringMVC支持哪些返回值 ModelAndView Model View ResponseEntity ResponseBodyEmitter StreamingResponseBody HttpEntity HttpHeaders Callable DeferredResult ListenableFuture CompletionStage WebAsyncTask 有 @ModelAttribute 且为对象类型的 @ResponseBody 注解 ---> RequestResponseBodyMethodProcessor;
2.8.3内容协商 根据客户端接受能力不同,返回不同媒体类型的数据
1.引入xml依赖
<dependency > <groupId > com.fasterxml.jackson.dataformat</groupId > <artifactId > jackson-dataformat-xml</artifactId > </dependency >
3.开启浏览器参数方式内容协商功能
spring: contentnegotiation: favor-parameter: true
发请求: http://localhost:8080/test/person?format=json http://localhost:8080/test/person?format=xml
2.9视图解析与模板引擎 视图解析:SpringBoot默认不支持 JSP,需要引入第三方模板引擎技术实现页面渲染。
2.9.1模板引擎-Thymeleaf 1.基本语法
表达式名字
语法
用途
变量取值
${…}
获取请求域、session域、对象等值
选择变量
*{…}
获取上下文对象值
消息
#{…}
获取国际化等值
链接
@{…}
生成链接
片段表达式
~{…}
jsp:include 作用,引入公共页面片段
2.9.2Thymeleaf使用 1.引入Starter
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
2.自动配置好了Themeleaf
@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(ThymeleafProperties.class) @ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class }) @AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class }) public class ThymeleafAutoConfiguration { }
自动配好的策略
1、所有thymeleaf的配置值都在 ThymeleafProperties
2、配置好了 SpringTemplateEngine
3、配好了 ThymeleafViewResolver
4、我们只需要直接开发页面
public static final String DEFAULT_PREFIX = "classpath:/templates/" ;public static final String DEFAULT_SUFFIX = ".html" ;
2.10拦截器 2.10.1HandlerInterceptor接口 @Slf4j public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requestURI = request.getRequestURI(); log.info("preHandle拦截的请求路径是{}" ,requestURI); HttpSession session = request.getSession(); Object loginUser = session.getAttribute("loginUser" ); if (loginUser != null ){ return true ; } request.setAttribute("msg" ,"请先登录" ); request.getRequestDispatcher("/" ).forward(request,response); return false ; } @Override public void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("postHandle执行{}" ,modelAndView); } @Override public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("afterCompletion执行异常{}" ,ex); } }
2.10.2配置拦截器 @Configuration public class AdminWebConfig implements WebMvcConfigurer { @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor ()) .addPathPatterns("/**" ) .excludePathPatterns("/" ,"/login" ,"/css/**" ,"/fonts/**" ,"/images/**" ,"/js/**" ); } }
2.10.3.拦截器原理 1、根据当前请求,找到HandlerExecutionChain【 可以处理请求的handler以及handler的所有 拦截器】
2、先来顺序执行 所有拦截器的 preHandle方法
1、如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle
2、如果当前拦截器返回为false。直接 倒序执行所有已经执行了的拦截器的 afterCompletion;
3、如果任何一个拦截器返回false。直接跳出不执行目标方法
4、所有拦截器都返回True。执行目标方法
5、倒序执行所有拦截器的postHandle方法。
6、前面的步骤有任何异常都会直接倒序触发 afterCompletion
7、页面成功渲染完成以后,也会倒序触发 afterCompletion
2.11文件上传 1.页面表单 <form method ="post" action ="/upload" enctype ="multipart/form-data" > <input type ="file" name ="file" > <br > <input type ="submit" value ="提交" > </form >
2.文件上传代码 @PostMapping("/upload") public String upload (@RequestParam("email") String email, @RequestParam("username") String username, @RequestPart("headerImg") MultipartFile headerImg, @RequestPart("photos") MultipartFile[] photos) throws IOException { log.info("上传的信息:email={},username={},headerImg={},photos={}" , email,username,headerImg.getSize(),photos.length); if (!headerImg.isEmpty()){ String originalFilename = headerImg.getOriginalFilename(); headerImg.transferTo(new File ("H:\\cache\\" +originalFilename)); } if (photos.length > 0 ){ for (MultipartFile photo : photos) { if (!photo.isEmpty()){ String originalFilename = photo.getOriginalFilename(); photo.transferTo(new File ("H:\\cache\\" +originalFilename)); } } } return "main" ; }
3.自动配置原理 **文件上传自动配置类-MultipartAutoConfiguration-**MultipartProperties
FileCopyUtils 。实现文件流的拷贝
2.12异常处理 1.错误处理
默认情况下,Spring Boot提供/error
处理所有错误的映射
对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据