Fork me on GitHub

SpringMVC 入门

1. HelloWorld 入门程序

// 1. 导入 jar 包;

// 2. 配置 WEB-INF/web.xml

<servlet>
    <servlet-name>springDispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

// 3. 在WEB-INF创建springDispatcherServlet-servlet.xml 配置文件
//      选择: spring Bean Configuration File, 名称, beans, context, mvc,   完成
<!-- 配置通用扫描的包路径 -->
<context:component-scan base-package="cn.itcast.springmvc"></context:component-scan>

<!-- 配置通用的视图解析器 -->
<bean id="internalResourceViewResolver"
            class="org.springframework.web.servlet.view.internalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".jsp"/>
</bean>


// 3.1 也可以在 src 目录下,创建SpringMVC的配置文件, 此时,web.xml 的配置为:
<servlet>
    <servlet-name>springDispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <!-- 配置springmvc 配置文件路径 -->
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servet-mapping>


// 4. 创建WEB-INF/views/ok.jsp, 即结果页面

// 5. 创建WebContent/index.jsp, 即请求页面
示例:<a href="${pageContext.request.contextPath}/helloworld">点击这里</a>

// 6. cn.itcast.springmvc.handler(或controller)
@Controller
public class HelloWorld{

    @RequestMapping(value="/helloworld",method=RequestMethod.GET)
    public String helloworld(){
        System.out.println("程序运行正常");
        return "ok";
    }
}

2. HelloWorld 深度解析

  1. 客户端请求提交到 DispatcherServlet;
  2. 由 DispatcherServlet 控制器查询一个或多个 HandlerMapping, 找到处理请求的 Controller;
  3. DispatcherServlet 将请求提交到 Controller;
  4. Controller 调用业务逻辑处理后,返回 ModelAndView;
  5. DispatcherServlet 查询一个或多个ViewResolver视图解析器,找到ModelAndView指定的视图;

3. 注解 RequestMapping

3.1 请求地址

@Controller
@RequestMapping(value="/crm")
public class HelloWorld{

    @RequestMapping(value="/helloworld",method=RequestMethod.GET)
    public String helloworld(){
        System.out.println("程序运行正常");
        return "ok";
    }
}

// 备注: 此时访问地址: /crm/helloworld

3.2 RequestMapping 中的参数介绍

  1. 常用方法:
    • method=RequestMethod.GET
    • method=RequestMethod.POST
  2. 请求参数映射 params
    • params={"age","tel!=110"}
    • 表示请求参数中必须有age和tel,且tel不是110;
  3. 请求头映射 headers
    • headers={"Host=localhost:8080"}
    • 表示请求主机必须是 localhost:8080;
  4. URL 映射
// 1. 占位符URL映射
    // ? 匹配一个字符; * 匹配零个或多个字符; ** 匹配零个或多个路径
    public class Demo{

        @RequestMapping(value="/test?", method=RequestMethod.GET)
        public String test(){
            System.out.println("程序运行正常");
            return "ok";
        }
    }

// 2. @PathVariable 占位符URL映射
    // index.jsp
    示例: <a href="${pageContext.request.contextPath}/tpv/887">testPathVariable</a>

    // cn.itcast.springmvc.demo
    @Controller
    public class Demo{

        @RequestMapping(value="/tpv/{bookId}",method=RequestMethod.GET)
        public String test2(@PathVariable("bookId") String bookId){
            System.out.println("程序运行正常"+bookId);
            return "ok";
        }
    }

4. REST 介绍

  1. REST, Representational State Transfer, (资源)表现层状态转化;
  2. 资源:网络上的一个具体信息,可以是一段文本,一张图片等等;每种资源对应一个特定的URI,要获取这个资源,
    访问它的 URI 即可, 因此, URI 即为每一个资源的独一无二的识别符;
  3. 表现层: 把资源具体呈现出来的形式; 例如文本可以用txt格式表现,也可以用HTML格式,XML格式或JSON格式
  4. 状态转化:每发出一个请求,就代表了客户端和服务器的一次交互过程. HTTP协议,是一个无状态协议,即所有
    的状态都保存在服务端.因此,如果客户端想要操作服务器,必须通过某种手段,让服务器发生"状态转化".而
    这种转化是建立在表现层之上的,所以就是"表现层状态转化".具体说,就是HTTP协议里面,四个表示操作方式
    的动词: GET,POST,PUT,DELETE,分别对应四种基本操作: GET 用来获取资源, POST 用来新建资源,
    PUT 用来更新资源, DELETE 用来删除资源;
// 需求: 发送RESTful风格请求
// 1. web.xml 中配置 HiddenHttpMethodFilter
<filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

// 2. index.jsp
<form action="${pageContext.request.contextPath}/order/224" method="post">
    <input type="hidden" name="_method" value="DELETE"/>
    <input type="submit" value="删除订单"/>
</form>

// 3. cn.itcast.springmvc.demo
@Controller
public class Demo{

    @RequestMapping(value="/order/{orderId}",method=RequestMethod.DELETE)
    public String delete(@PathVariable("orderId") String orderId){
        System.out.println("删除订单成功"+orderId);
        return "ok";
    }
}

5. 获取请求相关数据

  • @RequestParam
  • @PathVariable
  • @RequestHeader
  • @CookieValue
// @RequestParam, 获取请求参数
// index.jsp
<a href="${pageContet.reqeust.contextPath}/testRequestParam?name=zhangsan">点击这里</a>

// cn.itcast.springmvc.demo
@Controller
public class Demo{

    @RequestMapping(value="/testRequestParam",method=RequestMethod.GET)
    public String testRequestParam(@RequestParam("name") String name){

        System.out.println("姓名:"+name);
        return "ok";
    }

    // @RequestParam 中的参数: required, defaultValue
    @RequestMapping(value="/testRequestParam",method=RequestMethod.GET)
    public String test03(@RequestParam(value="name",required=true,
                                                defaultValue="lisi")  String name){

        System.out.println("姓名:"+name);
        return "ok";                
    }
}

// 请求中有多个参数
// index.jsp
<a href="${pageContext.request.contextPath}/test04?name=zhangsan&roleId=manager&roleId=actory">
点击这里
</a>

// demo.class
@Controller
public class Demo{

    @RequestMapping(value="/test04",method=RequestMethod.GET)
    public String test04(@RequestParam(value="name") String name,
                         @RequestParam("roleId") List<String> roleIds){

        for(String rId : roleIds){
            System.out.println(rId);
        }
        System.out.println(name);
        return "ok";
    }
}


// @RequestHeader, 获取请求头的值
// @CookieValue, 获取 cookie 的值
// index.jsp
<a href="${pageContext.request.contextPath}/test05">点击这里</a>

// demo.class
@Controller
public class Demo{

    @RequestMapping(value="/test05",method=RequestMethod.GET)
    public String test05(@RequestHeader("Host") String host,
                         @CookieValue("JSESSIONID") String JSESSIONID){
        System.out.println(host + JSESSIONID);
        return "ok";
    }
}


// 使用 POJO 对象
// User.class(略)
// index.jsp
<form action="${pageContext.request.contextPate}/test06" method="post">
    用户名:<input type="text" name="username"><br/>
    密码:<input type="password" name="password"><br/>
    <input type="submit" value="注册"/>
</form>

// demo.class
@Controller
public class Demo{

    @RequestMapping(value="/test06",method=RequestMethod.POST)
    public String test06(User user){
        System.out.println(user);
        return "ok";
    }
}


// 使用Servlet的原生API获取参数
// test06() 中的参数: HttpServletRequest, HttpServletResponse,HttpSession,
//                   java.security.Principal, Locale, InputStream, OutputStream,
//                   Reader, Writer

// index.jsp(略)
// demo.class
@Controller
public class Demo{

    @RequestMapping(value="/test07",method=RequestMethod.GET)
    public void test06(HttpServletRequest request, HttpServletResponse response,
                                         Writer writer) throws IOException{

        System.out.println(request.getContextPath());
        writer("程序运行正常");
        wirter.close();
    }
}

6. 处理模型数据

6.1 ModelAndView

  • SpringMVC 框架会将 ModelAndView 中的数据放进request请求域中, ModelAndView 底层实际就是
    request.setAttribute(k,v);
// index.jsp
<a href="${pageContext.request.contextPath}/test07">点击这里</a>

// demo.class
@Controller
public class Demo{

    @RequestMapping(value="/test07", method=RequestMethod.GET)
    public ModelAndView test07(){
        ModelAndView mv = new ModelAndView();
        mv.addObject("username","zhangsan");
        mv.setViewName("ok");
        return mv;
    }
}

// ok.jsp
用户名:${requestScope.username}

6.2 Map+Model+ModelMap

  • Map: java.util.Map, 接口;
  • Model: org.springframework.ui.Model, 接口;
  • ModelMap: org.springframework.ui.ModelMap, 实现类;
// index.jsp
<a href="${pageContext.request.contextPath}/test08">点击这里</a>

// demo.class
@Controller
public class Demo{

    @RequestMapping(value="/test08",method=RequestMethod.GET)
    public String test08(Map<String,Object> m1, Model m2, ModelMap m3){

        m1.put("m1","java.util.Map<K,V>");
        m2.addAttribute("m2","org.springframework.ui.Model");
        m3.addAttribute("m3","org.springframework.ui.ModelMap");

        System.out.println(m1 == m2);
        System.out.println(m2 == m3);
        System.out.println(m1 == m3);

        System.out.println(m1.getClass().getName());
        System.out.println(m2.getClass().getName());
        System.out.println(m3.getClass().getName());

        return "ok";
    }
}

// ok.jsp
m1: ${requestScope.m1}<br/>
m2: ${requestScope.m2}<br/>
m3: ${requestScope.m3}<br/>

6.2.1 结果说明
  • 此处虽然注入的是三个不同的类型(Map m1, Model m2, ModelMap m3), 但三者是同一个对象,都是
    同一个BindingAwareModelMap实例;
  • SpringMVC 框架会将模型数据放进request请求域中;
  • 为了方便重构,使用java.util.Map较多;

6.3 SessionAttributes

  • 该注解只能放在类上面;
// index.jsp
<a href="${pageContext.request.contextPath}/test09">点击这里</a>

// demo.class
@Controller
@SessionAttributes(value="user",types=String.class)
public class Demo{

    @RequestMapping(value="/test09",method=RequestMethod.GET)
    public String test09(Map<String,Object> map){

        User user = new User("zhangsan",22,"zhangsan@163.com");
        map.put("user",user);

        map.put("address","Beijing");
        return "ok";
    }
}

// ok.jsp
用户名: ${sessionScope.user}
地址: ${sessionScope.address}

6.4 ModelAttribute

  • 该注解作用在:方法或参数上, @Target({ElementType.PARAMETER, ElementType.METHOD})

// 需求: 用户李四将自己的用户名改为"zhangsan",年龄改为"25",密码和邮箱不变
// index.jsp
<h2>修改信息</h2>
<form action="${pageContext.request.contextPath}/test10" method="post">
    <input type="hidden" name="_method" value="PUT"/><br/>
    <input type="hidden" name="id" value="11"/><br/>
    用户名:<input type="text" name="username" value="zhangsan"/><br/>
    年龄: <input type="text" name="age" value="22"/><br/>
    Email: <input type="text" name="email" value="zhangsan@163.com"/><br/>
    <input type="submit" value="修改"/><br/>
</form>

// demo.class
@Controller
@SessionAttributes(value="user")
public class Demo{

    @RequestMapping(value="/test10",method=RequestMethod.PUT)
    public String test10(User user){
        System.out.println(user);
        return "ok";
    }
}
6.4.1 异常一: "Session attribute 'user' required - not found in session"
  • 解决方法: 将@SessionAttributes(value="user")注释掉;

6.4.2 异常二: 此时,收到的password=null

// 修改 demo.class
@Controller
public class Demo{

    @RequestMapping(value="/test10",method=RequestMethod.PUT)
    public String test10(User user){
        Sysetm.out.println("修改之后的User: "+ user);
        return "ok";
    }

    // 从数据库中根据 id 查询
    @ModelAttribute
    public void getUserById(@RequestParam(value="id") Integer id){
        System.out.println("come in @ModelAttribute ##############");

        if(null != id){

            // 模拟根据 id,从数据库查询出对应的 User 信息
            // User user = UserService.getUserById(id);
            // user(id,name,password,age,email);
            User user = new User(11,"lisi","12345",22,"lisi@163.com");
            System.out.println("数据库查询到的初始User: "+ user);
        }
    }
}
运行结果, 另外,点击其他请求,出现如下异常:"Required Integer parameter 'id' is not present"

结果分析
// 修改代码, 增加属性 `required=false`,将数据库查询到 user,存入到隐藏域

@ModelAttribute
public void getUserById(@RequestParam(value="id", required=false) Integer id,
                                                    Map<String,Object> map){
    System.out.println("come in @ModelAttribute ##############");

    if(null != id){

        // 模拟根据 id,从数据库查询出对应的 User 信息
        // User user = UserService.getUserById(id);
        // user(id,name,password,age,email);
        User user = new User(11,"lisi","12345",22,"lisi@163.com");

        // 将user放入隐藏域
        map.put("user",user);

        System.out.println("数据库查询到的初始User: "+ user);
    }
}
运行结果

总结一:
  1. 根据ID从数据库中查询出对象,并将对象放入Map,其中key为user(POJO类名首字母小写),完成修改;
  2. 在方法头上添加 @ModelAttribute
    • 每个 Controller 类下面的业务方法都会先调用标注了@ModelAttribute注解的方法;
    • 放入到Map的 key,需要与目标方法的参数首字母小写的对象一致;
  3. 在方法入参前使用 @ModelAttribute
    • 查询数据库放入map的 key与 @ModelAttribute修改的方法入参前名字,是否一致;不一致; 不写;
// 第一种方式: 在方法入参前,使用 @ModelAttribute("user"), 即与隐藏域map中的key名称一致
@RequestMapping(value="test10",method=RequestMethod.PUT)
public String test10(@ModelAttribute("user") User user){
    System.out.println("修改之后的User: "+user);
    return "ok";
}

// 第二种方式:在方法入参前,使用 @ModelAttribute("abc"),即与隐藏域map中的key名称不一致;
// 此时,password 为 null;
@RequestMapping(value="test10",method=RequestMethod.PUT)
public String test10(@ModelAttribute("abc") User user){
    System.out.println("修改之后的User: "+user);
    return "ok";
}

@ModelAttribute
public void getUserById(@RequestParam(value="id",required=false) Integer id,
                                                Map<String,Object> map){
        System.out.println("come in @ModelAttribute #######");
        if(null != id){
            User user = new User(11,"lisi","12345",22,"lisi@163.com");
        }

        map.put("user",user);

        System.out.println("数据库查询到的初始User: "+user);
    }

总结二:
  1. 确定key的值,如果有@ModelAttribute进行修饰,则key的值就是@ModelAttribute注解的value属性值;
    如果没有写,默认key值就是传入POJO类名的第一个字母小写;
  2. 从Map中获取key对应的Bean,如果有,就直接赋值并作为后续入参传入;
    如果没有,那就去Session域中查找,也就是当前Handler的@SessionAttributes注解的value值或者
    types值是否和key或者Bean的类型匹配,如果匹配就从Session域中获取;
    • 如果Session中有对象,则返回该值作为目标方法的入参;
    • 如果Session中没有对象,就会抛出异常 HttpSessionRequiredException;
  3. 如果implictModel中没有,sessionAttribute中也没有,那就通过反射创建一个 bindObject,
    bindObject = BeantUtils.instantiateClass(paramType);

参考资料

posted @ 2017-11-01 14:51  小a的软件思考  阅读(335)  评论(0编辑  收藏  举报