SpringMVC总结-1

基础知识简介

POJO:Plain Old Java Object

<!-- /和/*都是拦截所有请求
不同点:/不拦截.jsp请求,而/*会-->

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

前端控制器 DispatcherServlet

tomcat有编译功能,处理*.jsp是tomcat的事

所有项目的小web.xml 继承自服务器(tomcat)的大web.xml

大web.xml的DefaultServlet的<url-pattern>是"/"

大web.xml中还有一个JspServlet,用来拦截jsp文件

静态资源:除jsp和Servlet之外的都是静态资源

基础注解

@RequestMapping 标注位置

​ 可以标注在类或者方法上面

​ @RequestMapping("/sdfsd/sdfgr")标注在类上面时就是为该类的所有方法加一层路径/sdfsd/sdfgr

@RequestMapping的各个属性:

  • method:限定请求方式:GET、POST等等请求方式,默认值是全部接受
  • params:规定请求参数
    • params:eg:params={“username”}:表示发送请求时必须带上名为username的参数,没带则404,{“username=123”}或者{“username!=123”}都是可以使用的(规定请求信息中必须有username且值为123或不为123)
    • !params:eg:params={"!username"}:必须不带username参数
    • headers:规定请求头
      • User-Agent:描述浏览器信息,比如说可以区分火狐和Chrome
      • 等等的header参数都可以进行设置
      • headers={“User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36”}意为只允许Chrome访问
  • 支持Ant风格,即支持基本的正则匹配
    • ?:代表一个字符
    • *:代表多个字符或者一层路径
    • **:代表任意多层路径
    • 例如:@RequestMapping("/**/hello")表示接收任意路径的hello请求

@PathVariable 映射URL绑定的占位符

可以有RequestMapping("/hello/{id}") 这种形式,其中{}起占位符的作用,id是该占位符的名字,占位符和Ant风格的"*"有相似点,都可以代表不同名字。不同之处是,占位符所代表的值到后面还可以拿到。方式为:

@RequestMapping("/hello/{id}")
public void hi(@PathVariable("id")String id){
    System.out.println(路径上的占位符是:""+id);
}

REST风格

概念解释

  • REST:Representational State Transfer (资源表现层转化)是目前互联网最流行的一种互联网架构。

  • 资源(Resources):网络上的一个实体,或者说是网络上的一个具体信息。URI(统一资源定位符)是每一个资源的独一无二的识别符

  • 表现层(Representation):把资源具体呈现出来的形式。

  • 状态转化(State Transfer):每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器发生"状态转化"(State Transfer)。而这种转化时建立在表现层之上的,所以就是"表现层状态转化"。具体说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。他们分别对应四种基本操作:GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源。

  • 对比

    • 一般方式:
      • /getBook?id=1:查询1号图书
    • REST风格:
      • /book/1:使用GET方式发送,查询1号图书
    • 简洁的URI提交请求,以请求方式区分对资源的操作

问题:页面上只能发起GET、POST两种请求

  • 解决办法:使用Rest来构建增删改查系统

  • 如何从页面发出DELETE和PUT请求

    1. SpringMVC中有一个Filter,他可以把普通的请求转化为规定形式的请求;配置(web.xml):

      <filter>
          <filter-name>smart</filter-name>
          <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
      </filter>
      
      <filter-mapping>
          <filter-name>smart</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>
      
    2. 发出请求:

      1. 创建POST类型的表单
      2. 表单中携带_method的参数(<input name="_method" value=“delete”>)
      3. 这个_method的值就是DELETE和PUT

Tomcat8.0及以上的版本不允许使用DELETE、PUT这些请求

解决办法:

在jsp文件的头上加 isErrorPage=“true”

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib url="http://java.sun.com/jsp/jstl/core" prefix="c" isErrorPage="true" %>

获取页面发送请求时所带的参数

@RequestParam

页面向后端发送请求时,一般会带有一些参数,怎么获取这些参数?

  1. 默认方式:直接给方法入参上写一个和请求所带参数名字相同的变量,这个变量就来接收请求参数的值

  2. 使用@RequestParam(value=“username”,required=false,defaultValue=“默认值”)来获取请求中的指定参数

    @RequestMapping("\hello")
    public void hello(@RequestParam(value="username",required=false,defaultValue="你是猪")String userName){
        System.out.println(userName);
    }
    /*@RequestParam中
    value的值必须设置的,用来指定请求中的参数名;
    requied用来规定请求是否必须携带value指定的参数,非必须;
    defaultValue用来设置目标参数的默认值
    */
    

和@PathVariable()的区别:

​ 对请求url:/book/{user}?user=admin

​ @PathVariable(“user”)获取的是url路径上占位符中的值,

​ @RequestParam(“user”)获取的是url中参数的值,即?之后的值


@RequestHeader

获取请求头中的某个key的值

使用方式与@PathVariable一样,都是在方法的参数前面加,注解的参数也都一致

@RequestHeader(“User-Agent”,required=false,defaultValue=“默认值”)

等同于HttpServletRequest request:request.getHeader(“User-Agent”)

@RequestMapping("\hello")
public void hello(@RequestHeader("User-Agent")String User-Agent){
    System.out.println(User-Agent);
}

// 这二者是等价的
@RequestMapping("\hello")
public void hello(HttpServletRequest request){
    String userAgent=request.getheader("User-Agent");
    System.out.println(User-Agent);
}

@CookieValue

获取某个cookie的值

使用方式都与上面两个都一致,

@CookieValue(“JSESSIONID”,required=false,defaultValue=“默认值”)

第一次访问某个页面时,Headers中没有JSESSIONID

传入POJO

  • 如果我们的请求参数是一个POJO
    • 这句话是指,我们在请求中出=传入的一系列数据恰好可以构成一个对象
    • 对下面这个例子来说就是请求中同时传入了name、author、price、country、
      province

SpringMVC会自动为这个POJO进行赋值

  1. 将POJO中的每一个属性,从request中获取出来,并封装即可
  2. 还可以级联封装
// book.java

class Book{
    private String name;
   	private String author;
    private double price;
    private Address address;

    //....省略setter和getter方法
}
// Address.java

class Address{
    private String country;
    private String province;
    
    //...省略setter和getter方法
}
// Conroller.java

class Controller{
    @RequestMapping("/Book")
    public void addBook(Book book){
        System.out.println("图书:"+book);
    }
}
// 就是这样,直接把对象放到参数里面就可以
// Book类中有Address类型的成员变量address,也可以对address中的属性进行赋值,是所谓级联封装
// 注意:Book类中的成员变量的名字要和页面请求中的参数名一致

对原生API的支持

SpringMVC可以直接在参数上写原生API

  1. HttpServletRequest

  2. HttpServletResponse

  3. HttpSession

    下面几个不常用


  1. java.security.Principal:和HTTP安全协议有关

  2. Locale:国际化有关的区域信息

  3. InputStream:request.getInputStream

  4. OutputStream: response.getOutputStream

  5. Reader:request.getReader

  6. Writer:response.getWriter

乱码处理

提交的数据可能有乱码,首先要分清是请求乱码还是响应乱码

请求乱码:

​ GET请求:改Tomcat中的server.xml在8080端口处添加URIEncoding=“UTF-8”(Eclipse中是这样,Intellij中需要到tomcat的根目录下修改,具体百度)

​ POST请求:在web.xml中配置filter

<!--web.xml-->

<filter>
    <filter-name>filter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!--encoding:指定解决POST请求乱码-->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <!--forceEncoding:解决响应乱码,但是这里只设置了字符类型,如果想要设置内容类型还得在发送响应时设置,下面有介绍-->
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

响应乱码

在发送响应时,设置内容类型和编码类型:

​ response.setContentType(“text/html;charset=utf-8”)

@RequestMapping(value = "/hello")
public String hello(HttpServletResponse response){
    response.setContentType("text/html;charset=utf-8");
    return "hello";
}

配置多个filter的情况下,按照filter的配置顺序进行处理.

处理编码问题的filter放在其他filter的前面才能有效的处理乱码问题

发送数据到页面

如何将数据输出到页面

  1. 使用原生API:Request和Session
  2. 在方法处传入Map,Model或者ModelMap类型的参数,给这些参数里面保存的所有数据都会放在域中.可以在页面获取(其实三者都放在了request域中)
// Conroller.java

class Controller{
    @RequestMapping("/Book01")
    public String addBook(Map map){
        map.put("hello","你好");
        System.out.println("图书:"+book);
        return "success";
    }
    
    @RequestMapping("/Book03")
    public String addBook(Model model){
        model.addAttribute("hello","你好");
        System.out.println("图书:"+book);
        return "success";
    }
    
