SpringMVC学习笔记(一)

SpringMVC学习笔记(一)

内容包括:

  1. SpringMVC执行流程
  2. 注解开发@controller@RequestMapping
  3. 请求参数处理
  4. 数据输出处理
  5. 视图解析

参考视频:
B站 尚硅谷雷丰阳大神的Spring、Spring MVC、MyBatis课程

1. SpringMVC概述

MVC:

  • Model(模型): 数据模型,提供要展示的数据,:Value Object(数据Dao) 和 服务层(行为Service),提供数据和业务。
  • View(视图): 负责进行模型的展示,即用户界面
  • Controller(控制器): 调度员,接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。

SpringMVC的特点:

  • Spring为展现层提供的基于MVC设计理念的Web框架
  • SpirngMVC通过一套MVC注解,让POJO成为处理请求的控制器,而无须实现任何接口
  • 支持REST风格的URL请求
  • 采用了松散耦合可拔插组件结构,扩展性和灵活性

2. HelloWorld

1) 导入依赖

spring-webmvc的maven依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>

2) 配置web.xml , 注册DispatcherServlet

DispatcherServlet:前端控制器,负责请求分发。

要绑定Spring的配置文件

<?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">

    <!--注册DispatcherServlet,请求分发器(前端控制器)-->
    <servlet>
        <servlet-name>springDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--绑定Spring配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-config.xml</param-value>
        </init-param>
        <!--启动级别为1,即服务器启动后就启动-->
        <!--值越小优先级越高,越先创建对象-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- /  拦截所有的请求;(不包括.jsp)-->
    <!-- /* 拦截所有的请求;(包括.jsp,一旦拦截jsp页面就不能显示了)-->
    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

3) Spring配置文件

Spring的配置文件Springmvc-config.xml。

  1. 开启了包扫描,让指定包下的注解生效,由IOC容器统一管理
  2. 配置了视图解析器InternalResourceViewResolver,这里可以设置前缀和后缀,拼接视图名字
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启包扫描,让指定包下的注解生效,由IOC容器统一管理-->
    <context:component-scan base-package="com.xiao.controller"/>

    <!--配置视图解析器,拼接视图名字,找到对应的视图-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/page/"/>
        <!--后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

4) 编写controller层

HelloController类:

  1. @Controller:告诉Spirng这是一个控制器,交给IOC容器管理

  2. @RequestMapping("/hello01"):/ 表示项目地址,当请求项目中的hello01时,返回一个/WEB-INF/page/success.jsp页面给前端

@Controller
public class HelloController {

    @RequestMapping("/hello01")
    public String toSuccess(){
        System.out.println("请求成功页面");
        return "success";
    }
    @RequestMapping("/hello02")
    public String toError() {
        System.out.println("请求错误页面");
        return "error";
    }
}

5) 编写跳转的jsp页面

项目首页 index.jsp,两个超链接,分别发出hello01和hello02的请求

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>

  <a href="hello01">点这里去成功页面</a>
  <a href="hello02">点这里去失败页面</a>

  </body>
</html>

成功页面success.jsp和失败页面error.jsp,要注意文件的路径/WEB-INF/page/…jsp,与上面的保持一致

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>成功页面</title>
</head>

<body>

<h1>这里是成功页面</h1>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>错误页面</title>
</head>

<h1>这里是错误页面</h1>
<body>

</body>
</html>

6) 访问

启动项目:

在这里插入图片描述

点击去成功页面,可以看到发出了/hello01请求,页面转发到/WEB-INF/page/success.jsp,控制台输出了请求成功页面。

在这里插入图片描述

3. 实现细节

3.1 运行流程

  1. 客户端点击链接发送请求:http://localhost:8080/hello01;

  2. 来到tomcat服务器;

  3. SpringMVC的前端控制器收到所有请求;

  4. 看请求地址和@RequestMapping标注的哪个匹配,来找到底使用哪个类的哪个方法来处理;

  5. 前端控制器找到目标处理器类和目标方法,直接利用反射执行目标方法;

  6. 方法执行完后有一个返回值,SpringMVC认为这个返回值就是要去的页面地址;

  7. 拿到方法返回值后,视图解析器进行拼串得到完整的页面地址

  8. 得到页面地址,前端控制器帮我们转发到页面

3.2 @RequestMapping

01 标注在方法上

告诉SpringMVC这个方法用来处理什么请求。

@RequestMapping("/hello01")中的 /可以省略,就是默认从当前项目下开始。

02 标注在类上

表示为当前类中的所有方法的请求地址,指定一个基准路径。toSuccess()方法处理的请求路径是/haha/hello01

@Controller
@RequestMapping("/haha")
public class HelloController {

    @RequestMapping(value = "/hello01")
    public String toSuccess(){
        System.out.println("请求成功页面");
        return "success";
    }
}
03 规定请求方式

method属性规定请求方式,默认是所求请求方式都行。method = RequestMethod.GET,method = RequestMethod.POST。

如果方法不匹配会报:HTTP Status 405 错误 – 方法不被允许

@RequestMapping(value = "/hello01",method = RequestMethod.GET)
public String toSuccess(){
    System.out.println("请求成功页面");
    return "success";
}

组合用法

  • @GetMapping 等价于 @RequestMapping(method =RequestMethod.GET)
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping
04 规定请求参数

params属性规定请求参数。会造成错误:HTTP Status 400 – 错误的请求

不携带该参数,表示参数值为null;携带了不给值表示参数值是空串

//必须携带username参数
@RequestMapping(value = "/hello03",params ={"username"})
//必须不携带username参数
@RequestMapping(value = "/hello03",params ={"!username"})
//必须携带username参数,且值必须为123
@RequestMapping(value = "/hello03",params ={"username=123"})
//username参数值必须不为123,不携带或者携带了不是123都行
@RequestMapping(value = "/hello03",params ={"username=!123"})
05 规定请求头

headers属性规定请求头。其中User-Agent:浏览器信息

谷歌浏览器:User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.3

06 Ant风格URL

URL地址可以写模糊的通配符,模糊和精确多个匹配情况下精确优先。

?:替代任意一个字符

@RequestMapping( "/hello0?") /

*:替代任意多个字符或一层路径

@RequestMapping( "/hello0*")   //任意多个字符
@RequestMapping( "/a/*/hello01")  //一层路径

**:替代任意多层路径

@RequestMapping( "/a/*/*/hello01")  //任意多层路径

3.3 Spring配置文件的默认位置

默认位置是 /WEB-INF/xxx-servlet.xml,其中xxx是自己在web.xml文件中配置的servlet-name属性。

当然也可以手动指定文件位置。

3.4 url-pattern

/ 拦截所有的请求,不拦截jsp

/* 拦截所有的请求,包括*.jsp,一旦拦截jsp页面就不能显示了。. jsp是tomcat处理的事情

看Tomcat的配置文件web.xml中,有DefaultServlet和JspServlet,

  • DefaultServlet是Tomcat中处理静态资源的,Tomcat会在服务器下找到这个资源并返回。如果我们自己配置url-pattern=/,相当于禁用了Tomcat服务器中的DefaultServlet,这样如果请求静态资源,就会去找前端控制器找@RequestMapping,这样静态资源就不能访问了。解决办法:

    <!-- 告诉Spring MVC自己映射的请求就自己处理,不能处理的请求直接交给tomcat -->
    <mvc:default-servlet-handler />
    <!--开启MVC注解驱动模式,保证动态请求和静态请求都能访问-->
    <mvc:annotation-driven/>
    
  • JspServlet,保证了jsp可以正常访问

<servlet>
    <servlet-name>default</servlet-name>
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
    <init-param>
        <param-name>debug</param-name>
        <param-value>0</param-value>
    </init-param>
    <init-param>
        <param-name>listings</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>


<servlet>
     <servlet-name>jsp</servlet-name>
     <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
     <init-param>
         <param-name>fork</param-name>
         <param-value>false</param-value>
      </init-param>
      <init-param>
          <param-name>xpoweredBy</param-name>
          <param-value>false</param-value>
      </init-param>
      <load-on-startup>3</load-on-startup>
</servlet>

    <servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspx</url-pattern>
    </servlet-mapping>

4. REST风格

4.1 概述

REST就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。其强调HTTP应当以资源为中心,并且规范了URI的风格;规范了HTTP请求动作(GET/PUT/POST/DELETE/HEAD/OPTIONS)的使用,具有对应的语义。

  • 资源(Resource):网络上的一个实体,每种资源对应一个特定的URI,即URI为每个资源的独一无二的识别符;
  • 表现层(Representation):把资源具体呈现出来的形式,叫做它的表现层。比如txt、HTML、XML、JSON格式等;
  • 状态转化(State Transfer):每发出一个请求,就代表一次客户端和服务器的一次交互过程。GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源。

在参数上使用 @PathVariable 注解,可以获取到请求路径上的值,也可以写多个

    @RequestMapping(value = "/hello04/username/{id}")
    public String test2(@PathVariable("id") int id){
        System.out.println(id);
        return "success";
    }

4.2 页面上发出PUT请求

对一个资源的增删改查用请求方式来区分:

  • /book/1 GET:查询1号图书

  • /book/1 DELETE:删除1号图书

  • /book/1 PUT:修改1号图书

  • /book POST:新增图书

页面上只能发出GET请求和POST请求。将POST请求转化为put或者delete请求的步骤:

  1. 把前端发送方式改为post 。

  2. 在web.xml中配置一个filter:HiddenHttpMethodFilter过滤器

  3. 必须携带一个键值对,key=_method, value=put或者delete

<!--这个过滤器的作用 :就是将post请求转化为put或者delete请求-->
<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>
<form action="hello03" method="post">
  <input type="hidden" name="_method" value="delete">
  <input type="submit" name="提交">
</form>

高版本Tomcat会出现问题:JSPs only permit GET POST or HEAD,在页面上加上异常处理即可

<%@ page contentType="text/html;charset=UTF-8" language="java"  isErrorPage="true" %>

5 请求参数处理

5.1 传入参数

1. 如果提交的参数名称和处理方法的参数名一致,则无需处理,直接使用

提交数据 : http://localhost:8080/hello05?username=zhangsan,控制台会输出zhangsan

@RequestMapping("/hello05")
public String test03(String username) {
    System.out.println(username);
    return "success";
}

2. 提交的参数名称和处理方法的参数名不一致,使用@RequestParam注解

注解@RequestParam可以获取请求参数,默认必须携带该参数,也可以指定required=false,和没携带情况下的默认值defaultValue

@RequestMapping("/hello05")
public String test03(@RequestParam(value = "username",required = false, defaultValue ="hehe" ) String name) {
    System.out.println(name);
    return "success";
}

还有另外两个注解:

  • @RequestHeader:获取请求头中的信息,比如User-Agent:浏览器信息

    @RequestMapping("/hello05")
    public String test03(@RequestHeader("User-Agent" ) String name) {
        System.out.println(name);
        return "success";
    }
    
  • @CookieValue:获取某个cookie的值

    @RequestMapping("/hello05")
    public String test03(@CookieValue("JSESSIONID" ) String name) {
         System.out.println(name);
         return "success";
    }
    

5.2 传入一个对象

传入POJO,SpringMVC会自动封装,**提交的表单域参数必须和对象的属性名一致,否则就是null,请求没有携带的字段,值也会是null。**同时也还可以级联封装。

新建两个对象User和Address:

public class User {
    private String username;
    private Integer age;
    private Address address;
    //....
}
public class Address {
    private String name;
    private Integer num;
        //....
}

前端请求:

<form action="hello06" method="post">
    姓名: <input type="text" name="username"> <br>
    年龄: <input type="text" name="age"><br>
    地址名:<input type="text" name="address.name"><br>
    地址编号:<input type="text" name="address.num"><br>
    <input type="submit" name="提交">
</form>

后端通过对象名也能拿到对象的值,没有对应的值则为null

@RequestMapping("/hello06")
public String test03(User user) {
    System.out.println(user);
    return "success";
}

5.3 乱码问题

一定要放在在其他Filter前面。

<filter>
   <filter-name>encoding</filter-name>
   <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
   <init-param>
       <param-name>encoding</param-name>
       <param-value>utf-8</param-value>
   </init-param>
</filter>
<filter-mapping>
   <filter-name>encoding</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

5.4 传入原生ServletAPI

处理方法还可以传入原生的ServletAPI:

@RequestMapping("/hello07")
public String test04(HttpServletRequest request, HttpSession session) {
    session.setAttribute("sessionParam","我是session域中的值");
    request.setAttribute("reqParam","我是request域中的值");
    return "success";
}

通过EL表达式获取到值,${requestScope.reqParam}

<%@ page contentType="text/html;charset=UTF-8" language="java"  isErrorPage="true" %>
<html>
<head>
    <title>成功页面</title>
</head>

<body>

<h1>这里是成功页面</h1>
${requestScope.reqParam}
${sessionScope.sessionParam}
</body>
</html>

6. 数据输出

6.1 Map、Model、ModerMap

实际上都是调用的 BindingAwareModelMap(隐含模型),将数据放在请求域(requestScope)中进行转发,用EL表达式可以取出对应的值。

@RequestMapping("/hello01")
public String test01 (Map<String,Object> map){
    map.put("msg","HelloWorld!");
    return "success";
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

pageScope:  ${pageScope.msg}

requestScope :   ${requestScope.msg}

sessionScope:     ${sessionScope.msg}

applicationScope:   ${applicationScope.msg}

</body>
</html>

【补充】jsp的4个作用域 pageScope、requestScope、sessionScope、applicationScope的区别:

  • page指当前页面有效。在一个jsp页面里有效

  • request 指在一次请求的全过程中有效,即从http请求到服务器处理结束,返回响应的整个过程,存放在HttpServletRequest对象中。在这个过程中可以使用forward方式跳转多个jsp。在这些页面里都可以使用这个变量。

  • Session是用户全局变量,在整个会话期间都有效。只要页面不关闭就一直有效(或者直到用户一直未活动导致会话过期,默认session过期时间为30分钟,或调用HttpSession的invalidate()方法)。存放在HttpSession对象中

  • application是程序全局变量,对每个用户每个页面都有效。存放在ServletContext对象中。它的存活时间是最长的,如果不进行手工删除,它们就一直可以使用

6.2 ModerAndView

返回一个模型视图对象ModerAndView, 既包含视图信息(页面地址),也包含模型数据(给页面带的数据)

@RequestMapping("/hello04")
public ModelAndView test04 (){
   //新建一个模型视图对象,也可以直接传入名字
   ModelAndView mv = new ModelAndView();
   //封装要显示到视图中的数据
   //相当于req.setAttribute("msg",HelloWorld!);
   mv.addObject("msg","HelloWorld!");
   //设置视图的名字,相当于之前的return "success";
   mv.setViewName("success");
   return mv;
}

6.3 @SessionAttributes

给Session域中携带数据使用注解@SessionAttributes,只能标在类上,value属性指定key,type可以指定保存类型。这个注解会引发异常一般不用,就用原生API

@SessionAttributes(value = "msg"):表示给BindingAwareModelMap中保存key为msg的数据时,在session中也保存一份;

@SessionAttributes(types = {String.class}):表示只要保存String类型的数据时,给session中也放一份。

//表示给BindingAwareModelMap中保存key为msg的数据时,在session中也保存一份
@SessionAttributes(value = "msg")
@Controller
public class outputController {
    @RequestMapping("/hello01")
    public String test01 (Map<String,Object> map){
        map.put("msg","HelloWorld!");
        return "success";
    }
}

6.4 @ModelAttribute

方法入参标注该注解后,入参的对象就会放到数据模型中,会提前于控制方法先执行,并发方法允许的结果放在隐含模型中。

处理这样的场景:

前端传来数据,SpringMVC自动封装成对象,实际上是创建了一个对象,每个属性都有默认值,然后将请求参数中对应是属性设置过来,但是如果没有的值将会是null,如果拿着这个数据去更新数据库,会造成其他字段也变为null。因此希望使用@ModelAttribute,会在目标方法执行前先做一些处理

@ModelAttribute
public void  myModelAttribute(ModelMap modelMap){
    System.out.println("modelAttribute方法执行了");
    //提前做一些处理
    User user = new User("zhangsan",20);
    //保存一个数据到BindingAwareModelMap中,目标方法可以从中取出来
    modelMap.addAttribute("user",user);
}

@RequestMapping("/hello05")
public void  test05(@ModelAttribute("user") User user){
    System.out.println("目标方法执行了");
    //在参数上加上@ModelAttribute注解,可以拿到提前存入的数据
    System.out.println(user);

}

6.5 @ResponseBody

在控制器类中,在方法上使用**@ResponseBody**注解可以不走视图解析器,如果返回值是字符串,那么直接将字符串写到客户端;如果是一个对象,会将对象转化为JSON串,然后写到客户端。

或者在类上加 @RestController注解,可以让类中的所有方法都不走视图解析器,直接返回JSON字符串

7. 再谈SpringMVC执行流程

7.1 前端控制器DisatcherServlet

在这里插入图片描述

7.2 SpringMVC执行流程

  1. 用户发出请求,DispatcherServlet接收请求并拦截请求。

  2. 调用doDispatch()方法进行处理:

    1)getHandler():根据当前请求地址,在HandlerMapping(处理器映射器)中找到能处理这个请求的目标处理器类(处理器);

    2)getHandlerAdapter():根据当前处理器类找到当前类的HandlerAdapter(处理器适配器)

    3)使用刚才获取到的适配器(AnnotationMethodHandlerAdapter)执行目标方法;

    4)目标方法执行后,会返回一个ModerAndView对象

    5)根据ModerAndView的信息转发到具体页面,并可以在请求域中取出ModerAndView中的模型数据

HandlerMapping为处理器映射器,保存了每一个处理器能处理哪些请求的映射信息,handlerMap

HandlerAdapter为处理器适配器,能解析注解方法的适配器,其按照特定的规则去执行Handler

在这里插入图片描述

7.3 SpringMVC的九大组件

  • multipartResolver:文件上传解析器
  • localeResolver:区域信息解析器,和国际化有关
  • themeResolver:主题解析器
  • handlerMappings:handler的映射器
  • handlerAdapters:handler的适配器
  • handlerExceptionResolvers:异常解析功能
  • viewNameTranslator:请求到视图名的转换器
  • flashMapManager:SpringMVC中允许重定向携带数据的功能
  • viewResolvers:视图解析器
    @Nullable
    private MultipartResolver multipartResolver;
    @Nullable
    private LocaleResolver localeResolver;
    @Nullable
    private ThemeResolver themeResolver;
    @Nullable
    private List<HandlerMapping> handlerMappings;
    @Nullable
    private List<HandlerAdapter> handlerAdapters;
    @Nullable
    private List<HandlerExceptionResolver> handlerExceptionResolvers;
    @Nullable
    private RequestToViewNameTranslator viewNameTranslator;
    @Nullable
    private FlashMapManager flashMapManager;
    @Nullable
    private List<ViewResolver> viewResolvers;

8. 视图解析

通过SpringMVC来实现转发和重定向。

  • 直接 return “success”,会走视图解析器进行拼串
  • 转发:return “forward:/succes.jsp”;直接写绝对路径,/表示当前项目下,不走视图解析器
  • 重定向:return “redirect:/success.jsp”;不走视图解析器
@Controller
public class ResultSpringMVC {
   @RequestMapping("/hello01")
   public String test1(){
       //转发
       //会走视图解析器
       return "success";
  }

   @RequestMapping("/hello02")
   public String test2(){
       //转发二
       //不走视图解析器
       return "forward:/success.jsp";
  }

   @RequestMapping("/hello03")
   public String test3(){
       //重定向
       //不走视图解析器
       return "redirect:/success.jsp";
  }
}

使用原生的ServletAPI时要注意,/路径需要加上项目名才能成功

   @RequestMapping("/result/t2")
   public void test2(HttpServletRequest req, HttpServletResponse resp) throwsIOException {	
       //重定向
       resp.sendRedirect("/index.jsp");
  }

   @RequestMapping("/result/t3")
   public void test3(HttpServletRequest req, HttpServletResponse resp) throwsException {
       //转发
       req.setAttribute("msg","/result/t3");
       req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,resp);
  }

mvc:view-controller:直接将请求映射到某个页面,不需要写方法了:

<mvc:view-controller path="/toLogin" view-name="login"/>
<!--开启MVC注解驱动模式-->
<mvc:annotation-driven/>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值