跳至主要內容

SpringMVC开发实战-2

wangdx大约 18 分钟

@InitBinder

请求参数处理

  • 参数的传递是动态 WEB 最为重要的操作特点,SpringMVC 是构建在 JakartaEE 服务标准之上的应用,这样所有接收到的请求参数在接收时对应的数据类型全部为 StringSprina 会根据当前方法参数中的数据类型进行数据的转换处理,这样的处理机制,使开发者避免了重复性的数据转型处理,使得参数的接收更加的方便。

WebDataBinder 数据绑定

  • 在 Spring 中为了便于数据的转换提供了 ConversionService 转换服务接口,该接口提供了常见的数据转换服务,但是由于不同的应用可能处于不同的国家,所以对于日期以及日期时间的转换操作,就需要由开发者进行配置,为了解决这一问题,“@InitBinder”注解,该注解结合 WebDataBinder 实例,可以由 SpringMVC 提供了开发者自定义日期或日期时间的转换处理操作
1、
<%@ page pageEncoding="UTF-8"%>
<html>
    <head><title>SpringMVC开发框架</title></head>
    <body>
        <form action="${request.contextPath}/pages/message/echo" method="post">
            消息内容:   <input type="text" name="msg" value="www.yootk.com"><br>
            消息级别:   <select id="level" name="level">
                            <option value="0">紧急</option>
                            <option value="1">普通</option>
                            <option value="2">延迟</option>
                        </select><br>
            发布日期:   <input type="date" id="pubdate" name="pubdate" value="2262-01-21"><br>
            消息标签:    <input type="checkbox" name="tags" id="tags" value="政治" checked>政治
                        <input type="checkbox" name="tags" id="tags" value="经济" checked>经济
                        <input type="checkbox" name="tags" id="tags" value="文化" checked>文化<br>
            <button type="submit">发送</button>
            <button type="reset">重置</button>
        </form>
    </body>
</html>

2、
package com.yootk.action;

import com.yootk.service.IMessageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller // 控制层注解
@RequestMapping("/pages/message/") // 父路径
public class MessageAction {
    private static final Logger LOGGER = LoggerFactory.getLogger(MessageAction.class);
    @Autowired
    private IMessageService messageService; // 业务接口实例
    @PostMapping("echo") // 映射地址
    public ModelAndView echo(String msg, int level,
                             java.util.Date pubdate) { // 自动实现数据转型
        LOGGER.info("消息回应处理,msg = {}", msg); // 日志输出
        LOGGER.info("消息回应处理,level = {}", level); // 日志输出
        LOGGER.info("消息回应处理,pubdate = {}", pubdate); // 日志输出
        return null; // 路径跳转
    }
    @GetMapping("input") // 映射地址
    public String input() {
        return "/pages/message/input.jsp";
    }
}



3、
http://localhost/pages/message/input

4、
package com.yootk.action.abs;

import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;

import java.beans.PropertyEditorSupport;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

public abstract class AbstractAction {
    // 如果发现父类格式不好,那么子类可以自己扩充绑定的配置,子类配置优先生效
    private static final DateTimeFormatter LOCAL_DATE_FORMAT =
            DateTimeFormatter.ofPattern("yyyy-MM-dd");
    @InitBinder // 添加初始化绑定注解
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(java.util.Date.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                LocalDate localDate = LocalDate.parse(text, LOCAL_DATE_FORMAT);
                Instant instant = localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
                super.setValue(java.util.Date.from(instant));
            }
        });
    }
}

@ModelAttribute

控制层开发

  • 控制层是连接用户请求与业务处理的核心结构,在传统的控制层开发中,除了要进行有效的业务处理之外,还要配置视图跳转路径,会使得控制层的方法变得庞大,从而造成代码维护的闲难
  • 虽然 Spring 的内部已经尽可能的对控制层代码进行优化,但是在控制层的方法内部依然需要进行参数的接收,业务调用以及视图跳转的处理,为了可以进一步进行结构上的拆分 SprinaMVC 中可以在一个请求内使用专属的方法进行业务调用,再使用另外-个方法进行视图跳转,而作为请求处理的操作方法中就需要使用@ModelAttribute 注解进行配置。

