young-z

  前言

  在上篇Spring MVC入门篇中,我们初步了解了Spring MVC开发的基本搭建过程,本文将针对实际开发过程的着重点Controller部分,将常用的知识点罗列出来,并配以示例。在这之前,我们有必要回顾一下,Spring MVC在我们的WEB开发中,定位或者作用是什么?Spring MVC在项目中,主要作用是接收客户端请求、解析路径并分发请求到相应的控制器即Controller中执行相应方法,在方法中,我们常见的操作有,调用业务逻辑层(后面会介绍到)方法,访问数据库,获取数据或者更新数据,如果是获取数据一般是要返回给前端,常用方式有两种:1.传统的Jsp开发中,我们可以将数据封装到Model属性中,然后在页面中通过el表达式之类的方式得到;2.随着Ajax技术/RESTful风格的兴起,同样可以在前后端分离的情况下传递数据(RESTful基础学习阶段可以不作重点,但Ajax一定要掌握)。另外基于Spring MVC的开发模式下,参数的获取、匹配,以及页面的跳转都十分方便,下面我们就来实践操作一下。

   0.页面文件路径以及页面跳转问题

  如果学习阶段习惯将Jsp文件直接放在web文件根目录webapp下,建议现在开始转变,将其分类放置WEB-INF下,这样一来,我们希望访问一个页面,将不再是直接通过路径去访问,而是通过Spring MVC中的请求,匹配到相应控制器中方法内部,再跳转至相应页面。区别在于WEB-INF下文件针对服务器端,而客户端例如浏览器是无法直接访问的,这样可以减少直接访问的风险,另外即使抛开安全性隐蔽性不谈,需要填充Model传递数据的Jsp页面如果在直接访问的情况下,可能会出现各种奇奇怪怪的问题,空白、无数据、样式错乱等等。页面跳转SpringMVC默认是请求转发,本文主要针对请求部分,均作默认请求转发处理。

  1.本文注解预介绍

  @Controller

  该注解用于Spring识别并实例化控制器bean到上下文中,即加上这个注解的类Spring才能识别知道它是要作为控制器。

  @RequestMapping

  该注解既可用于类上也可以用在方法上,实际开发往往两者都会用到,用于匹配url路径,接收请求,只有匹配的请求才会进入该控制器执行相关方法。

  @RequestParam

  在控制器方法中,用于将注入的参数与前台请求参数绑定

  @PathVariable

  用于绑定url路径中占位的参数

  2.自动匹配客户端传来的参数

  登录应该是大家再熟悉不过的web程序操作,这里以用户名、密码为例,分别对应实体类User中属性username和password,那么我们的页面表单部分如下所示:
    login.jsp中

<form action="${pageContext.request.contextPath }/user/login" method="post">
        用户名<input type="text" name="username"  /><br/>
        密码<input type="password" name="password"  /><br/>
        <button type="submit">登录</button>
    </form>

  Controller代码

package com.mmm.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("user")
public class DemoController {
    
    @RequestMapping(value="/toLogin")
    public String toLogin() {
        return "login";
    }
    
    @RequestMapping(value="/login")
    public String login(String username,String password) {
        System.out.println("用户名---" + username);
        System.out.println("密码---" + password);
        return "index";
    }
}

  有了前面的文件路径问题,这里我们先定义一个toLogin方法用于访问登录页面login.jsp,然后在页面表单点击提交后会请求到这里的login方法,重点:form表单中输入框<input>的name属性值,对应Controller中方法的参数名,即这里login方法的两个参数名,username和password,通过这两处的值,Spring MVC即可自动匹配使我们轻松获取参数。

  启动Tomcat,在地址栏输入http://localhost/spring-mvc/user/toLogin,即可访问到登录页面,如下所示


  任意输入用户名和密码内容后,点击登录,即可看到eclipse控制台输出类似如下信息

  并跳转至index.jsp页面,这里即成功获取到了前台传递来的参数,这种方式十分方便,但并不是唯一,所以下面分别介绍其它的方法。

  ***基于之前学过的Servlet系列的请求对象HttpServletRequest,它有一个getParameter(String parameterName)方法,这个parameterName即前台参数名,这里即同样对应输入框<input>的name属性值。

  ***应用Spring MVC的机制,通过实体类装配属性,同样可以匹配到参数,这里的用户名和密码,往往是对应实体类这里以User为例,类中有username和password这2两个字符串属性。这样一来,通过User对象我们也可以匹配到前台的这两个参数。

  下面我们汇总上面的内容,修改login方法为如下所示

@RequestMapping(value="/login")
    public String login(String username,String password, HttpServletRequest req,User user) {
        System.out.println("直接参数名匹配获取:用户名---" + username);
        System.out.println("直接参数名匹配获取:密码---" + password);
        System.out.println("HttpServletRequest获取:用户名---" + req.getParameter("username"));
        System.out.println("HttpServletRequest获取:密码---" + req.getParameter("password"));
        System.out.println("User获取:用户名---" + user.getUsername());
        System.out.println("User获取:密码---" + user.getPassword());
        return "index";
    }

  再次访问登录页面,输入内容并点击登录后,即可看到eclipse控制台输出类似如下信息

   表明以上方式均可用于获取前台传递参数值。

  PS:上面第一种方式(也是常用方式中)如果前端表单中name值和我这里方法中的参数名确实不一样,有没有别的办法能解决这个问题?目前Spring MVC各方面的注解渐渐在完善,这里Spring MVC就为我们提供了这样一种注解,@RequestParam,用于指定前台参数名,即这里如果我们将上面登录页面中的密码输入框的name属性值改为pwd,如下所示

密码<input type="password" name="pwd"  /><br/>

  按照前面我们的参数名匹配,Controller的login方法中是password,前台是pwd,这样是匹配不上,随之也无法获取数据的。但是引入上面提到的注解,方法可以中参数可以将String password修改为:@RequestParam("pwd") String password,这样一来,即可成功获取数据了。

  上面结合表单示例介绍了几种方式获取请求参数,之前学习Servlet部分,应该都熟悉doPost和doGet方法,这里的表单往往就是用的post请求方式提交。

  使用post提交时,数据将以数据块的形式提交到服务器,URL地址栏中不会出现数据,所以用这种方式提交的表单数据是相对安全的。所以表单提交包含类似于密码等数据时,建议使用post方法。

  使用get提交时地址栏上会在你当前项目路径后加上类似?pram1=value1&param2=value2”的形式,将表单数据附加到URL的后面,提交到服务器处理,这种方式效率更高,所以在没有私密数据时,请求推荐这种。

  除了上面提到的方式,还有一种获取路径传参的方式,这里也介绍一下,比较实用,通过@PathVariable注解,通过url路径实现动态传参并获取参数值,代码如下示例:

@RequestMapping(value="/show/{id}")
    public String show(@PathVariable(value="id") String str) {
        System.out.println("url获取:id参数值---" + str);
        return "index";
    }

  然后,我们在地址栏中输入localhost/spring-mvc/user/show/abcdefg,路径末尾的abcdefg即对应上面Controller代码@RequestMapping(value="/show/{id}")中的id,我们通过{id}这个大括号加参数名的方式去动态接收它,不论你的值是多少,这里就是用id变量来对应,于是下面@PathVariable(value="id")中的id即与之相应,随后绑定到String str上,这个str即匹配该参数值,我们便可以获取到该数据了。输入地址后回车,即可看到控制台,输出如下:

   3.改进登录模块

  前面提到的get、post主要是讲前台请求方式,name我们后台是否可以选择接受指定方式的请求呢?当然是可以的。@RequestMapping的属性中有个method,英文是方法的意思,在这里用于指定要接收的请求方式,如下所示

  为了方便,Spring MVC还为我们提供了RequestMethod枚举,如下所示

 * @author Juergen Hoeller
 * @since 2.5
 * @see RequestMapping
 * @see org.springframework.web.servlet.DispatcherServlet#setDispatchOptionsRequest
 * @see org.springframework.web.servlet.DispatcherServlet#setDispatchTraceRequest
 */
public enum RequestMethod {

    GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE

}

  其中指定了各种请求方式,我们可以直接通过例如RequestMetho.GET来指定。

  例如上面的登录部分,我们首先要前往登录页面,请求并没有参数传递,所以我们直接通过get方式请求,然后在登录页面中输入信息后点击登录,提交表单登录时,我们往往用post。上面我们的@RequestMapping(value="")这个value值一个是toLogin,一个是login,两个不同的值,就是为了区别这两个请求,但是其实是可以写成一样的,这里我们稍作修改为如下:

@RequestMapping(value="/login",method=RequestMethod.GET)
    public String toLogin() {
        return "login";
    }
    
    @RequestMapping(value="/login",method=RequestMethod.POST)
    public String login(String username, String password) {
        System.out.println("直接参数名匹配获取:用户名---" + username);
        System.out.println("直接参数名匹配获取:密码---" + password);
        return "index";
    }

   这样我们同样可以实现前面的效果。

  4.获取请求参数后我们要干嘛?

  在没有应用maven时,之前经常使用下面的方式新建web项目。

  我们首先要理清的是,我们的web项目都是动态的( dynamic),这个动态最上层,是客户端的各种动态操作,深入到底层,大多会反映到数据库,那么两者有什么关联,或者是怎样关联起来的?其中上面讲到的请求参数往往就是一个中间人。以上面的登录为例,客户端通过表单提交了用户名和密码两个参数,那么我们到底是要到数据库中查询有没有账号和密码都同时匹配的用户记录,有的话代表输入正确,没有的话则代表输入信息有误。客户端得到的登录成功或者失败这种不确定的结果,基于他的输入,也基于数据库中数据,无数个类似这样的关联关系个构建起了我们的web项目业务逻辑结构。

  所以后面我们就要理清什么是业务逻辑了。在登录过程中,我们验证用户名和密码是否正确,会调用到用户的持久层对象,去查询数据库用户表记录;但是同时我们需要记录登录信息,即在日志表中插入一条登录信息。但是持久层有查询用户表的方法,也有插入日志信息的方法,但是两种方法一起调用的并没有,所以这里需要我们在控制层和持久层之间添加一层业务逻辑层,整合这些持久操作,封装好业务逻辑方法供控制层调用。

  当然这里举例日志可能有点不太恰当,因为基于Spring AOP,例如事务管理、日志信息记录等通用组件能很方便的实现,能让我们专注于实现项目中的业务逻辑,但是总的来说,这里可以先简单的理解为,业务逻辑Service层,作用于持久层(dao)和控制层(controller)之间,它往往用于组装比持久层更复杂的操作过程,并且也不局限于数据库操作,然后供控制层调用,控制层则是专注于接收请求,分发到相应业务逻辑方法进行处理,然后作出响应。

  小结

  以上讲到了Spring MVC通用请求相关的处理参数过程及方法,当然还有例如文件上传也是一种特殊的请求,并且常用到,后面会理出来。我们在实际开发中往往下功夫最多的是如何实现那些复杂的业务逻辑,相反控制层不应该作用到这些业务处理。所以下面会专门理清一下业务逻辑层的部分,通过注入前面我们学习的MyBatis封装的持久对象,然后将业务逻辑层对象注入到控制层,实现Spring MVC与MyBatis的整合。

相关文章: