框架技术 --- springMVC的引入

springMVC

内容管理

  • SpringMVC
    • SpringMVC introduce
      • DispatcherServlet 接收所有req
    • web开发简单步骤
      • 导入依赖
      • 在web.xml中注册spring的核心DispatcherServlet
      • 模拟访问页面
      • 创建控制器类
      • 模拟返回结果页面
      • 创建springMVC的配置文件,和spring类似
    • 请求处理分析
    • springMVC执行的源码分析
      • Tomcat启动创建Dispatcher
      • 请求处理
    • 视图解析
    • @RequestMapping
      • 注解放在类上面
      • method属性
      • 参数加载到形参列表
        • 逐个接收参数
        • 框架的过滤器CharacterEncodingFilter
        • 校正参数名@RequestParam
        • 对象接收参数

javaWeb—SSM中最后控制层MVC框架


简单介绍MVC ---- 后面闲下来会和大家聊聊源码


框架开发的好处就是便捷,坏处就是久而久之就少了一些乐趣,因为框架时固定的,都是固定的,比如mybatis编程也是固定的,反射也给封装了;spring加入mybatis加点datasource就可,在配置文件中配置datasource、SqlSessionFactory、dao对象就可以;spring的核心就是IOC和AOP;

SpringMVC

SSM框架还是挺好的,这个框架封装的是servlet的功能,最原始的技术就是servlet、手工new对象、JDBC来完成业务;后面再控制层对Servlet进行改进,发展了JSP解决输出到浏览器HTML代码的问题;后面使用AJAX和JSON逐渐弱化JSP,因为JSP文件可读性低。MVC则是如何工作?

SpringMVC introduce

之前分析spring的时候已经提过,spring是一个大的框架,里面有很多小的部分,其中Spring就是其中的分管Web的部分。web开发的底层都是servlet,框架就是在servlet的基础上面加入一些功能,让开发更加便捷。Spirng能够创建对象,springMVC能够创建对象,放到容器中springMVC容器,springMVC容器中放的是控制器对象@Controller

使用@controller创建控制器对象,创建的是普通的类的对象,不是serlvet,但是springMVC赋予了控制器对象一些额外的功能; 这些对象能够作为控制器使用【接收用户的请求,显示处理结果,当作servlet使用,但是不是servlet】

SpringMVC是spring的一个模块,基于MVC架构,功能分工明确,解耦合;SpringMVC是轻量级的,不依赖特定的接口;可以使用Spring的技术IOC和AOP,方便整合struts、Mybatis等其他的框架。【整合框架依靠的就是IOC】 springMVC强化注解的使用,在控制器、service、dao都可以使用注解@controller、@service、@repository;使用@Resource注入引用属性

DispatcherServlet 接收所有req

web的开发依赖的都是Servlet,springMVC中有一个对象是Servlet,就是DispatcherServlet,就是一个总的调度servlet;用户将所有的请求都给了这个dispatcherServlet,然后这个servlet将请求转发给创建的Controller对象,控制器对象对请求进行处理; dispatcher – 中央调度器;重定向就是getDispatcher、也是一个重新分配的作用;

index.html -----> DispactherServlet----转发、分配----> Controller对象(@Controller)

web开发简单步骤

这里简单使用springMVC来看看完成一个web项目的步骤,首先还是使用maven来管理项目【maven管理依赖真的很棒】

导入依赖

这是使用新的技术必不可少的步骤,导入的依赖就是SringMVC的依赖,也就是spring-webmvc


    org.springframework
    spring-webmvc
    5.3.14


注意这个依赖导入会自动加载其的依赖项,所以下面的context和web就不需要额外导入了

框架技术 --- springMVC的引入_第1张图片