    @RequestMapping("/Book04")
    public String addBook(ModelMap modelMap){
        modelMap.addAttribute("hello","你好");
        System.out.println("图书:"+book);
        return "success";
    }
}

三者之间的关系:对三者的类型(Map.getClass)进行输出后可以发现,三者的类型都是BindingAwareModelMap

BindingAwareModelMap中保存的信息都会放在请求域中

Map(jdk的interface) Model(spring的interface

ModelMap是一个类,继承了LinkedHashMap,明显是和Map是一脉 ,

  1. 设置方法的返回值为ModelAndView(信息放在了请求域中,将想要发送的信息放入ModelAndView中

    @RequestMapping("/Book02")
    public ModelAndView addBook(ModelMap modelMap){
        // 视图解析器会帮我们拼串最终得到页面的真实地址
        ModelAndView model = new ModelAndView("success");
        // 或者ModelAndView model=new ModelAndView();
        // model.setViewName("success");
        model.addObject("hello","你好坏");
        return model;
    }
    

    数据一般可以放在request,session,application中,session中的数据多了之后容易爆掉,application中的数据对所有人可见,所以一般都是把数据放在request中

  2. SpringMVC中提供了一种可以临时给Session域中保存数据的方式:@SessionAttributes (只能标在类上)

    @SessionAttributes(value=“hello”)

    加上这个注解后,在给BindingAwareModelMap(即Map,Model,ModelMap),或者ModelAndView中保存key为"hello"的数据时,同时(就是必须先在某个方法中给BindAwareModelMap中添加一个值)会在Session中放一份

    想指定多个value怎么办: @SessionAttributes(value={“hello”,“msg”})

    @SessionAttributes中还有一个参数是type,用来指定数据的类型

    不推荐使用,可能会引发异常,给Session中放数据时最好使用原生API

    如果一定要使用:

    1. 保证隐含模型中有@SessionAttributes标注的key
    2. 如果隐含模型中没有,session还说有就一定要有(各种方法给他加进去),否则抛异常
  3. @ModelAttribute

    很复杂,现在已经没人用了

    应用场景:数据库进行更新时,是全字段更新,如果我们只从网页拿到部分数据,直接用这些数据对数据库进行更新时,该条记录的其他值就会被设置为null,这显然是不允许的.

    解决办法就是,提前将被更新的记录从数据库中拿出保存到一个对象中,然后对对象进行操作,更新需要更新的属性,最后再将该对象保存到数据库中.

    1. SpringMVC要封装请求参数的Book对象不应该是自己new出来的,而应该是从数据库中拿到的准备好的对象
    2. 再使用这个对象封装请求参数

    @ModelAttribute标注位置:

    1. 参数:取出隐含模型(后面会讲)中保存的数据
    2. 方法:这个方法就会提前于类中其他方法运行(随便调用一个目标方法,该方法都会在目标方法之前运行),我们就可以这样提前查出数据库中的记录

    使用方法:

    1. 首先要有一个从数据库中拿数据,并保存到隐含模型中的方法
    2. 之后要有从隐含模型中拿出数据对象,对对象进行更新,并保存到数据库.如果隐含模型中没有,Spring会自己new一个对象进行封装.如果在拿数据时没有指定key,那么Spring会以对应参数的返回值类型的首字母小写为key拿数据
    @ModelAttribute
    public void getData(Model model){
        // 从数据库中拿数据,保存到隐含模型中
        
        // 通常的做法是将拿出的数据保存到对应的对象中
        // 在将对象保存到Model(Map,ModelMap)中
        // 此处就不详细写了,假装book就存储着拿出的数据
        model.add("book1",book);
        // 以 book1 为key将数据对象book放入隐含模型中
    }
    
    @RequestMapping("hello")
    // 在参数中,用key拿出数据对象
    public void hello(@ModelAttribute("book1")Book book){
        
    }                											
    

隐含模型

  1. Map,Model或者ModelMap三种类型都是对三者的类型(都是BindingAwareModelMap.
  2. 更加有意思的一点是,不论你这个类中有多少个方法,设置了多少个Map,Model或者ModelMap(一个方法中只能有一个),其实使用的都是同一个BindingAwareModelMap对象.
  3. BindingAwareModelMap在Spring运行过程中全程存在,被称为隐含模型.

视图解析

有前缀的返回值独立解析,不使用视图解析器进行拼串

forward转发

forward:转发到一个页面

@RequestMapping("/hello1")
public String hello1(){
    return "forward:/success1.jsp";
}
// "forward:/success1"表示取当前项目路径(一般路径最后的部分是WEB-INF)下的success1,
// 一定加上"/"如果不加/,就表明使用相对路径,容易出错

@RequestMapping("/hello2")
public String hello2(){
   return "forward:/hello1"
}
// 可以通过forward将请求从hello2转发到hello1

redirect重定向

原生的Servlet重定向需要加上项目名:response.sendRedirect("/项目名/hello1.jsp")

redirect:重定向的路径(不需要前面的项目名)SpringMVC会帮我们自动拼接项目名(一般路径最后的部分是WEB-INF).

@RequestMapping("/hello1")
public String hello1(){
   return "redirect:/success.jsp";
}

@RequestMapping("/hello2")
public String hello2(){
   return "redirect:/hello1";
}
// 可以通过redirect将请求从hello2重定向到hello1

返回其他路径下的jsp

我们配置好视图解析器之后,视图解析器会帮我们自动进行拼串,但是视图解析器中的前缀prefix路径是固定的,那我们想返回其他路径下的jsp文件该怎么办呢?

  1. 使用相对路径:"…"表示返回上一层路径

    @RequestMapping("/hello1")
    public String hello1(){
        //比如前缀是 WEB-INF/pages
        // 我们想返回WEB-INF/success.jsp
        // 那么"../success.jsp"就等同于WEB-INF/success.jsp
       return "../success.jsp";
    }
    
  2. 返回值中使用前缀"forward",“redirect”

    @RequestMapping("/hello1")
    public String hello1(){
        // 我们想返回WEB-INF/success.jsp
       return "forward:/success.jsp";
    }
    

一些原理

  • 请求处理方法执行完成后,最终返回一个 ModelAndView 对象。对于那些返回 String,View 或 ModeMap 等类型的处理方法,SpringMVC 也会在内部将它们装配成一个 ModelAndView 对象,它包含了逻辑名和模型对象的视图

  • Spring MVC 借助视图解析器ViewResolver)得到最终的视图对象(View),最终的视图可以是 JSP ,也可能是 Excel、JFreeChart 等各种表现形式的视图

  • 对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚焦在生产模型数据的工作上,从而实现 MVC 的充分解耦

  • 视图:视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户

国际化

使用国际化Jstl包

导入Jstl包后,在创建View时SpringMVC会自动改为创建JstlView

JavaWeb的国际化步骤:

  1. 得到一个Locale对象

  2. 使用ResourceBundle绑定国际化资源文件

  3. 使用ResourceBundle.getString(“key”);获取到国际化配置文件中的值

  4. web页面的国际化,fmt标签库来做

    <fmt:setLocale>

    <fmt:setBundle>

    <fmt:message>

JstlView 进行国际化的步骤:

  1. 写好国际化资源文件

  2. 让Spring管理国际化资源

    <!-- 配置文件-->
    
    <!--让SpringMVC管理国际化资源文件:配置资源文件管理器-->
    <!--id 必须命名为"messageSource"-->
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <!--basename 用来指定基础名,比如说国际化文件的全名是:i18n_zh_CN.properties,基础名就是:i18n-->
        <property name="basename" value="i18n"></property>
    </bean>
    

    国际化资源文件命名规范:https://blog.csdn.net/qq_34419607/article/details/100114102

  3. 直接去页面使用<fmt:message>拿资源就可以

    在页面中(jsp)使用fmt标签库

    <%@ page language="java" contentType="text/html; charset=UTF-8"%>
    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <%--添加fmt标签库--%>
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    <h4>Sucess Page</h4>
    <br><br>
    
    <%--从properties文件中拿属性--%>    
    <fmt:message key="i18n.username"/>
    
    <br><br>
    <fmt:message key="i18n.password"/>
    
    </body>
    </html>
    

注意:

  • 一定要经过SpringMVC的视图解析流程(不能在地址栏中直接使用url的方式进行访问),SpringMVC会创建jstlView进行快速国际化

  • 在返回值中,不能使用forward,redirect前缀,我们之前说过有前缀的返回值独立解析,不使用视图解析器进行拼串,使用这两个前缀,SpringMVC会创建另外两种View,而不是jstlView


在配置中进行页面转发

场景:因为jstl的使用必须要经过视图解析流程,那么就会有很多方法只是为了转发一下页面而存在,这没什么意义,解决办法就是在xml中进行配置.

// 就比如这种方法,没有意义嘛,纯粹为了进行转发页面而存在
@RequestMapping("/toLoginPage")
public String toLogin(){
    return "login";
}
<!--path 指定哪个请求
       view-name:指定映射给哪个视图-->
<mvc:view-controller path="/toLoginPage" view-name="login"></mvc:view-controller>

<!--当使用配置view-controller后,其他基于@RequestMapping注解的方法会失效,所以要在这里开始注解模式-->
<!--开启MVC注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>

现象:国际化信息的显示是按照浏览器带来的语言信息决定的(得到源码中看):

​ Locale locale = request.getLocale();// 获取到浏览器的区域信息

SpringMVC中区域信息是由区域信息解析器得到的:private Localeresolver localeresolver

默认会用一个AcceptHeaderLocaleResolver,所有用到区域信息的地方都是用AcceptHeaderLocaleResolver 获取的

实现点击链接切换国际化(只有大概描述)

1. 实现自己的区域解析器(实现AcceptHeaderLocaleResolver)

可以模仿默认的LocaleResolver去写

一些介绍:

  • AcceptHeaderLocaleResolver使用请求头的区域信息

  • FixedLocaleResolver 使用系统默认的区域信息

  • SessionLocaleResolver 从Session中拿区域信息,可以根据请求参数创建locale对象,把他放在Session中,页面根据Session中的信息进行相应的国际化改变,需要对Session中的属性进行设置,具体怎么设置可以看Session的源代码.

  • CookieLocaleResolver

2. 使用系统默认的拦截器(配合SessionLocaleResolver )

比如:LocaleChangeInterceptor ,先在配置文件中进行注册,LocaleChangeInterceptor 会帮我们将国际化信息从请求中拿出,放入Session中
在这里插入图片描述


什么时候用Filter,拦截器
  1. 如果某些功能需要其他组件配合完成,就使用拦截器
  2. Filter无法加入ioc容器中
  3. 其他情况可以写Filter(JavaWeb的三大组件,没有SpringMVC也能用)
补充
  • 扩展

    视图解析器根据方法的返回值得到视图对象

    SpringMVC中有多个视图解析器,他们都会尝试能否得到视图对象

    视图对象不同就可以具有不同功能


  • 一定要学会看源代码,很多问题只要看过源代码之后就都不存在了.

  • 看源代码时,要学会分辨哪些代码是有用的,哪些是默认的不能改变的,还有哪些是我们需要根据需求进行设置的

自定义视图

自定义视图和视图解析器的步骤:

  1. 编写自定义的视图解析器

    // 实现自己的视图解析器,需要实现ViewResolver和Ordered 接口
    public class MyViewResolver implements ViewResolver, Ordered {
        private int order=0;
        // 需要实现的方法只有一个,根据视图名返回相应的视图对象
        @Override
        public View resolveViewName(String s, Locale locale) throws Exception {
            // 如果我们只想解析meinv 前缀的请求
            if(s.startsWith("meinv")){
                return new MyView();
            }else{
                return null;
            }
        }
        // 在配置文件中设置属性需要有setter方法,是为了后面在配置文件中对order进行设置
        public void setOrder(int order) {
            this.order = order;
        }
        @Override
        public int getOrder() {
            return order;
        }
    }
    
  2. 编写视图实现类

    // 自己的视图类,实现View 接口
    public class MyView implements View {
        // 内容类型
        @Override
        public String getContentType() {
            return "text/html";
        }
        @Override
        public void render(Map<String, ?> map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
            System.out.println("之前保存的数据:" + map);
            // 需要设置内容类型,不然会乱码
            httpServletResponse.setContentType("text/html");
            // 一个简单的渲染逻辑
            httpServletResponse.getWriter().write("哈哈<h1>你想看的,这里都没有!!<h1>");
        }
    }
    
  3. 视图解析器必须放在ioc容器中

    <!--servlet.xml中添加-->
    <bean class="com.spring.controller.MyViewResolver">
        <!--设置MyViewResolver的优先级-->
        <property name="order" value="1"/>
    </bean>
    

    InternalResourceViewResolver的优先级是最低的(order的值越大,优先级越低)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值