被@ModelAttribute 注释的方法会在此 controller 每个方法执行前被执行。因此对于一个 controller 映射多个 URL 的用法来说,要谨慎使用。

1、
package com.yootk.action;

import com.yootk.action.abs.AbstractAction;
import com.yootk.service.IMessageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller // 控制层注解
@RequestMapping("/pages/message/") // 父路径
public class MessageAction extends AbstractAction {
    private static final Logger LOGGER = LoggerFactory.getLogger(MessageAction.class);
    @Autowired
    private IMessageService messageService; // 业务接口实例
    @PostMapping("echo") // 映射地址
    public ModelAndView echo(String msg, int level,
                             java.util.Date pubdate) { // 自动实现数据转型
        ModelAndView mav = new ModelAndView("/pages/message/show.jsp");
        mav.addObject("msg", msg);
        mav.addObject("level", level);
        mav.addObject("pubdate", pubdate);
        return mav; // 路径跳转
    }
    @GetMapping("input") // 映射地址
    public String input() {
        return "/pages/message/input.jsp";
    }
}


2、
package com.yootk.action;

import com.yootk.action.abs.AbstractAction;
import com.yootk.service.IMessageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller // 控制层注解
@RequestMapping("/pages/message/") // 父路径
public class MessageAction extends AbstractAction {
    private static final Logger LOGGER = LoggerFactory.getLogger(MessageAction.class);
    @Autowired
    private IMessageService messageService; // 业务接口实例
    @ModelAttribute
    public void echoHandle(String msg, int level,
                                   java.util.Date pubdate, Model model) { // 业务的处理
        model.addAttribute("msg", msg);
        model.addAttribute("level", level);
        model.addAttribute("pubdate", pubdate);
    }
    @PostMapping("echo") // 映射地址
    public String echo() { // 负责跳转
        return "/pages/message/show.jsp";
    }
    @GetMapping("input") // 映射地址
    public String input() {
        return "/pages/message/input.jsp";
    }
}

RedirectAttributes

RedirectAttributes

  • 在 JavaWEB 开发中为了便于不同路径之间的访问链接,提供有两种跳转机制,分别是服务端跳转(forward:/路径)和客户端跳转(redirect:/)SpringMVC 中也支持有这种跳转的处理,即:可以由一个控制器跳转到另外一个控制器,在默认情况下采用服务端跳转操作可以继续传递 request 属性,而采用客户端跳转操作将无法传递 request 属性。

RedirectAttributes

  • 为了解决客户端跳转之后参数传递问题,在 SpringMVC 3.1 版本之后提供了一个 RedirectAttributes 接口(该接口为 Model 子接)。利用该接口提供的方法,可以实现跳转路径的显式参数(在地址栏后面显示传递参数内容)与隐式参数的两种传递形式下面通过一个具体的案例对这一机制进行说明。
1、
<%@ page pageEncoding="UTF-8"%>
<html>
    <head><title>SpringMVC开发框架</title></head>
    <body>
        <h1>name = ${name}</h1>
        <h1>url = ${url}</h1>
    </body>
</html>

2、
package com.yootk.action;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@Controller
@RequestMapping("/pages/data/*")
public class DataAction {
    private static final Logger LOGGER = LoggerFactory.getLogger(DataAction.class);
    @RequestMapping("set_param")
    public String setParam(RedirectAttributes attributes) { // 属性的配置
        attributes.addAttribute("name", "沐言科技"); // 属性设置
        attributes.addAttribute("url", "yootk.com"); // 属性设置
        return "redirect:/pages/data/list_param"; // 跳转到其他控制层方法
    }
    @RequestMapping("list_param")
    public String listParam(String name, String url) {
        LOGGER.info("【Redirect参数接收】name = {}、url = {}", name, url);
        return "/pages/data/show.jsp";
    }
}


3、
localhost/pages/data/set_param

4、
localhost/pages/data/set_flash

请求参数与对象转换

请求参数与对象转换

  • 一个标准的 WEB 程序开发中,为了保证项目的可维护性,一定都会采用分层设计架构这样对于用户发送的请求数据,往往会将其以对象的形式进行传递处理,控制层会将一转为指定类型的对象实例。由于后台业系列相关的请求参数转,通过 Java 的反射机制,务层和数据层之间也是以对象为基础进行传输,这这样在数据层操作时,可以直接结合 ORMapping 组件,以完成最终的业务逻辑处理。

