【Spring MVC】 ---- 深入Spring MVC组件开发(一)

目录

1.控制器接收各类请求参数
2.拦截器
3.验证表单
4.数据模型
5.视图和视图解析器

1.控制器接收各类请求参数

1.1 接收普通请求参数

@RequestMapping("/commonParams")
    public ModelAndView commonParams(String roleName, String note) {
        System.out.println("roleName =>" + roleName);
        System.out.println("note =>" + note);
        ModelAndView mv = new ModelAndView();
        mv.setViewName("index");
        return mv;
    }

注意:通过参数名称和HTTP请求参数的名称来保持一致来获取参数,如果不一致无法获取到,这样的方式允许参数为空

角色参数类

public class RoleParams {
    private String roleName;
    private String note;
    /***setter and getter ***/
}
@RequestMapping("/commonParamPojo")
    public ModelAndView commonParamPojo(RoleParams roleParams) {
        System.out.println("roleName =>" + roleParams.getRoleName());
        System.out.println("note =>" + roleParams.getNote());
        ModelAndView mv = new ModelAndView();
        mv.setViewName("index");
        return mv;
    }

1.2 使用@RequestParam注解获取参数

@RequestMapping("/commonParams")
    public ModelAndView commonParams(@RequestParam("role_name") String roleName, String note) {
        System.out.println("roleName =>" + roleName);
        System.out.println("note =>" + note);
        ModelAndView mv = new ModelAndView();
        mv.setViewName("index");
        return mv;
    }

注意: 此时传递过来的参数必须为role_name,否则会发生400 Bad Request ,因为参数被@RequestParam注解,默认情况下该参数不能为空,如果为空系统会抛出异常。如果希望为空,可以设置required=false属性
在这里插入图片描述
1.3 使用URL传递参数

 @RequestMapping("/getRole/{id}")
    // @PathVariable表示从URL的请求地址中获取参数
    public ModelAndView ModelAndViewpathVariable(@PathVariable("id") Long id) {
        Role role = roleService.getRole(id);
        ModelAndView mv = new ModelAndView();
        mv.addObject(role);
        mv.setView(new MappingJackson2JsonView());
        return mv;
    }

{id}代表处理器需要接收一个由URL组成的参数,且参数名称为id
通过@PathVariable注解获取各类参数
在这里插入图片描述
1.4 传递JSON参数

PageParams分页参数

public class PageParam {
    private int start;
    private int limit;
    /*** setter and getter ***/
}

带有分页参数的角色参数查询

public class RoleParams {
    private String roleName;
    private String note;
    private PageParam pageParams = null ; // 分页参数
    /*** setter and getter ***/

ajax传递JSON数据

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>Title</title>
    <script type="text/javascript" src="${pageContext.request.contextPath}/static/js/jquery-1.7.2.js"></script>
    <script type="text/javascript">

        // 就绪函数,表示当前页面加载完毕后,直接执行里面的代码
        $(function () {
            $("#btn").click(function(){
                $.ajax({
                    type: 'post',
                    url:"${pageContext.request.contextPath}/params/findRoles.do",
                    // 将JSON转化为字符串传递
                    data:JSON.stringify({
                        roleName : 'role',
                        note: 'note',
                        // 分页参数
                        pageParams: {
                            start : 1,
                            limit : 20
                        }
                    }),
                    // 此处需要告知传递参数类型为JSON,不能缺少
                    contentType:"application/json",
                    success:function(msg){
                    }
                }) ;
            });
        })
    </script>
</head>
<body>
    <input type="submit" value="提交" id="btn"/>
</body>
</html>

注意:

  • 传递的JSON数据需要和对应参数的POJO保持一致
  • 请求的时候告知请求的参数类型为JSON
  • 传递的参数是一个字符串,而不是JSON,用JSON.stringify()方法将JSON数据转换为字符串
@RequestMapping(value = "/findRoles")
    public ModelAndView findRoles(@RequestBody RoleParams roleParams) {
        List<Role> roleList = roleService.findRoles(roleParams);
        ModelAndView mv = new ModelAndView();
        mv.addObject(roleList);
        mv.setView(new MappingJackson2JsonView());
        return mv;
    }

@RequestBody:将请求的json数据绑定到目标方法的pojo对象
在这里插入图片描述
1.5 接收列表数据和表单序列化

例子1: 根据编号删除多个角色

传递数组给控制器

// 删除角色数组
            var idList = [1, 2, 3];
            $("#btn1").click(function(){
                $.ajax({
                    type: 'post',
                    url:"${pageContext.request.contextPath}/params/deleteRoles.do",
                    data:JSON.stringify(
                        idList
                    ),
                    contentType:"application/json",
                    success:function(msg){

                    }
                })
            })

接收数组参数

// @RequestBody表示将传递过来的JSON数组数据转化为对应的Java集合类型
    @RequestMapping("/deleteRoles")
    public ModelAndView deleteRoles(@RequestBody List<Long> idList){
        ModelAndView mv = new ModelAndView();
        // 删除角色
        int total = roleService.deleteRoles(idList);
        // 绑定视图
        mv.addObject("total", total);
        // JSON视图
        mv.setView(new MappingJackson2JsonView());
        return mv;
    }

@RequestBody:将请求的json数据转化为对应的Java集合类型

例子2: 新增多个角色对象

传递角色数组

// 新增角色数组
            var roleList = [
                {roleName: 'role_name_1', note:'note_1'},
                {roleName: 'role_name_2', note:'note_2'},
                {roleName: 'role_name_3', note:'note_3'}
            ];

