Spring MVC

参考视频:B站狂神,写这个只是方便个人复习,怎么写是我自己的事,我能看懂就行,没要求非要让你看!白嫖还挑刺,是很没有风度的事情。希望做个有风度的“五好青年”!


4、RestFul和Controller

1.控制器Controller

  • 控制器复杂提供访问应用程序的行为,通常可以通过接口定义或注解定义两种方法实现。
  • 控制器负责解析用户的请求并将其转换为一个模型。
  • 在Spring MVC中一个控制器类可以包含多个方法
  • 在Spring MVC中,对于Controller的配置方式有很多种

2.实现Controller接口

Controller是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法;

// 实现该接口的类获得控制器功能
public interface Controller {
   // 处理请求且返回一个模型与视图对象
   ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
}
  1. 新建一个Moudle,springmvc-04-controller 。将刚才的03拷贝一份, 进行操作!

    • 删掉HelloController。

  • web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">

    <!--1.注册servlet-->
    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!-- 启动顺序,数字越小,启动越早 -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!--所有请求都会被springmvc拦截 -->
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>
  • springmvc-servlet.xml,mvc的配置文件只留下视图解析器!
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>

</beans>
  1. 编写一个Controller类,ControllerTest.java

package com.github.subei.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// 定义控制器:只要实现了 Controller 接口类,说则这就是一个控制器了
// 注意点:不要导错包,实现Controller接口,重写方法;
public class ControllerTest implements Controller {
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        //返回一个模型视图对象
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","ControllerTest");
        mv.setViewName("test");
        return mv;
    }
}
  1. 编写完毕后,去Spring配置文件中注册请求的bean;name对应请求路径,class对应处理请求的类。

    <bean name="/t1" class="com.github.subei.controller.ControllerTest"/>
  1. 编写前端test.jsp,注意在WEB-INF/jsp目录下编写,方便对应视图解析器。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${msg}
</body>
</html>
  1. 配置Tomcat运行测试,我这里项目发布名配置是/springmvc_04,如果没写,只是一个 / ,则请求不用加项目名,直接运行即可。不影响!

  • 测试访问路径:http://localhost:8080/springmvc_04/t1
  • 测试成功!!!

  • <mark>注</mark>
    • 实现接口Controller定义控制器是较老的办法;
    • 缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦。

3.使用注解@Controller

  • @Controller注解类型用于声明Spring类的实例是一个控制器;
  1. Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明组件扫描。

    <!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
    <context:component-scan base-package="com.github.subei.controller"/>
  1. 增加一个ControllerTest2类,使用注解实现;

package com.github.subei.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller//代表这个类会被spring接管
// 被这个注解的类,中的所有方法,如果返问值是string,并且有具体页面可以跳转,那么就会被视图解析器解析;
public class ControllerTest2 {
    // 映射访问路径
    @RequestMapping("/t2")
    public String index(Model model){
        // Spring MVC会自动实例化一个Model对象用于向视图中传值
        model.addAttribute("msg", "ControllerTest2");
        // 返回视图位置
        return "test";
    }
}
  1. 运行tomcat测试。测试路径:http://localhost:8080/springmvc_04/t2

可以发现:两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。

注解方式是平时使用的最多的方式!

4.RequestMapping

@RequestMapping

  • @RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
  • 只注解在方法上面。

package com.github.subei.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class ControllerTest3 {

    @RequestMapping("/h1")
    public String test(){
        return "test";
    }

}
  • 访问路径:http://localhost:8080 / 项目名 /h1
  • http://localhost:8080/springmvc_04/h1

  • 同时注解类与方法

package com.github.subei.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/s1")
public class ControllerTest3 {
    @RequestMapping("/y1")
    public String test(Model model){
        model.addAttribute("msg","s1/y1");
        return "test";
    }
}
  • 访问路径:http://localhost:8080 / 项目名/ s1/y1 , 需要先指定类的路径再指定方法的路径;
  • http://localhost:8080/springmvc_04/s1/y1

5.RestFul

Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。


功能

  • 资源:互联网所有的事物都可以被抽象为资源;
  • 资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。分别对应 添加、 删除、修改、查询。

传统方式操作资源 :通过不同的参数来实现不同的效果!方法单一,post 和 get

  • http://127.0.0.1/item/queryItem.action?id=1 查询,GET
  • http://127.0.0.1/item/saveItem.action 新增,POST
  • http://127.0.0.1/item/updateItem.action 更新,POST
  • http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST

使用RESTful操作资源 :可以通过不同的请求方式来实现不同的效果!如下:请求地址一样,但是功能可以不同!

  • http://127.0.0.1/item/1 查询,GET
  • http://127.0.0.1/item 新增,POST
  • http://127.0.0.1/item 更新,PUT
  • http://127.0.0.1/item/1 删除,DELETE

案例测试

  1. 再新建一个类 RestFulController。

package com.github.subei.controller;

import org.springframework.stereotype.Controller;

@Controller
public class RestFulController {
    
}
  • 原来的方式!
package com.github.subei.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class RestFulController {

    // 原来的:http://localhost:8080/springmvc_04/add?p1=1&p2=9

    // 映射访问路径
    @RequestMapping("/add")
    public String index( int p1, int p2, Model model){

        int result = p1+p2;
        // Spring MVC会自动实例化一个Model对象用于向视图中传值
        model.addAttribute("msg", "加法结果:"+result);
        // 返回视图位置
        return "test";

    }
}

  1. 在Spring MVC中可以使用 @PathVariable 注解,让方法参数的值对应绑定到一个URI模板变量上。

package com.github.subei.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class RestFulController {

    // 原来的:http://localhost:8080/springmvc_04/add?p1=1&p2=9
    // 现在的:http://localhost:8080/springmvc_04/add/45/66

    @RequestMapping("/add/{p1}/{p2}")
    public String index(@PathVariable int p1, @PathVariable int p2, Model model){

        int result = p1+p2;
        // Spring MVC会自动实例化一个Model对象用于向视图中传值
        model.addAttribute("msg", "加法结果:"+result);
        // 返回视图位置
        return "test";

    }
}
  1. 测试请求并查看。
  • http://localhost:8080/springmvc_04/add/45/66

思考:使用路径变量的好处?

  • 使路径变得更加简洁;
  • 获得参数更加方便,框架会自动进行类型转换。
  • 通过路径变量的类型可以约束访问参数,如果类型不一样,则访问不到对应的请求方法,如这里访问是的路径是/add/9/k,则路径与方法不匹配,而不会是参数转换失败。

  1. 修改下对应的参数类型,再次测试
package com.github.subei.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class RestFulController {

    // 原来的:http://localhost:8080/springmvc_04/add?p1=1&p2=9
    // 现在的:http://localhost:8080/springmvc_04/add/45/66

    @RequestMapping("/add/{p1}/{p2}")
    public String index(@PathVariable int p1, @PathVariable String p2, Model model){

        String result = p1+p2;
        // Spring MVC会自动实例化一个Model对象用于向视图中传值
        model.addAttribute("msg", "字符结果:"+result);
        // 返回视图位置
        return "test";

    }
}

使用method属性指定请求类型

  • 用于约束请求的类型,可以收窄请求范围。指定请求谓词的类型如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE等。

案例测试:

  • 增加一个方法

    // 映射访问路径,必须是POST请求
    @RequestMapping(value = "/home",method = {RequestMethod.POST})
    public String index2(Model model){
        model.addAttribute("msg", "My warm home!");
        return "test";
    }
  • 使用浏览器地址栏进行访问默认是Get请求,会报错405:

  • 将POST修改为GET则正常了;
    // 映射访问路径,必须是GET请求
    @RequestMapping(value = "/home",method = {RequestMethod.GET})
    public String index2(Model model){
        model.addAttribute("msg", "My warm home!");
        return "test";
    }

Spring MVC 的 @RequestMapping 注解能够处理 HTTP 请求的方法, 比如 GET, PUT, POST, DELETE 以及 PATCH。

  • 所有的地址栏请求默认都会是 HTTP GET 类型的。
  • 方法级别的注解变体有如下几个:组合注解
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

@GetMapping 是一个组合注解,平时使用的会比较多!它所扮演的是 @RequestMapping(method =RequestMethod.GET) 的一个快捷方式。

6.每个程序员都要知道的:小黄鸭调试法

转载自《程序员的那些事》

花了一下午(或一天)在试图解决某个 Bug,后来才知道解决方案很简单,当时就是没有想到。

有个同事正好路过,看到你愁眉苦脸的,问你“怎么了呀?”

“噢,是这样的。我遇到了一个问题,点击这个控件的时……” 当你正准备和同事详细解释的时候,突然灵光一现,你话都没说完,就中断了和同事的倾诉,继续干活了。

同事微微一笑,又走开了。他并没有怪你。


「程序员的那些事」主页君相信大家都有类似的经历。遇到 Bug/问题被卡住了,拉个人过来,和他 blablabla 讲了一通,很多时候中途你就找到了解决办法。

有时候,并不一定要和人倾诉,还可以像其他东西倾诉,强迫自己把遇到的问题,详细地解释出来(一定要说出来)。

其实呢。这种方法,有一个术语:小黄鸭调试法(Rubber Duck Debugging)。

维基百科有解释:小黄鸭调试法是软件工程中使用的调试代码方法之一。就是在程序的调试、纠错或测试过程中,耐心地向小黄鸭解释每一行程序的作用,以此来激发灵感

名称由来

此概念是参照于一个故事。故事中程序大师随身携带一只小黄鸭,在调试代码的时候会在桌上放上这只小黄鸭,然后详细地向鸭子解释每行代码。