这里关于之前的依赖就简单说一下功能就可--------- spring的核心spring-context ;方便IOC引用属性注入的java的注解@Resource的依赖javax-annotation-api;spring的事务的依赖spirng-jdbc、spring-tx;springAop实现框架AspectJ的依赖spring-aspects; mybatis的主依赖 mybatis; 使用的数据库Mysql的依赖connect-mysql; 方便快捷分页的插件 pagehelper; mybatis进入spring的依赖 mybatis-spring;快捷实现连接池的druid ; 在web中使用spring容器的 spring-web ; servlet的两个依赖 ---- 底层还是servlet,需要有; 测试的依赖junit

在web.xml中注册spring的核心DispatcherServlet

这个就是中央调度器,是一个servlet,继承的就是HTTPservlet; 叫做前端控制器【front controller】 负责接收用户请求,调用其他的控制器对象,把请求处理结果交给用户

注册SpringMVC的核心对象DispatcherServlet,需要在Tomcat服务器启动后,创建这个对象的实例,因为这个DispatcherServlet在创建过程中,会同时创建springmvc容器对象;读取springmvc的配置文件,把这个配置文件中的对象都创建好,当用户发起请求就可以访问了; 同时将这个容器对象放到全局作用域中

相当于在这个DispatcherServlet中的init方法就创建容器 : WebApplication ctx = new ClasspathXMLApplicationContext(“springmvc.xml”) //同时将对象放到全局作用域中 getServletContext.setAttribute(key, ctx);

<servlet>
    <servlet-name>DispatcherServletservlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
    <load-on-startup>1load-on-startup>
servlet>
这里的数值就是大于等于0的
//这里不需要servlet-mapping 因为不是访问的时候创建,而是需要tomcat服务器启动的时候就要创建这个时候就要使用load-on-startup就可以了;其值为一个整数,越小越先创建,但是-1表示的是默认、访问的创建

这里Tomcat创建的时候会报错

org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/DispatcherServlet-servlet.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/DispatcherServlet-servlet.xml]

也就是这里创建这个对象,就会默认去读取SpringMVC的配置文件,文件名就是这个DispatcherServlet的name加上-servlet.xml即可;并且默认在WEB-INF下,和之前读取spring-web的监听器一样;

但是文件一般都在resources目录下,所以这里需要自定义配置文件的位置;之前的spring-web中的监听器使用的是context-param; 这里因为是初始化这个servlet的时候使用;在servlet中指定init-param即可

所以综合在一起就是

 <servlet>
        <servlet-name>DispatcherServletservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>

        <load-on-startup>1load-on-startup>
        <init-param>
            <param-name>contextConfigLocationparam-name>
            <param-value>classpath:springmvc.xmlparam-value>
        init-param>
    servlet>

这样之后就不会报错  Artifact MVCtest:war exploded: Artifact is deployed successfully

接下来就是配置其映射的路径,这是Servlet规范中规定的,也是tomcat的map中,这里的中央调度器作为MVC控制层唯一的真正的Servlet;会接收所有的请求,所以路径直接通配即可

  1. 使用扩展名、后缀的方式,*.XXX 比如 .do .action等;就是会解析所有的以特定扩展名结尾的请求
  2. 使用斜杠的方式 /* 等会拦截所有的请求包括静态资源的请求 这里可以简单一点,就都会激活访问这个servlet
<servlet-mapping>
        <servlet-name>DispatcherServletservlet-name>
        <url-pattern>/*url-pattern>
    servlet-mapping>

<url-pattern>*.dourl-pattern>

模拟访问页面

这里使用html页面,简单用一个超链接进行访问

DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>SpringMvctitle>
    head>
    <body>
        <h1 align="center">简单使用MVCh1>
        <hr color="pink"/>
        <a href="some.do">访问页面a>
    body>
html>

创建控制器类

在类的上面加上controller注解,创建对象,加入到springMVC容器中【之前是tomcat创建的】;在类的方法上面加入@RequestMapping注解

这里就是创建一个普通的类即可,不需要继承HTPPservlet; 控制器对象的方法使用RequestMapping和路径绑定;一个方法处理的就是一个请求 — 所以一个控制器可以处理多个请求了; 而之前一个Servlet通过doGet或者Post方法只能处理一个请求

@RequestMapping一般放在方法上面, RequestMapping有多个参数,value是Stirng类型的,表示的请求地址,必须是唯一的,就是之前注册的url-pattern;这里是后台路径,是有项目路径的; 位置 : 可以在方法的上面,也可以在类的上面; 使用注解修饰的方法叫做控制器方法; 类似与doGet和doPost ; 返回值: ModuleAndView----- Model:数据,请求处理完成后,要显示给用户的数据;View:视图,比如JSP或者html等; 也就是返回值就是本次处理的结果,就类似于之前的Response; 注意 :@RequestMapping的value属性可以有多个值,代表多个请求都是由一个方法进行处理; 现在使用MVC就可以使用一个方法来处理多个请求

ModuleAndView返回结果就是一张Map;将所有的处理结果放到ModuleAndView中,最后其实就是框架将其放入到了请求作用域中,请求转发的方式 — 从请求作用域request中获取数据返回

之前就在使用jsp之后就提到过,servlet的作用就是获取请求参数,执行业务处理,得到业务处理结果,放到请求作用域,然后进行请求转发或者重定向调用视图层,这里也是一样,需要指定视图的完整路径,就和之前的request.getRequestDispatcher("…").forward(request,response); 【所以Module的数据就放在请i去作用域中】,使用对象的setViewName即可

//ModuleAndVie是spring-web下面的,如果没有重新Reimport即可,代表的就是处理的结果,相当于resp;交给主调度器返回

package Jning.controller;

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

/**
 * 这里的controller注解和之前的Spring中的Component差不多,专门创建的处理器对象
 * 比普通的component更加专业
 */
@Controller
public class loginAction {
    /**
     * 处理用户的请求,springmvc中是使用方法来处理的, 方法是自定义的,可以有多种返回值
     * 多种参数,方法名称自定义
     *
     * 一个方法处理一个路径的访问的请求,使用RequestMapping注解,一个请i去地址和一个方法绑定在一起
     * 一个请求指定一个方法处理
     */

    /**
     *RequestMapping有多个参数
     * value是Stirng类型的,表示的请求地址,必须是唯一的,就是之前注册的url-pattern;这里是后台路径,是有项目路径的
     * 位置 : 可以在方法的上面,也可以在类的上面; 使用注解修饰的方法叫做控制器方法; 类似与doGet和doPost
     * ModuleAndView----- Model:数据,请求处理完成后,要显示给用户的数据;View:视图,比如JSP或者html等
     */
    @RequestMapping(value = "/some.do") //类似与之前在servlet上面的注解@WebServlet
    public ModelAndView doSome(){
        //处理some.do的请求,处理和结果都放到MoudleAndView中;其中添加数据都是Map
        ModelAndView mv = new ModelAndView();
        //假设处理完成,接下来就是将数据放到request作用域【顶层就是会将moduleandview的解雇放进去】
        mv.addObject("msg","你好,欢迎使用MVC");
        mv.addObject("fun","执行的是doSome方法");
        //指定视图,类似请求转发
        mv.setViewName("/result.jsp");
        //返回ModelAndView对象
        return mv;
    }
}

可以看到这个方法就是和之前的doGet等方法同,获取参数,调用业务代码,放入请求作用域,请求转发

模拟返回结果页面

这里的返回结果的页面就很简单了,使用EL表达式取出请求域中的数据

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


    MVC result


    

result


${requestScope.msg}

${fun}

这里上面的是标准的写法,下面的是简化的EL表达式,虽然可能会出现问题,并且优先级不是最高

创建springMVC的配置文件,和spring类似

  1. 首先就是因为使用的是注解的形式来实现IOC,所以必须加入组件扫描器;spring中就是假的component-scanner;当然这是context中的,自动引入命名空间【IDEA】
  2. 声明视图的解析器,帮助处理视图【这里简单就还没有】

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/"/>
        <property name="suffix" value=".jsp"/>
    bean>

这里的扫描器,还是compnent-scann; 还有一个就是要什么视图的解析器


<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 https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="Jning.controller"/>

beans>

这里就简单声明组件扫描器就可以了;可以试一试访问的结果

result
msg数据 :你好,欢迎使用MVC

fun数据 :执行的是doSome方法

可以看到就和之前的访问的结果是相同的;成功访问

这里框架做的最重要的工作就是内部完成了数据放入请求域以及实现请求得转发

请求处理分析

首先就是用户发起请求some.do,发送给Tomcat服务器,Tomcat在Map容器中寻找与路径匹配的servlet对象,也就是中央调度器;中央调度器再在SpringMVC中有哪些controller处理器,根据some.do和方法doSome()是对应的,就将请求转发给LoginAction的doSome方法 — > 方法将处理的结果放到ModelAndView,转发到result.jsp

some.do ---- > Dispatcher -----> doSome() ; 中央调度器读取到springMVc配置文件,知道有哪些处理器方法【控制器对象的】;Controller可能有很多很多个

其实这个中央调度器和之前的全局过滤器差不多,拦截所有的请求,可以对请求进行处理

springMVC执行的源码分析

Tomcat启动创建Dispatcher

通过load-on-start指定的1来创建这个类的对象;中央调度器的父类就是servlet;会执行init方法,在init方法中就会读取文件创建容器; 创建容器也就能扫描注解,所有的对象就通过map放到了mvc容器中;

首先看看Dispatcher是不是servlet

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware 
    
public class DispatcherServlet extends FrameworkServlet

可以看看直接的父类的init方法

protected final void initServletBean()
     this.webApplicationContext = this.initWebApplicationContext();//创建了容器对象

 protected WebApplicationContext initWebApplicationContext()
      WebApplicationContext wac = null;//容器对象
	  
if (wac == null) {
            wac = this.findWebApplicationContext();
        }

        if (wac == null) {
            wac = this.createWebApplicationContext(rootContext);
        }

		  String attrName = this.getServletContextAttributeName();
         this.getServletContext().setAttribute(attrName, wac);

		return wac;

这里wac就是springmvc容器,可以看到其放到了全局作用域中this.getServletContext().setAttribute(attrName, wac);

请求处理

这个是处理方法,那么处理请求,应该是service方法,还是在frameworkservlet中寻找;直接CTRL + F12就可以查看类的方法

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        if (httpMethod != HttpMethod.PATCH && httpMethod != null) {
            super.service(request, response);
        } else {
            this.processRequest(request, response);
        }

    }
//进入这个方法
 protected final void processRequest
     try {
            this.doService(request, response);//进入DispacterServlet的doService方法
         
 try {
            this.doDispatch(request, response); //里面就是处理请求的
     
      protected void doDispatch(HttpServletRequest request, HttpServletResponse response) 
          
           ModelAndView mv = null;
     //中间会调用控制器的方法
     		mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

DispacterServlet的doDispatch方法会调用控制器的doSome()方法,也就是说其实这里用了mappedHandler代理执行,核心的方法就是doDispatch

也就是说中央调度器首先会创建spring容器,第二个就是它会使用doDispatch方法来调用对应的访问的方法,获得mv然后进行请求转发;并不是控制器的方法来直接进行处理

视图解析

什么是视图解析呢,首先将资源直接放在webapp目录下,也就是和WEB=INF平级,用户是可以直接通过Get的方式直接获取到静态资源,当然可以用过滤器的方式进行拦截; 但是这里还有一种方式,就是出于安全的考虑,直接将资源放到WEB-INF目录下

WEB-INF目录下资源文件安全性都是很高的。它是不能直接通过浏览器来访问。访问它的方式只有一种。那就是通过服务器来访问------所以WEB-INF目录下一般存放的是lib(项目需要用到的jar包),spring的配置文件,服务器的配置文件web.xml,以及一些访问安全性较高的jsp/html页面(通常是后台管理页面)。如果要访问WEB-INF下的jsp/html页面,只能通过请求转发(经过控制层)来访问。----所以即使做了以下配置,通过浏览器访问还是会报404
也就是说WEB-INF下面的资源都是服务器资源【也叫做internal Resource】服务器内部资源,不能从外部访问,只能通过服务器来进行调用; 比如原本的lib和web.xml这些文件用户是访问不到的,所以一般安全的资源都放到WEB-INF下面;

这里将result.jsp放到WEB-INF下面的view文件夹中

mv.setViewName("/WEB-INF/view/result.jsp");

服务器内部可以调用资源给用户,用户不能直接访问,这样子之后用户不能直接访问,但是结果还是正常显示了

那么这里有视图解析器什么事情?

/WEB-INF/view/result.jsp ----> 这个路径很长,前面的都是固定的,如果都是jsp文件,那么变化的部分之后result;那么框架就可以将这个部分给封装


    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/"/>
        <property name="suffix" value=".jsp"/>
    bean>

里面有两个属性,一个是前缀路径 : value就是前缀路径的字符串,一定要带上/

另外一个就是后缀 : 表示视图文件的扩展名; 也是String类型的;

这里的bean对象和之前创建dao对象的类似,只用指定class即可,内部的属性来完成操作;

配置视图解析器之后,就使用逻辑名称即可,也就是文件名,来指定视图 ; 这样项目会使用视图解析器的前缀 + 逻辑名称 + 后缀 就可以访问 使用的类就是内部提供的InternalResourceViewResolver — 内部资源解析

mv.setViewName("result");

访问还是成功的

@RequestMapping

这个注解上面已经提到过了,注解的value属性就相当于之前的@WebServlet;MVC一个重要的就是减少了控制层的类的量,之前servlet开发需要创建很多servlet;而现在一个方法就可以搞定

value属性的值是一个 数组; 可以存放多个路径,代表访问这些路径,都是由这个方法进行处理;注意这里的是多对一;是可以的,MAP的key才是唯一的

注解放在类上面

上面放在方法的上面就相当于将一个方法当作一个servlet的do方法; 放在类的上面,其value就是所有 请求的功能部分,也就是模块的名称; 这样之后下面的方法的注解的value就不需要加上模块的名称的路径;比如/test/some.do ;

<a href="test/some.do">访问页面a>
/**
 * 注解放在类的上面,value指定的就是模块的名称,这样其中的方法就不需要在些模块的名称,框架会自动加上,就像视图解析器一样
 */
@Controller
@RequestMapping(value = "/test")
public class loginAction {

    @RequestMapping(value = "/some.do")
    public ModelAndView doSome(){
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","你好,欢迎使用MVC");
        mv.addObject("fun","执行的是doSome方法");
        mv.setViewName("result");
        return mv;
    }
}
//访问之后
http://localhost:8080/MVCtest/test/some.do  ---> 成功访问

这样如果要修改模块,直接在类的上面进行修改就可以,不需要一个一个方法去进行修改

method属性

这个属性代表的是请求的方式,值是枚举类,RequestMehtod.GET和RequestMethod.POST(指定访问的方式,不是这种方式就访问不到)【之前的servlet中区分的就是doGet和doPost,两个的方法不同,对应的就是不同的method】; 相当于将这个方法给手动变为之前的doGet或者doPost()方法

比如这里将some.do修改为Post的方式

 @RequestMapping(value = "/some.do",method = RequestMethod.POST)
    public ModelAndView doSome(){

不指定的时候,就可以以任何的方式来进行访问;指定之后就必须以指定的方式访问

HTTP状态 405 - 方法不允许
类型 状态报告

消息 Request method 'GET' not supported

参数加载到形参列表

处理器方法的参数包括HttpServletRequest、resp、HttpSession、所有的请求参数,在servlet中是包含这些参数的,框架中要想使用,直接在处理器方法中定义就可以了,中央处理器会自动赋值,这里可以手动将这些给加入形参列表

<a href="test/some.do?name='zhangsan'">访问页面a>  //携带一个数据
//这里对doSome方法加入,获取提交的参数name
@Controller
@RequestMapping(value = "/test")
public class loginAction {

    @RequestMapping(value = "/some.do",method = RequestMethod.POST)
    public ModelAndView doSome(HttpServletRequest req, HttpServletResponse resp, HttpSession session){
        //定义处理结果返回对象
        ModelAndView mv = new ModelAndView();
        //获取请求的参数
        String username = req.getParameter("name");
        mv.addObject("msg","你好,欢迎使用MVC, 尊敬的" + username);
        mv.addObject("fun","执行的是doSome方法");
        mv.setViewName("result");
        return mv;
    }
}

这样之后返回的就是 msg数据 :你好,欢迎使用MVC, 尊敬的’zhangsan’

在url中加入参数的时候字符串就不需要再加上引号了

前面简单都是直接创建好的对象,框架会直接将值给注入,所以可以直接使用;接收参数使用的是处理方法的形参

这里换一个页面

DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>SpringMvctitle>
    head>
    <body>
        <h1 align="center">简单使用MVCh1>
        <hr color="pink"/>

        <form action="test/some.do" method="post">
            用户名<input type="text" name="user"/><br/>
            年龄<input type="text" name="age"/><br/>
            <input type="submit" value="提交参数"/>
        form>
    body>
html>

逐个接收参数

上面有多个参数,这里可以不用request接收,可以直接放到形参列表中;处理器方法形参名和请求中参数名必须一致,同名的请求参数赋值给同名的形参

这里类型可以自定义,总之名称必须相同

@Controller
@RequestMapping(value = "/test")
public class loginAction {

    @RequestMapping(value = "/some.do",method = RequestMethod.POST)
    public ModelAndView doSome(String name, int age){
        //定义处理结果返回对象
        ModelAndView mv = new ModelAndView();
        //请求参数直接获取到了形参列表中
        mv.addObject("msg","你好,欢迎使用MVC, 尊敬的 " + name + " 你的年龄是 " + age);
        mv.addObject("fun","执行的是doSome方法");
        mv.setViewName("result");
        return mv;
    }
}

这里框架就会将请求参数自动放到形参列表中,因为是同名的

msg数据 :你好,欢迎使用MVC, 尊敬的 null 你的年龄是 25

只有同名的才会注入,如果不同名,就不会注入,就还是默认值;比如这里上面提交的是user;但是参数列表是name,结果显示的就是空

框架实际上还是使用的request对象, 使用的就是 String name = request.getParameter(“name”);然后框架通过DispatcherServlet调用处理器类的doSome()方法, 调用方法时,按照名称对应,将接收的参数赋值给形参 ; 会自动实现类型转换,将String转化为int、long等类型 ----> 这样之后就不需要在方法中直接获取参数了,也就是手动request.getParameter了

HTTP状态 400 - 错误的请求
类型 状态报告

描述 由于被认为是客户端对错误(例如:畸形的请求语法、无效的请求信息帧或者虚拟的请求路由),服务器无法或不会处理当前请求。

status400 — 表示在提交请求参数的过程中,发生了错误,但是不会抛出异常,写在日志中;类型转换失败的时候就需要进行正常的类型转换; 比如前台提供了空, 这个时候age时不能直接转换的

//日志中的错误
org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.logException Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: ""]

所以这种单个接收参数,限制太大了,不知道类型就很容易发生400的错误【所有的参数原始的类型都是String】 — 好处就是参数不合法的时候就不会执行doSome方法

但是有个问题就是Post方式会出现乱码【GET方式不需要设置】,之前就遇到过,这里需要setCharsetEncoding;通过request的方法设置; 之前一般是拦截所有的请求进行设置

框架的过滤器CharacterEncodingFilter

就像之前web提供了全局作用域的监听器用来创建spring容器对象一样,框架也提供了过滤器CharacterEncodingFilter — 和之前监听器在一个包中; 这里也要在web.xml中声明; 这里直接拦截所有

需要在其中设置3个参数

public class CharacterEncodingFilter extends OncePerRequestFilter {
    @Nullable
    private String encoding;
    private boolean forceRequestEncoding;
    private boolean forceResponseEncoding;
  • encoding : 字符编码方式
  • forceRequestEncoding 强制请求对象 HttpServletRequest使用encoding的值 【要设置为真】
  • forceResponseEncoding 强制响应对象 HttpServletRequest使用encoding的值 【要设置为真】
<filter>
        <filter-name>characterEncodingFilter</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>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*
    

这里会拦截所有的请求,对其进行字符编码

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String encoding = this.getEncoding();
        if (encoding != null) {
            if (this.isForceRequestEncoding() || request.getCharacterEncoding() == null) {
                request.setCharacterEncoding(encoding);
            }

            if (this.isForceResponseEncoding()) {
                response.setCharacterEncoding(encoding);
            }
        }

        filterChain.doFilter(request, response);
    }
}  //这里就是设置字符编码的源码

所以其实这里和之前的过滤器是相同的

校正参数名@RequestParam

逐个传参的过程中,要求必须一样,如果不一致就会出现问题,@RequestParam就可以解决这个问题;属性value :就是请求中的参数名; 位置就放在处理方法的形参定义的前面; ----> 其实就类似于之前Mybatis中传参的@Param注解;使用注解之后框架就会将参数直接赋值给只当的形参

这里比如之前的参数传的是user,接收以name接收,这里就可以使用@RequestParam矫正【为了和@Param区分】

public ModelAndView doSome(@RequestParam(value = "user",required = true) String name, int age)

这样就可以成功接收参数了

msg数据 :你好,欢迎使用MVC, 尊敬的 张三 你的年龄是 25

required : @RequestParam的另外一个属性,boolean类型,代表是否一定需要传参;默认是true;不传参就会报400错误

对象接收参数

逐个接收参数如果很少,参数列表还可以,如果参数过多,就不方便;这里其实和Mybatis的传参差不多,可以使用@Param一个一个传参,同时可以定义一个java对象,这个对象的属性和参数同名就可以了

比如这里直接使用之前定义的学生类,传参就穿学生的3个属性

public class Student {

    private int stuno;
    private  String stuname;
    private  String stuclass;
  ……………………

传参的时候传入的是和这3个属性同名的参数

<form action="test/some.do" method="post">
    学号<input type="text" name="stuno"/><br/>
    姓名<input type="text" name="stuname"/><br/>
    班级<input type="text" name="stuclass"/><br/>
    <input type="submit" value="提交参数"/>
form>

方法接收的时候,直接写对象的类型就可以了

@Controller
@RequestMapping(value = "/test")
public class loginAction {

    @RequestMapping(value = "/some.do",method = RequestMethod.POST)
    public ModelAndView doSome(Student student){
        //定义处理结果返回对象
        ModelAndView mv = new ModelAndView();
        //请求参数直接获取到了形参列表中
        mv.addObject("msg","你好,欢迎使用MVC, 尊敬的 " + student.getStuno()+ " " + student.getStuname() + " 你的班级是 " + student.getStuclass());
        mv.addObject("fun","执行的是doSome方法");
        mv.setViewName("result");
        return mv;
    }
}

这里是因为在dispatcher调用的时候就自动执行request.getParameter(……)然后赋值给对象类型的同名属性,然后再方法中就可以直接使用这个赋值过的对象【这里注入的属性是通过set方法进行属性注入】----- 所以创建的对象必须有set方法

//    public void setStuno(int stuno) {
//        this.stuno = stuno;
//    }

这里的属性注入使用的是set注入,没有set方法,属性注入失败,保持默认值,这里就保持默认值0

msg数据 :你好,欢迎使用MVC, 尊敬的 1001 张三 你的班级是 HC2001

逐个传参和对象传参都是在reqeust的基础上进一步简化的结果,使用之后就不需要手动调用request进行数据的取出;并且可以自动避免没有参数【会报400,不能进行类型转换】

传参减少了从请求头重取出参数的过程,简化了操作,并且会直接阻拦一些非法的数据

你可能感兴趣的:(JAVAweb,mvc,spring,java,springmvc)