参数与对象级联配置

  • SpringMVC 中为了便于对象的使用,可以自动的将请求参数直接转化为对象的形式进行注入,这样就可以继续使用 Spring 中的数据类型转换处理,并减少了实例化对象的属性设置操作,使得代码的开发更加简化
1、
<%@ page pageEncoding="UTF-8"%>
<html>
    <head><title>SpringMVC开发框架</title></head>
    <body>
        <form action="${request.contextPath}/pages/emp/add" method="post">
            雇员编号:<input type="text" name="empno" value="7369"><br>
            雇员姓名:<input type="text" name="ename" value="李兴华"><br>
            雇佣日期:<input type="date" name="hiredate" value="2050-09-19"><br>
            部门编号:<input type="text" name="dept.deptno" value="10"><br>
            部门名称:<input type="text" name="dept.dname" value="教学研发部"><br>
            部门位置:<input type="text" name="dept.loc" value="洛阳"><br>
            <button type="submit">发送</button><button type="reset">重置</button>
        </form>
    </body>
</html>

2、
<%@ page pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<html>
    <head><title>SpringMVC开发框架</title></head>
    <body>
        【雇员信息】编号:${emp.empno}、姓名:${emp.ename}、
            雇佣日期:<fmt:formatDate value="${emp.hiredate}" pattern="yyyy年MM月dd日"/><br>
        【部门信息】编号:${emp.dept.deptno}、名称:${emp.dept.dname}、位置:${emp.dept.loc}
    </body>
</html>

3、
package com.yootk.vo;

public class Dept {
    private Long deptno;
    private String dname;
    private String loc;
    public Long getDeptno() {
        return deptno;
    }
    public void setDeptno(Long deptno) {
        this.deptno = deptno;
    }
    public String getDname() {
        return dname;
    }
    public void setDname(String dname) {
        this.dname = dname;
    }
    public String getLoc() {
        return loc;
    }
    public void setLoc(String loc) {
        this.loc = loc;
    }
}


4、
package com.yootk.vo;

import java.util.Date;

public class Emp {
    private Long empno;
    private String ename;
    private java.util.Date hiredate;
    private Dept dept;
    public Long getEmpno() {
        return empno;
    }
    public void setEmpno(Long empno) {
        this.empno = empno;
    }
    public String getEname() {
        return ename;
    }
    public void setEname(String ename) {
        this.ename = ename;
    }
    public Date getHiredate() {
        return hiredate;
    }
    public void setHiredate(Date hiredate) {
        this.hiredate = hiredate;
    }
    public Dept getDept() {
        return dept;
    }
    public void setDept(Dept dept) {
        this.dept = dept;
    }
}


5、
package com.yootk.action;

