学习笔记【SpingBoot-第二节:数据处理及原理】

本文详细介绍了SpringBoot中数据处理的各个环节,包括YAML配置、Web开发中的静态资源处理、请求参数解析、REST原理、请求映射机制、参数处理原理,以及数据响应和内容协商。此外,还探讨了视图解析与模板引擎Thymeleaf的使用。

YAML

是 “YAML Ain’t Markup Language”(YAML 不是一种标记语言)的递归缩写。

在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)。

非常适合用来做以数据为中心的配置文件

语法

  • key: value;kv之间有空格
  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用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

配置提示:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

Web开发

静态资源目录
只要静态资源放在类路径下: called /static (or /public or /resources or /META-INF/resources

访问 : 当前项目根路径/ + 静态资源名

原理: 静态映射/**。

请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面

spring:
  指定访问静态资源的路径
  mvc:
    static-path-pattern: /res/**
  改变默认的静态资源路径
  resources:
    static-locations: [classpath:/haha/]

自定义 Favicon
favicon.ico 放在静态资源目录下即可。

禁用所有静态资源规则

resources:
    add-mappings: false 

请求参数处理

rest使用与原理

@xxxMapping
Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)

  • 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
  • 现在: /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
    • 核心Filter;HiddenHttpMethodFilter
    • 用法: 表单method=post,隐藏域 _method=put
    • SpringBoot中手动开启
spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true

Rest原理(表单提交要使用REST的时候)

  • 表单提交会带上_method=PUT
  • 请求过来被HiddenHttpMethodFilter拦截
  • 请求是否正常,并且是POST
    • 获取到_method的值。
    • 兼容以下请求;PUT.DELETE.PATCH
    • 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
    • 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的
@GetMapping("/user")
public String getUser(){
    return "GET-张三";
}

@PostMapping("/user")
public String postUser(){
    return "Post-张三";
}

@PutMapping("/user")
public String putUser(){
    return "PUT-张三";
}
@DeleteMapping("/user")
public String deleteUser(){
    return "Delete-张三";
}

扩展:如何把_method 这个名字换成我们自己喜欢的:

 @Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
    HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
    methodFilter.setMethodParam("_m");
    return methodFilter;
}
请求映射原理

在这里插入图片描述
SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet-》doDispatch()

RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射规则。

所有的请求映射都在HandlerMapping中。

  • SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
  • SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
    • 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
    • 如果有就找到这个请求对应的handler
    • 如果没有就是下一个 HandlerMapping
  • 我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping

参数处理

路径变量@PathVariable
@PathVariable(“id”),
@PathVariable Map<String,String> pv,

获取请求头@RequestHeader
@RequestHeader(“User-Agent”) String userAgent,
@RequestHeader Map<String,String> header,

获取请求参数@RequestParam
@RequestParam(“age”) Integer age,
@RequestParam(“inters”) List inters,
@RequestParam Map<String,String> params,

获取Cookie值@CookieValue
@CookieValue("_ga") String _ga,
@CookieValue("_ga") Cookie cookie

获取请求体@RequestBody
@RequestBody String content

获取request域属性

@GetMapping("/goto")
public String goToPage(HttpServletRequest request){
    request.setAttribute("msg","成功了。。。");
    request.setAttribute("code",200);
    return "forward:/success";
}

@ResponseBody
@GetMapping("/success")
public Map success(@RequestAttribute("msg") String msg,
                      @RequestAttribute("code") Integer code,
                      HttpServletRequest request){
    Object msg1 = request.getAttribute("msg");
    Map<String,Object> map=new HashMap<>();
    map.put("req_msg",msg1);
    map.put("anno_msg",msg);
    return map;
}

矩阵变量
语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd

  • SpringBoot默认是禁用了矩阵变量的功能
  • 手动开启:原理。对于路径的处理。UrlPathHelper进行解析。
  • removeSemicolonContent(移除分号内容)支持矩阵变量的
  • 矩阵变量必须有url路径变量才能被解析
@Configuration(proxyBeanMethods = false)
public class MyConfig/* implements WebMvcConfigurer*/ {
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {\
                UrlPathHelper urlPathHelper=new UrlPathHelper();
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }
        };
    }
 
    /*@Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper=new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);
        configurer.setUrlPathHelper(urlPathHelper);
    }*/
}

// /boss/1;age=20/2;age=10

 // /boss/1;age=20/2;age=10
@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;
}

复杂参数:

  • Map、Model(map、model里面的数据会被放在request的请求域 request.setAttribute)
  • RedirectAttributes( 重定向携带数据)
  • ServletResponse(response)
  • 等等
参数处理原理
  • HandlerMapping中找到能处理请求的Handler(Controller.method())
  • 为当前Handler 找一个适配器 HandlerAdapter;RequestMappingHandlerAdapter
  • 适配器执行目标方法并确定方法参数的每一个值
  • 参数解析器-HandlerMethodArgumentResolver

源码原理:

  • 1.HandlerAdapter

  • 2.执行目标方法

  • 3.参数解析器-HandlerMethodArgumentResolver

    • 确定将要执行的目标方法的每一个参数的值是什么;
    • SpringMVC目标方法能写多少种参数类型。取决于参数解析器。
  • 4.返回值处理器

  • 5.确定目标方法每一个参数的值

    • 挨个判断所有参数哪个解析器支持解析这个参数
    • 解析这个参数的值
    • 自定义类型参数 封装POJO ,ServletModelAttributeMethodProcessor 这个参数处理器支持
      • WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
      • WebDataBinder :web数据绑定器,将请求参数的值绑定到指定的JavaBean里面
      • WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中
      • GenericConversionService:在设置每一个值的时候,找它里面的所有converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型(JavaBean – Integer)
      • 未来我们可以给WebDataBinder里面放自己的Converter
 //1、WebMvcConfigurer定制化SpringMVC的功能
 @Bean
 public WebMvcConfigurer webMvcConfigurer(){
     return new WebMvcConfigurer() {
         @Override
         public void configurePathMatch(PathMatchConfigurer configurer) {
             UrlPathHelper urlPathHelper = new UrlPathHelper();
             // 不移除;后面的内容。矩阵变量功能就可以生效
             urlPathHelper.setRemoveSemicolonContent(false);
             configurer.setUrlPathHelper(urlPathHelper);
         }

         @Override
         public void addFormatters(FormatterRegistry registry) {
             registry.addConverter(new Converter<String, Pet>() {

                 @Override
                 public Pet convert(String source) {
                     // 啊猫,3
                     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;
                 }
             });
         }
     };
 }
  • 6.目标方法执行完成
    • 将所有的数据都放在 ModelAndViewContainer;包含要去的页面地址View。还包含Model数据。
  • 7.处理派发结果
    • processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
      renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);

数据响应与内容协商

响应json

jackson.jar+@ResponseBody

@Controller
public class ResponseTestController {
    @ResponseBody
    @GetMapping("/test/person")
    public Person getPerson(){
        Person person=new Person();
        person.setAge(28);
        person.setBirth(new Date());
        person.setUsername("zhangsan");
        return person;
    }
}

1、返回值解析器
2、返回值解析器原理

  • 1、返回值处理器判断是否支持这种类型返回值 supportsReturnType
  • 2、返回值处理器调用 handleReturnValue 进行处理
  • 3、RequestResponseBodyMethodProcessor 可以处理返回只标了@ResponseBody 注解的。
    • 利用 MessageConverters 进行处理 将数据写为json
      • 1、内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)
      • 2、服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据
      • 3、SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理?HttpMessageConverter: 看是否支持将 此 Class类型的对象,转为MediaType类型的数据。例子:Person对象转为JSON。或者 JSON转为Person
        • 得到MappingJackson2HttpMessageConverter可以将对象写为json
        • 利用MappingJackson2HttpMessageConverter将对象转为json再写出去。

默认的MessageConverter

  • 只支持Byte类型的
  • String
  • String
  • Resource
  • ResourceRegion
  • DOMSource.class \ SAXSource.class) \ StAXSource.class \StreamSource.class \Source.class
  • MultiValueMap
  • true
  • true
  • 支持注解方式xml处理的。

SpringMVC到底支持哪些返回值

  • ModelAndView
  • Model
  • View
  • ResponseEntity
  • ResponseBodyEmitter
  • StreamingResponseBody
  • HttpEntity
  • HttpHeaders
  • Callable
  • DeferredResult
  • ListenableFuture
  • CompletionStage
  • WebAsyncTask
  • 有 @ModelAttribute 且为对象类型的
  • @ResponseBody 注解 —> RequestResponseBodyMethodProcessor;
内容协商

根据客户端接收能力不同,返回不同媒体类型的数据。
1、引入xml依赖

 <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

2、分别返回json和xml
只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。

3、开启浏览器参数方式内容协商功能
为了方便内容协商,开启基于请求参数的内容协商功能。

spring:
    contentnegotiation:
      favor-parameter: true  #开启请求参数内容协商模式

4、内容协商原理

  • 1、判断当前响应头中是否已经有确定的媒体类型。MediaType
  • 2、获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)【application/xml】
    • contentNegotiationManager 内容协商管理器 默认使用基于请求头的策略
    • HeaderContentNegotiationStrategy 确定客户端可以接收的内容类型
  • 3、遍历循环所有当前系统的 MessageConverter,看谁支持操作这个对象(Person)
  • 4、找到支持操作Person的converter,把converter支持的媒体类型统计出来。
  • 5、客户端需要【application/xml】。服务端能力【10种、json、xml】
  • 6、进行内容协商的最佳匹配媒体类型
  • 7、用 支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。

5、自定义 MessageConverter
实现多协议数据兼容。json、xml、x-guigu

  • @ResponseBody 响应数据出去 调用 RequestResponseBodyMethodProcessor 处理
  • Processor 处理方法返回值。通过 MessageConverter 处理
  • 所有 MessageConverter 合起来可以支持各种媒体类型数据的操作(读、写)
  • 内容协商找到最终的 messageConverter;
@Bean
public WebMvcConfigurer webMvcConfigurer(){
     return new WebMvcConfigurer() {
         @Override
         public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {

         }
     }
 }

视图解析与模板引擎

视图解析:SpringBoot默认不支持 JSP,需要引入第三方模板引擎技术实现页面渲染。

视图解析原理流程

1、目标方法处理的过程中,所有数据都会被放在 ModelAndViewContainer 里面。包括数据和视图地址

2、方法的参数是一个自定义类型对象(从请求参数中确定的),把他重新放在 ModelAndViewContainer

3、任何目标方法执行完成以后都会返回 ModelAndView(数据和视图地址)。

4、processDispatchResult 处理派发结果(页面改如何响应)

  • render(mv, request, response); 进行页面渲染逻辑
  • 根据方法的String返回值得到 View 对象【定义了页面的渲染逻辑】
    • 所有的视图解析器尝试是否能根据当前返回值得到View对象
    • 得到了 redirect:/main.html --> Thymeleaf new RedirectView()
    • ContentNegotiationViewResolver 里面包含了下面所有的视图解析器,内部还是利用下面所有视图解析器得到视图对象。
    • view.render(mv.getModelInternal(), request, response); 视图对象调用自定义的render进行页面渲染工作
      • RedirectView 如何渲染【重定向到一个页面】
      • 1、获取目标url地址
      • 2、response.sendRedirect(encodedURL);

视图解析:
• 返回值以 forward: 开始: new InternalResourceView(forwardUrl); --> 转发request.getRequestDispatcher(path).forward(request, response);
• 返回值以 redirect: 开始: new RedirectView() --》 render就是重定向
• 返回值是普通字符串: new ThymeleafView()—>自定义视图解析器+自定义视图

模板引擎-Thymeleaf

1、thymeleaf简介
Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text.
现代化、服务端Java模板引擎
2、基本语法
1、表达式

表达式名字语法用途
变量取值${…}获取请求域、session域、对象等值
选择变量*{…}获取上下文对象值
消息#{…}获取国际化等值
链接@{…}生成链接
片段表达式~{…}jsp:include 作用,引入公共页面片段

2、字面量
文本值: ‘one text’ , ‘Another one!’ ,…数字: 0 , 34 , 3.0 , 12.3 ,…布尔值: true , false
空值: null
变量: one,two,… 变量不能有空格

3、文本操作
字符串拼接: +
变量替换: |The name is ${name}|

4、数学运算
运算符: + , - , * , / , %

5、布尔运算
运算符: and , or
一元运算: ! , not

6、比较运算
比较: > , < , >= , <= ( gt , lt , ge , le )等式: == , != ( eq , ne )

7、条件运算
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)

8、特殊操作
无操作: _

3、设置属性值-th:attr
设置单个值

<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>

设置多个值

<img src="../../images/gtvglogo.png"  th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />

以上两个的代替写法 th:xxxx

<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>
<form action="subscribe.html" th:action="@{/subscribe}">

所有h5兼容的标签写法
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#setting-value-to-specific-attributes

4、迭代

<tr th:each="prod : ${prods}">
        <td th:text="${prod.name}">Onions</td>
        <td th:text="${prod.price}">2.41</td>
        <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
  <td th:text="${prod.name}">Onions</td>
  <td th:text="${prod.price}">2.41</td>
  <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>

5、条件运算

<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">view</a>
<div th:switch="${user.role}">
  <p th:case="'admin'">User is an administrator</p>
  <p th:case="#{roles.manager}">User is a manager</p>
  <p th:case="*">User is some other thing</p>
</div>

thymeleaf使用

1、引入Starter

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

2、自动配置好了thymeleaf

@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";  //xxx.html

3、页面开发

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 th:text="${msg}">哈哈</h1>
<h2>
    <a href="www.atguigu.com" th:href="${link}">去百度</a>  <br/>
    <a href="www.atguigu.com" th:href="@{link}">去百度2</a>
</h2>
</body>
</html>

开发小技巧:
静态资源处理

  • 自动配置好,我们只需要把所有静态资源放到 static 文件夹下

路径构建

  • th:action="@{/login}"

模板抽取

  • th:insert/replace/include

页面跳转

@PostMapping("/login")
public String main(User user, HttpSession session, Model model){
    if(StringUtils.hasLength(user.getUserName()) && "123456".equals(user.getPassword())){
        //把登陆成功的用户保存起来
        session.setAttribute("loginUser",user);
        //登录成功重定向到main.html;  重定向防止表单重复提交
        return "redirect:/main.html";
    }else {
        model.addAttribute("msg","账号密码错误");
        //回到登录页面
        return "login";
    }
}

数据渲染

@GetMapping("/dynamic_table")
public String dynamic_table(Model model){
    //表格内容的遍历
    List<User> users = Arrays.asList(new User("zhangsan", "123456"),
            new User("lisi", "123444"),
            new User("haha", "aaaaa"),
            new User("hehe ", "aaddd"));
    model.addAttribute("users",users);
    return "table/dynamic_table";
}
<table class="display table table-bordered" id="hidden-table-info">
  <thead>
  <tr>
      <th>#</th>
      <th>用户名</th>
      <th>密码</th>
  </tr>
  </thead>
  <tbody>
  <tr class="gradeX" th:each="user,stats:${users}">
      <td th:text="${stats.count}">Trident</td>
      <td th:text="${user.userName}">Internet</td>
      <td >[[${user.password}]]</td>
  </tr>
  </tbody>
  </table>
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值