            $("#btn2").click(function(){
                $.ajax({
                    type: 'post',
                    url:"${pageContext.request.contextPath}/params/addRoles.do",
                    data: JSON.stringify(roleList),
                    contentType: "application/json",
                    success:function (result) {
                    }
                })
            })
@RequestMapping("/addRoles")
    public ModelAndView addRoles(@RequestBody List<Role> roleList) {
        ModelAndView mv = new ModelAndView();
        // 增加角色
        int total = roleService.insertRoles(roleList);
        // 绑定视图
        mv.addObject("total", total);
        // JSON视图
        mv.setView(new MappingJackson2JsonView());
        return mv;
    }

在这里插入图片描述
例子3: 表单序列化

提交序列化表单

$("#commit").click(function(){
                $.ajax({
                    type: 'post',
                    url:"${pageContext.request.contextPath}/params/commonParamPojo2.do",
                    // 将form数据序列化,传递给后台,数据以roleName=xxx&note=xxx传递
                    data: $("#form").serialize(),
                    success:function (result) {
                    }
                })
            })
<form id="form">
        <table>
            <tr>
                <td>角色名称</td>
                <td><input id="roleName" name="roleName" value=""/></td>
            </tr>
            <tr>
                <td>备注</td>
                <td><input id="note" name="note" value=""/></td>
            </tr>
            <tr>
                <td><input id="commit" type="button" value="提交"></td>
            </tr>
        </table>
</form>

接收序列化表单

@RequestMapping("/commonParamPojo2")
    public ModelAndView commonParamPojo2(String roleName, String note) {
        System.out.println("roleName =>" + roleName);
        System.out.println("note =>" + note);
        ModelAndView mv = new ModelAndView();
        mv.setViewName("index");
        return mv;
    }

2.拦截器

2.1 拦截器执行流程

在这里插入图片描述

  • preHandle方法: 在处理器之前执行的前置方法, 返回一个boolean值,影响后面Spring MVC的流程
  • postHandle方法: 在处理器之后执行的后置方法, 处理器的逻辑完成后允许它
  • afterCompletion方法: 无论是否产生异常都会在渲染视图后执行的方法

2.2 开发拦截器

  • 拦截器必须实现HandlerInterceptor接口

注意: 当XML配置文件加入了元素 < mvc:annotation-driven>,系统就会初始化拦截器ConversionServiceExposingInterceptor,它是个一开始就被Spring MVC系统默认加载的拦截器。

SpringMVC提供了公共拦截器HandlerInterceptorAdapter,当只想实现3个拦截器方法中一到两个时,只要继承它,根据需要覆盖原有的方法就可以了。

在这里插入图片描述
角色拦截器

public class RoleInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandler");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion");
    }
}

配置拦截器

 <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/role/*.do"/>
            <bean class="cn.whc.interceptor.RoleInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

执行结果
在这里插入图片描述

3.验证表单

验证器需要的jar包:classmate、jboss-logging、hibernate-validator(通过Hibernate校验规则)、validation-api(验证注解)、依赖于classmate、jboss-logging两个包

 <dependency>
      <groupId>com.fasterxml</groupId>
      <artifactId>classmate</artifactId>
      <version>1.1.0</version>
    </dependency>

    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>5.2.4.Final</version>
    </dependency>

    <dependency>
      <groupId>org.jboss.logging</groupId>
      <artifactId>jboss-logging</artifactId>
      <version>3.2.1.Final</version>
    </dependency>

    <dependency>
      <groupId>javax.validation</groupId>
      <artifactId>validation-api</artifactId>
      <version>1.1.0.Final</version>
    </dependency>

3.1 使用JSR 303 注解验证输入内容
Spring提供了对Bean的功能校验,通过注解@Valid标明哪个Bean需要启动注解式的验证。
验证注解定义

注解详细信息
@Null被注释的元素必须为null
@NotNull被注释的元素必须不为null
@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(min,max)被注释的元素的大小必须在指定的范围内
@Future被注释的元素必须是一个将来的日期
@Pattern(value)被注释的元素必须符合指定的正则表达式

例子:

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="${pageContext.request.contextPath}/validate/annotation.do">
        <table>
            <tr>
                <td>产品编号:</td>
                <td><input name="productId" id="productId"/></td>
            </tr>
            <tr>
                <td>用户编号:</td>
                <td><input name="userId" id="userId"/></td>
            </tr>
            <tr>
                <td>交易日期:</td>
                <td><input name="date" id="date"/></td>
            </tr>
            <tr>
                <td>价格:</td>
                <td><input name="price" id="price"/></td>
            </tr>
            <tr>
                <td>数量:</td>
                <td><input name="quantity" id="quantity"/></td>
            </tr>
            <tr>
                <td>交易金额:</td>
                <td><input name="amount" id="amount"/></td>
            </tr>
            <tr>
                <td>用户邮件:</td>
                <td><input name="email" id="email"/></td>
            </tr>
            <tr>
                <td>备注:</td>
                <td><textarea name="note" id="note" cols="20" row="5"></textarea></td>
            </tr>
            <tr>
                <td colspan="2" align="right">
                    <input type="submit" value="提交"/>
                </td>
            </tr>
        </table>
    </form>
</body>
</html>

表单POJO

public class Transaction {

    // 产品编号
    @NotNull
    private Long productId;

    // 用户编号
    @NotNull
    private Long userId;

    // 交易日期
    @Future
    @DateTimeFormat(pattern = "yyyy-MM-dd") //日期格式转化
    @NotNull
    private Date date;

    // 价格
    @NotNull // 不能为空
    @DecimalMin(value = "0.1") // 最小值0.1元
    private Double price;

    // 数量
    @Min(1) // 最小值为1
    @Max(100) // 最大值
    @NotNull // 不能为空
    private Integer quantity;

    // 交易金额
    @NotNull
    @DecimalMax("500000.00") // 最大金额为5万元
    @DecimalMin("1.00") // 最小交易金额1元
    private Double amount;

    // 邮件
    // 域名:"qq.com”、“www.qq.com”、“mp.weixin.qq.com”
    @Pattern(regexp = "^([a-zA-Z0-9]*[-_]?[a-zA-Z0-9]+)*@" + "([a-zA-Z0-9]*[-_]?[a-zA-Z0-9] +)"
     + "[\\.][A-Za-z]{2,3}([\\.][A-Za-z]{2})?$", message = "不符合邮件格式")
    private String email;

    // 备注
    @Size(min = 0, max = 256) // 0到256个字符
    private String note;

	/**setter and getter**/
}
  • 产品编号、用户编号、交易日期、价格、数量、交易金额不能为空
  • 交易日期格式为yyyy-MM-dd,且只能大于今日
  • 价格最小值为0.1
  • 数量是一个整数,且最小值为1,最大值为100
  • 交易金额最小值为1,最大值为100
  • 用户邮件需要满足邮件正则表达式
  • 备注内容不得多于256个字符
@Controller
@RequestMapping("/validate")
public class ValidateController {

    @RequestMapping("/annotation")
    public ModelAndView annotationValidate(@Valid Transaction transaction, Errors errors) {
        // 是否存在错误
        if(errors.hasErrors()) {
            // 获取错误信息
            List<FieldError> errorList = errors.getFieldErrors();
            for (FieldError fieldError : errorList) {
                // 打印字段错误信息
                System.out.println("field:" + fieldError.getField() + "\t" + "msg:" + fieldError.getDefaultMessage());
            }
        }
        ModelAndView mv = new ModelAndView();
        mv.setViewName("index");
        return mv;
    }
}

@Valid:标明这个Bean将会被校验
Errors参数用于保存是否存在错误信息

测试结果图

在这里插入图片描述
在这里插入图片描述
控制台打印出现中文乱码问题的话:https://blog.csdn.net/weixin_42158633/article/details/89554314

3.2 使用验证器
进行业务校验,Spring提供了Validator接口来实现校验

public class TransactionValidator implements Validator {
    public boolean supports(Class<?> aClass) {
        // 判断验证是否为Transaction,如果是则进行验证
        return Transaction.class.equals(aClass);
    }

    public void validate(Object target, Errors errors) {
        Transaction transaction = (Transaction)target;
        // 求交易金额和价格*数量的差额
        double dis = transaction.getAmount() - (transaction.getPrice() + transaction.getQuantity());
        // 如果差额大于0.01,则认为业务错误
        if(Math.abs(dis) > 0.01) {
            // 加入错误信息
            errors.rejectValue("amount", null, "交易金额和购买数量与价格不匹配");
        }
    }
}

验证器判断是否为Transaction对象,Spring MVC提供了注解@InitBinder,将验证器和控制器绑定到一起

@Controller
@RequestMapping("/validate")
public class ValidateController {

    @InitBinder
    public void initBinder(DataBinder binder) {
        // 数据绑定器加入验证器
        binder.setValidator(new TransactionValidator());
    }

    @RequestMapping("/validator")
    public ModelAndView validator(@Valid Transaction transaction, Errors errors) {
        // 是否存在错误
        if(errors.hasErrors()) {
            // 获取错误信息
            List<FieldError> errorList = errors.getFieldErrors();
            for (FieldError fieldError : errorList) {
                // 打印字段错误信息
                System.out.println("field:" + fieldError.getField() + "\t" + "msg:" + fieldError.getDefaultMessage());
            }
        }
        ModelAndView mv = new ModelAndView();
        mv.setViewName("index");
        return mv;
    }

}

注意: JSR 303注解方式和验证器方式不能同时使用

4. 数据模型

从控制器获取数据后,会装载数据到数据模型和视图中,然后将视图名称转发到视图解析器中,通过解析器解析后得到最终视图,最后将数据模型渲染到视图中,展示最终的结果给用户。

用ModelAndView来定义视图类型,包括JSON视图,也用它来加载数据模型。ModelAndView有一个类型为ModelMap的属性model,而ModelMap继承了LinkedHashMap<String, Object>,因此它可以存放各种键值对,为了进一步定义数据模型功能,Spring还创建了类ExtendedModelMap,这个类实现了数据模型定义的Model接口,并且还在此基础上派生了关于数据绑定的类——BindAwareModelMap

在这里插入图片描述

在控制器的方法中,可以把ModelAndView、Model、ModelMap作为参数。

测试数据模型

@Controller
public class ModelController {

    @Autowired
    private RoleService roleService = null;

    // 使用ModelMap作为参数
    @RequestMapping(value = "/getRoleByModelMap", method = RequestMethod.GET)
    public ModelAndView getRoleByModelMap(@RequestParam("id")Long id, ModelMap modelMap) {
        Role role = roleService.getRole(id);
        ModelAndView mv = new ModelAndView();
        mv.setViewName("roleDetails");
        modelMap.addAttribute("role", role);
        return mv;
    }

    // 使用Model作为参数
    @RequestMapping(value = "/getRoleByModel", method = RequestMethod.GET)
    public ModelAndView getRoleByModel(@RequestParam("id") Long id, Model model) {
        Role role = roleService.getRole(id);
        ModelAndView mv = new ModelAndView();
        mv.setViewName("roleDetails");
        model.addAttribute("role", role);
        return mv;
    }

    // 使用ModelAndView作为参数
    @RequestMapping(value = "/getRoleByMv", method = RequestMethod.GET)
    public ModelAndView getRoleByMv(@RequestParam("id") Long id, ModelAndView mv) {
        Role role = roleService.getRole(id);
        mv.setViewName("roleDetails");
        mv.addObject("role", role);
        return mv;
    }
}

5. 视图和视图解析

5.1 视图

视图接口定义

public interface View {
// 响应状态属性
    String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
    // 定义数据模型下取出变量路径
    String PATH_VARIABLES = View.class.getName() + ".pathVariables";
    // 选择响应内容类型
    String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";
   //  响应客户端类型
    String getContentType();
  // 渲染方法, var1是数据模型
    void render(Map<String, ?> var1, HttpServletRequest var2, HttpServletResponse var3) throws Exception;
}

SpringMVC常用视图类

在这里插入图片描述

  • MappingJackson2JsonView是非逻辑视图,在没有视图解析器的情况下可以进行渲染,最终将其绑定的数据模型转换为JSON数据
  • InternalResourceView是逻辑视图,对于逻辑视图需要一个视图解析器

5.2 视图解析器
视图解析器定义

public interface ViewResolver {
    View resolveViewName(String var1, Locale var2) throws Exception;
}

在这里插入图片描述

<!--定义视图解析器-->
    <!--找到Web工程/WEB-INF/JSP文件夹,且文件结尾为jsp的文件作为映射-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
@RequestMapping(value = "/index", method = RequestMethod.GET)
   public String index(@RequestParam("id") Long id, ModelMap modelMap) {
       Role role = roleService.getRole(id);
       modelMap.addAttribute("role", role);
       return "roleDetails";
   }

对于这样的一个字符串,由于配置了InternalResourceViewResolver,通过Spring MVC系统的作用,生成JstlView视图。ModelMap是数据模型,系统会绑定视图和数据模型到一个ModelAndView中,然后通过视图解析器会根据视图的名称,找到对应的视图资源。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值