import com.yootk.action.abs.AbstractAction;
import com.yootk.vo.Emp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/pages/emp/*")
public class EmpAction extends AbstractAction { // 父类存在有日期转换支持
    private static final Logger LOGGER = LoggerFactory.getLogger(EmpAction.class);
    @GetMapping("add_input")
    public String addInput() {  // 跳转到表单输入页
        return "/pages/emp/emp_add.jsp";
    }
    @PostMapping("add")
    public ModelAndView add(Emp emp) { // 所有的参数自动转为对象的成员属性
        LOGGER.info("【雇员信息】编号:{}、姓名:{}、雇佣日期:{}",
                emp.getEmpno(), emp.getEname(), emp.getHiredate());
        LOGGER.info("【部门信息】编号:{}、名称:{}、位置:{}",
                emp.getDept().getDeptno(), emp.getDept().getDname(), emp.getDept().getLoc());
        ModelAndView mav = new ModelAndView("/pages/emp/emp_add_show.jsp"); // 配置跳转路径
        mav.addObject("emp", emp); // 向页面传递属性内容
        return mav;
    }
}


6、
localhost/pages/emp/add_input

@RequestBody

JSON 数据传输

  • 随着 WEB 技术的发展,控制层中的数据接收不仅仅来自于表单的参数,还有可能是接收一个完整的 JSON 数据,这些数据可能来自于专属的前端项目,也有可能是其他用户所发送来的请求。这个时候就需要根据 JSON 的数据结构来进行对象参数的配置。
1、
// https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core
implementation('com.fasterxml.jackson.core:jackson-core:2.13.4')
// https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
implementation('com.fasterxml.jackson.core:jackson-databind:2.13.4')
// https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations
implementation('com.fasterxml.jackson.core:jackson-annotations:2.13.4')


2、
package com.yootk.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

import java.util.List;

@Configuration
public class JacksonConfig { // JSON配置类
    @Bean // 进行请求映射的处理
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
        RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
        MappingJackson2HttpMessageConverter converter =
                new MappingJackson2HttpMessageConverter();
        converter.setSupportedMediaTypes(List.of(MediaType.APPLICATION_JSON)); // MIME类型
        adapter.setMessageConverters(List.of(converter)); // 添加转换器
        return adapter;
    }
}


3、
package com.yootk.action;

import com.yootk.action.abs.AbstractAction;
import com.yootk.vo.Emp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;

@Controller
@RequestMapping("/pages/emp/*")
public class EmpAction extends AbstractAction { // 父类存在有日期转换支持
    private static final Logger LOGGER = LoggerFactory.getLogger(EmpAction.class);

    @PostMapping("add")
    public ModelAndView add(@RequestBody Emp emp) {
        // @RequestBody注解主要的目的是告诉控制层,此时传输的内容是一个完整的JSON数据
        // 该数据需要通过Jackson组件转换之后才可以使用
        LOGGER.info("【雇员信息】编号:{}、姓名:{}、雇佣日期:{}",
                emp.getEmpno(), emp.getEname(), emp.getHiredate());
        LOGGER.info("【部门信息】编号:{}、名称:{}、位置:{}",
                emp.getDept().getDeptno(), emp.getDept().getDname(), emp.getDept().getLoc());
        return null; // 只关注后台的日志输出
    }

    @PostMapping("array")
    public ModelAndView array(@RequestBody List<Emp> all) { // 接收一组雇员信息
        for (Emp emp : all) { // 数据迭代
            LOGGER.info("【雇员信息】编号:{}、姓名:{}、雇佣日期:{}、" +
                            "部门编号:{}、部门名称:{}、部门位置:{}",
                    emp.getEmpno(), emp.getEname(), emp.getHiredate(),
                    emp.getDept().getDeptno(), emp.getDept().getDname(), emp.getDept().getLoc());
        }
        return null; // 只关注后台的日志输出
    }
}


4、
curl -X POST "http://localhost/pages/emp/add" -H "Content-Type: application/json;charset=utf-8" -d "{\"empno\": \"7369\", \"ename\": \"Lee\", \"hiredate\": \"2050-09-19\", \"dept\": {\"deptno\": \"10\", \"dname\": \"Edu.Tec\", \"loc\": \"LuoYang\"}}"


5、
curl -X POST "http://localhost:80/pages/emp/array" -H "Content-Type: application/json;charset=utf-8" -d "[{\"empno\": \"7369\", \"ename\": \"Happy\", \"hiredate\": \"2050-09-19\", \"dept\": {\"deptno\": \"10\", \"dname\": \"Edu.Tec\", \"loc\": \"LuoYang\"}}, {\"empno\": \"7566\", \"ename\": \"Hello\", \"hiredate\": \"2060-07-27\", \"dept\": {\"deptno\": \"10\", \"dname\": \"Edu.Tec\", \"loc\": \"BeiJing\"}}]"

@ResponseBody

@ResponseBody 与数据响应

  • 传统的 WEB 开发之中,控制层一般会跳转到指定的 JSP 页面进行数据的展示,但是考虑到 Ajax 的异步调用处理,所以在 SpringMVC 中控制层可以直接将用户的请求以 JSON 数据的形式返回,此时就可以在控制层方法定义时,使用@ResponseBodv 注解进行标记
1、

package com.yootk.action;

import com.yootk.action.abs.AbstractAction;
import com.yootk.vo.Dept;
import com.yootk.vo.Emp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.*;

@Controller
@RequestMapping("/pages/emp/*")
public class EmpAction extends AbstractAction { // 父类存在有日期转换支持
    private static final Logger LOGGER = LoggerFactory.getLogger(EmpAction.class);
    @GetMapping("get")
    @ResponseBody // 直接进行JSON响应(将对象转为JSON)
    public Object get() { // 直接进行对象的返回
        return new Emp(7369L, "李兴华", new Date(),
                new Dept(10L, "沐言科技教学研发部", "洛阳"));
    }
    @GetMapping("list")
    @ResponseBody
    public Object list() {
        List<Emp> all = new ArrayList<>();
        for (int x = 0 ; x < 3 ; x ++) {
            Emp emp = new Emp(7369L + x, "李兴华 - " + x, new Date(),
                    new Dept(10L, "沐言科技教学研发部", "洛阳"));
            all.add(emp);
        }
        return all;
    }
    @GetMapping("map")
    @ResponseBody
    public Object map() {
        Map<String, Object> map = new HashMap<>();
        map.put("data", new Emp(7369L, "李兴华", new Date(),
                new Dept(10L, "沐言科技教学研发部", "洛阳")));
        map.put("skill", Set.of("Java", "Python", "Golang"));
        return map;
    }
}

2、
http://localhost/pages/emp/get

3、
http://localhost/pages/emp/list

4、
http://localhost/pages/emp/map

5、

package com.yootk.config.mapper;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.text.SimpleDateFormat;
import java.util.TimeZone;

public class CustomObjectMapper extends ObjectMapper { // 自定义的配置类
    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd"; // 日期格式
    public CustomObjectMapper() {
        super.setDateFormat(new SimpleDateFormat(DEFAULT_DATE_FORMAT)); // 日期格式化
        super.configure(SerializationFeature.INDENT_OUTPUT, true); // 格式化输出
        super.setSerializationInclusion(JsonInclude.Include.NON_NULL); // NULL不参与序列化
        super.setTimeZone(TimeZone.getTimeZone("GMT+8:00")); // 配置时区
    }
}

6、
package com.yootk.context.config;

import com.yootk.config.mapper.CustomObjectMapper;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;

import java.util.List;

@Configuration
@EnableWebMvc // 加入此注解才表示WEBMVC配置类有效
@ComponentScan("com.yootk.action")
public class SpringWEBContextConfig implements WebMvcConfigurer { // WEB上下文配置类
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) { // 矩阵参数的配置
        var urlPathHelper = new UrlPathHelper(); // 配置一个对象
        urlPathHelper.setRemoveSemicolonContent(false); // 启用矩阵参数接收
        configurer.setUrlPathHelper(urlPathHelper); // 配置路径参数
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 可以配置所有的消息转换器
        MappingJackson2HttpMessageConverter converter =
                new MappingJackson2HttpMessageConverter();
        CustomObjectMapper objectMapper = new CustomObjectMapper(); // 对象的映射转换处理配置
        converter.setObjectMapper(objectMapper);
        converters.add(converter); // 追加转换器
    }
}

RequestContextHolder

RequestContextHolder

  • JakartaEE 的开发规范中,为了便于程序与 HTTP 协议的处理,提供了内置对象的设计概念,在程序的开发中最为核心的内置对象分别为 request(可以获取 session 与 application)与 response,考虑到内置对象的使用问题,在 SpringMVC 中提供了一个 RequestContextHolder 处理类,该类可以获取到 RequestAttributes 接口实例,而该接口的子类中就提供了内置对象的获取方法
1、
package com.yootk.action;

import com.yootk.action.abs.AbstractAction;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.annotation.MapMethodProcessor;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/pages/web/*")
public class InnerObjectAction extends AbstractAction {
    private static final Logger LOGGER = LoggerFactory.getLogger(InnerObjectAction.class);

    @GetMapping("object")
    @ResponseBody // 直接进行REST响应
    public Object object() {
        Map<String, Object> map = new HashMap<>();// 通过Map包装返回的结果
        ServletRequestAttributes attributes = (ServletRequestAttributes)
                RequestContextHolder.getRequestAttributes(); // 获取Servlet属性信息
        HttpServletRequest request = attributes.getRequest(); // 获取Request对象实例
        HttpServletResponse response = attributes.getResponse(); // 获取Response对象实例
        HttpSession session = request.getSession(); // 获取Session对象
        ServletContext context = request.getServletContext(); // 获取Application对象
        map.put("【request】ContextPath", request.getContextPath());
        map.put("【response】Locale", response.getLocale());
        map.put("【session】SessionId", session.getId());
        map.put("【application】ResponseCharacterEncoding",
                context.getResponseCharacterEncoding());
        LOGGER.debug("获取内置对象的信息:{}", map);
        return map; // 直接响应
    }
}


2、
http://localhost/pages/web/object

3、
package com.yootk.action;

import com.yootk.action.abs.AbstractAction;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/pages/web/*")
public class InnerObjectAction extends AbstractAction {
    private static final Logger LOGGER = LoggerFactory.getLogger(InnerObjectAction.class);

    @GetMapping("object")
    @ResponseBody // 直接进行REST响应
    public Object object() {
        Map<String, Object> map = new HashMap<>();// 通过Map包装返回的结果
        ServletRequestAttributes attributes = (ServletRequestAttributes)
                RequestContextHolder.getRequestAttributes(); // 获取Servlet属性信息
        HttpServletRequest request = attributes.getRequest(); // 获取Request对象实例
        HttpServletResponse response = attributes.getResponse(); // 获取Response对象实例
        HttpSession session = request.getSession(); // 获取Session对象
        ServletContext context = request.getServletContext(); // 获取Application对象
        map.put("【request】ContextPath", request.getContextPath());
        map.put("【response】Locale", response.getLocale());
        map.put("【session】SessionId", session.getId());
        map.put("【application】ResponseCharacterEncoding",
                context.getResponseCharacterEncoding());
        LOGGER.debug("获取内置对象的信息:{}", map);
        return map; // 直接响应
    }
    @GetMapping("action")
    @ResponseBody // 直接进行REST响应
    public Object action(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
        Map<String, Object> map = new HashMap<>();// 通过Map包装返回的结果
        ServletContext context = request.getServletContext(); // 获取Application对象
        map.put("【request】ContextPath", request.getContextPath());
        map.put("【response】Locale", response.getLocale());
        map.put("【session】SessionId", session.getId());
        map.put("【application】ResponseCharacterEncoding",
                context.getResponseCharacterEncoding());
        LOGGER.debug("获取内置对象的信息:{}", map);
        return map; // 直接响应
    }
}


4、
http://localhost/pages/web/action

@RequestHeader

用户请求与响应

  • 在 HTTP 协议之中包含有头信息和数据信息两个组成部分,每次在用户发送请求时都会自动进行请求头信息的发送,而在响应时也可以进行头信息的设置处理
1、
package com.yootk.action;

import com.yootk.action.abs.AbstractAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/pages/header/*")
public class HeaderAction extends AbstractAction {
    private static final Logger LOGGER = LoggerFactory.getLogger(HeaderAction.class);
    @GetMapping(value = "get") // 一般都是省略value属性的
    @ResponseBody
    public Object get(
            @RequestHeader(name = "yootk", defaultValue = "www.yootk.com") String yootk,
            @RequestHeader("Accept-Encoding") String acceptEncoding,
            @RequestHeader("user-agent") String userAgent,
            @RequestHeader("cookie") String cookie) {
        // 如果按照以往的做法,此时的头信息的接收需要通过request内置对象才可以实现
        Map<String, String> map = new HashMap<>(); // 创建保存集合
        map.put("【HEADER】yootk", yootk);
        map.put("【HEADER】acceptEncoding", acceptEncoding);
        map.put("【HEADER】userAgent", userAgent);
        map.put("【HEADER】cookie", cookie);
        return map;
    }
}


2、
http://localhost/pages/header/get

3、
    @GetMapping("set")
    @ResponseBody // 如果你现在方法返回的不是路径结构,必须使用此注解
    public Object set(HttpServletResponse response) {
        response.setHeader("yootk", "沐言优拓:www.yootk.com");
        return "Set Header Success";
    }

4、
http://localhost/pages/header/set

@CookieValue

Cookie 数据

  • 在用户每次请求和响应时的头信息中实际上都会包含有 Cookie 的数据内容,在 SpringMVC 中除了可以使用传统 JavaWEB 中的 HttpServetRequest 获取 Cookie 数据之外,也可以在控制层的方法参数中使用@CookieValue 注解来获取指定名称 Cookie 的内容
1、
package com.yootk.action;

import com.yootk.action.abs.AbstractAction;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/pages/cookie/*")
public class CookieAction extends AbstractAction { // 进行Cookie操作的配置
    @GetMapping("get")
    @ResponseBody
    public Object get(
            @CookieValue(name = "yootk", defaultValue = "yootk.com") String yootk,
            @CookieValue(name = "jixianit", defaultValue = "jixianit.com") String jixianit,
            @CookieValue(name = "JSESSIONID", defaultValue = "YOOTK-DEFAULT") String id) {
        Map<String, String> map = new HashMap<>(); // 保存数据的接收结果
        map.put("【Cookie】yootk", yootk);
        map.put("【Cookie】jixianit", yootk);
        map.put("【Cookie】JSESSIONID", id);
        return map;
    }
}


2、
http://localhost/pages/cookie/get

3、
package com.yootk.action;

import com.yootk.action.abs.AbstractAction;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/pages/cookie/*")
public class CookieAction extends AbstractAction { // 进行Cookie操作的配置
    @GetMapping("get")
    @ResponseBody
    public Object get(
            @CookieValue(name = "yootk", defaultValue = "yootk.com") String yootk,
            @CookieValue(name = "jixianit", defaultValue = "jixianit.com") String jixianit,
            @CookieValue(name = "JSESSIONID", defaultValue = "YOOTK-DEFAULT") String id) {
        Map<String, String> map = new HashMap<>(); // 保存数据的接收结果
        map.put("【Cookie】yootk", yootk);
        map.put("【Cookie】jixianit", yootk);
        map.put("【Cookie】JSESSIONID", id);
        return map;
    }
    @GetMapping("set")
    @ResponseBody
    public Object set(HttpServletResponse response) {
        Cookie c1 = new Cookie("yootk", "www.yootk.com");
        Cookie c2 = new Cookie("jixianit", "www.jixianit.com");
        c1.setPath("/");
        c2.setPath("/");
        c1.setMaxAge(3600); // 单位:秒
        c2.setMaxAge(3600); // 单位:秒
        response.addCookie(c1);
        response.addCookie(c2);
        return "Cookie Set Success";
    }
}


4、
http://localhost/pages/cookie/set

session 管理

SpringMVC 中的 Session 属性操作

  • Session 是 WEB 开发之中的重要组成技术,在 WEB 容器中依靠 Session 来区分不同的用户请求,同时每一个 Session 都可以设置自己的属性,SpringMVC 的开发中开发者除了可以使用 HttpSession 接口提供的方法来进行 session 属性的操作,也可以基于 Spring 内部管理机制,使用“@SessionAttribute”或“@SessionAttributes”注解并结合 Model 或 ModelMap 进行 session 属性的操作
1、
package com.yootk.action;

import com.yootk.action.abs.AbstractAction;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;

import java.util.Map;
import java.util.Set;

@Controller
@RequestMapping("/pages/user/*")
@SessionAttributes({"user", "role"}) // 配置Session属性的名称
public class UserAction extends AbstractAction {
    @GetMapping("login") // 表示登录处理
    public String login(ModelMap model) {
        // 在Session属性范围之中配置两个内容,一个保存Map属性,另外一个保存Set属性
        model.addAttribute("user",
                Map.of("id", "yootk", "password", "HelloMuyan"));
        model.addAttribute("role",Set.of("company", "dept", "emp"));
        return "/pages/user/login_success.jsp"; // 直接跳转到视图页
    }
}


2、
<%@ page pageEncoding="UTF-8"%>
<html>
    <head><title>SpringMVC开发框架</title></head>
    <body>
        <h1>用户登录成功</h1>
        <h2>用户信息:${sessionScope.user}</h2>
        <h2>角色信息:${sessionScope.role}</h2>
    </body>
</html>

3、
localhost/pages/user/login

4、
package com.yootk;

import com.yootk.action.abs.AbstractAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/pages/session/*")
@SessionAttributes({"user", "role"}) // 配置的session属性
public class SessionAction extends AbstractAction {
    private static final Logger LOGGER = LoggerFactory.getLogger(SessionAction.class);
    @GetMapping("get")
    public ModelAndView get(ModelMap map) {
        LOGGER.info("【用户信息】{}", map.get("user"));
        LOGGER.info("【角色信息】{}", map.get("role"));
        return null;
    }
}


5、
localhost/pages/session/get


6、
package com.yootk.action;

import com.yootk.action.abs.AbstractAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/pages/session/*")
@SessionAttributes({"user", "role"}) // 配置的session属性
public class SessionAction extends AbstractAction {
    private static final Logger LOGGER = LoggerFactory.getLogger(SessionAction.class);
    @GetMapping("get")
    public ModelAndView get(ModelMap map) {
        LOGGER.info("【用户信息】{}", map.get("user"));
        LOGGER.info("【角色信息】{}", map.get("role"));
        return null;
    }
    @GetMapping("clean")
    public ModelAndView clean(Model model, SessionStatus status) {
        LOGGER.info("【用户信息】{}", model.getAttribute("user"));
        LOGGER.info("【角色信息】{}", model.getAttribute("role"));
        status.setComplete(); // 清除当前的session
        LOGGER.info("Session数据已被清除");
        return null;
    }
}


7、
localhost/pages/session/clean

文件上传支持

接收上传文件

  • 在用户请求处理中,除了普通的文本请求参数之外,实际上还会包含有二进制的文件数据,为了便于文件的接收管理,在 SpringMVC 中提供了一个 MultipartFile 接口,所有上传的文件都可以通过该接口实现相关信息的获取以及文件流的操作

1、
    @Override
    protected void customizeRegistration(ServletRegistration.Dynamic registration) {
        long maxFileSize = 2097152; // 单个文件上传的最大长度(2M)
        long maxRequestSize = 5242880; // 总的上传大小(5M)
        int fileSizeThreshold = 0; // 达到阈值写入磁盘
        MultipartConfigElement element = new MultipartConfigElement(
                null, maxFileSize, maxRequestSize, fileSizeThreshold);
        registration.setMultipartConfig(element); // 上传配置
    }

2、
package com.yootk.action;

import com.yootk.action.abs.AbstractAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Controller
@RequestMapping("/pages/common/*")
public class UploadAction extends AbstractAction {
    private static final Logger LOGGER = LoggerFactory.getLogger(UploadAction.class);
    @PostMapping("upload") // 提交地址
    @ResponseBody
    public Object upload(String id, MultipartFile photo) throws Exception {
        Map<String, Object> map = new HashMap<>();
        map.put("id", id);
        map.put("ContentType", photo.getContentType()); // 文件类型
        map.put("OriginalFilename", photo.getOriginalFilename()); // 文件原始名称
        map.put("Size", photo.getSize()); // 文件大小
        map.put("SaveFileName", this.save(photo)); // 文件保存的处理,返回保存的文件名称
        return map;
    }
    @GetMapping("input")
    public String input() {
        return "/pages/common/input.jsp";
    }
    public String save(MultipartFile file) throws Exception { // 文件保存处理
        // 根据MIME类型获取文件的后缀,而文件的名称通过UUID生成
        String fileName = UUID.randomUUID() + "." + file.getContentType()
                .substring(file.getContentType().lastIndexOf("/") + 1);
        LOGGER.info("生成文件名称:{}", file);
        String filePath = ContextLoader.getCurrentWebApplicationContext()
                .getServletContext().getRealPath("/upload/") + fileName;
        LOGGER.info("文件存储路径:{}", filePath);
        file.transferTo(new File(filePath)); // 文件存储
        return fileName;
    }
}


3、
<%@ page pageEncoding="UTF-8"%>
<html>
    <head><title>SpringMVC开发框架</title></head>
    <body>
        <form action="${request.contextPath}/pages/common/upload"
            method="post" enctype="multipart/form-data">
            文件编号:<input type="text" name="id" value="89282823"><br/>
            上传图片:<input type="file" name="photo"><br/>
            <button type="submit">发送</button><button type="reset">重置</button>
        </form>
    </body>
</html>

4、
localhost/pages/common/input

demo


上次编辑于: