天天看点

SpringMVC笔记(自用)

1.SpringMVC简介

  SpringMVC:是基于spring的一个框架,实际上就是spring的一个模块,专门做web开发的。理解是servlet的一个升级

  web开发底层是servlet,框架是在servlet基础上面加一些功能,让你做web开发方便。

  springMVC就是一个Spring。Spring是一个容器,ioc能够管理对象,使用,@Component、@Respository、@Service、@Controller。因此,SpringMVC也能够创建对象,放入到(SpringMVC)容器中,它存放的是控制器对象(@Controller)

  我们要做的是,使用@Controller创建控制器对象,把对象放入到springmvc容器中,把创建的对象作为控制器使用。

  这个控制器对象能接收用户的请求,显示处理结果,就当做是一个servlet使用。

  要注意的是,使用@Controller创建的实际上只是一个普通类的对象,不是Servlet。但是springmvc赋予了控制器对象一些额外的功能。

  因为web开发的底层是servlet,springmvc中有一个对象是Servlet:DispatherServlet(中央调度器也叫前端控制器)

  DispatherServlet:负责接收用户的所有请求,用户把请求给了DispatherServlet,之后DispatherServlet把请求转发给了Controller对象,最后是Controller对象处理请求。请求的传递如下:

index.jsp----->DispatherServlet(Servlet)—转发,分配给–>Controller对象(@Controller注解的对象)

  所以,当我们使用springmvc框架时,所需要一个中央调度器和一个Controller对象

2.SpringMVC框架的使用

1.加入依赖

spring-webmvc依赖,简介把spring的依赖都加入到项目

jsp、servlet依赖

<!--servlet依赖-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
    </dependency>

    <!--SpringMVC依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
           

2.在web.xml中注册springmvc框架的核心对象DispatcherServlet,以及静态资源冲突的问题

SpringMVC笔记(自用)
<!--声明SpringMVC的核心对象,DispatcherServlet-->
    <!--
        需要在tomcat服务器启动后就创建DispatcherServlet对象的实例
        为什么要创建DispatcherServlet的实例呢?
        因为DispatcherServlet在他的创建过程中,会同时创建springmvc容器对象,读取springmvc的配置文件,把这个配置文件中的对象也创建好,
    当用户发起请求时就可以直接使用对象了。
        servlet的初始化会执行init()方法。DispatcherServlet在init()中会执行{
            //创建容器,读取配置文件
            WebApplicationContext wac=new ClassPathXmlApplicationContext("springmvc.xml");
            //把容器放入到ServletContext全局作用域中
            getServletContext().setAttribute(key,wac);
        }
    -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--自定义springmvc读取的配置文件的位置-->
        <init-param>
            <!--springmvc配置文件的位置-->
            <param-name>contextConfigLocation</param-name>
            <!--指定自定义文件的位置-->
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!--
        在tomcat启动后,创建servlet对象
        load-on-startup:表示tomcat启动后创建对象的顺序。他的值是整数,数值越小,tomcat创建对象的时间越早。数值大于等于0
        -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!--
            使用框架的时候,url-pattern可以用两种值
            1.使用扩展名方法,语法:*.xxxx,xxx是自定义的扩展名。常用的方式*.do,*.action,*.mvc等等
            http://localhost:8080/myweb/some.do
            2.使用斜杠"/"
        -->
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
           

如果使用“/”会出现哪些问题?

客户端请求的静态数据如html、js、图片等资源一般都会交给tomcat来进行处理

<!--tomcat的web.xml-->
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
           

如果加了"/",那么因为我们的web.xml比tocmat的优先级更高,会导致静态资源将优先交给我们的DispatcherServlet中央调度器来进行处理,进而使得静态资源404

<!--我们自己的web.xml-->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
			<!--
            当你的项目中使用"/",它会替代tomcat中的default。
            导致所有的静态资源都给DispatcherServlet处理,默认情况下DispatcherServlet没有处理静态资源的能力。
            没有控制器对象处理静态资源的访问。所以静态资源(html、js、图片、css)都是404
			
			我们自己的动态资源some.do还是能够继续访问的,因为我们的程序中有相关的控制器对其进行处理。
			-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
           

如何解决静态资源的冲突?

1)使用

<mvc:default-servlet-handler>

<!--
        第一种处理静态资源的方式:
            需要在SpringMVC的配置文件中加入<mvc:default-servlet-handler>
            原理:加入这个标签后,框架会创建控制器对象DefaultServletHttpRequestHandler(类似于我们自己的控制器)
            DefaultServletHttpRequestHandler这个对象可以接收的请求转发给tomcat的default这个servlet。
    -->
    <mvc:default-servlet-handler/>
    <!--default-servlet-handler和@RequestMapping有冲,需要加入annotation-driven解决问题-->
    <mvc:annotation-driven/>
           

2)使用

<mvc:resources>

在Spring3.0后,Spring定义了专门用于静态资源访问的处理器ResourceHttpRequestHandler

<!--
        第二种处理静态资源的方式:
            <mvc:resources/>加入后框架会创建ResourceHttpRequestHandler这个处理器对象
            让这个对象处理静态资源的访问,不依赖tomcat服务器
            mapping:服务端访问静态资源的uri地址,使用通配符**
            location:静态资源在你项目中的目录位置。

            /images/**:表示 images/p1.jpg ,images/user/logo.gif , images/order/history/list.png
            location前方的/代表webapp的根,后方的/代表这是个目录
    -->
    <mvc:resources mapping="/images/**" location="/images/"/>
    <mvc:resources mapping="/js/**" location="/js/"/>
    <!--resources和@RequestMapping有冲,需要加入annotation-driven解决问题-->
    <mvc:annotation-driven/>

	<!--一句话解决静态资源的访问-->
	<!--
	所有的静态资源都放在static目录下,比如/static/images/xx.jpg     /static/js/xx.js
	<mvc:resources mapping="/static/**" location="/static/">
	-->
           

3.创建一个发送请求的页面index.jsp

4.创建控制器类

1)在类的上面加入@Controller注解,创建对象,并放入到springmvc容器中

2)在类中的方法上面加入@RequestMapping注解

/*
* 创建处理器(控制器)对象,放在springmvc容器中
* 位置:类的上面
* */
@Controller
public class MyController {
    /*
    * 处理用户提交的请求,springmvc中是使用方法来处理的。
    * 方法是自定义的,可以有多种返回值,多种参数,方法名称自定义
    * */

    /*
    *   @RequestMapping:请求映射,作用是把一个请求地址和一个方法绑定在一起。一个请求指定一个方法来处理
    *       属性:1.value:是一个String,表示请求的url地址。value的值必须是唯一的,不能重复。使用时,推荐以"/"开头
    *       		位置:1).在方法的上面(常用)
    *           		2).在类的上面
    * 			2.method:表示请求的方式。它的值RequestMethod类枚举值。
    * 							例如表示get请求方法,RequestMethod.GET
    * 								   post请求方式,RequestMethod.POST
    *       说明:使用RequestMapping修饰的方法叫做处理器方法或控制器方法。被修饰的方法类似于servlet中的doGet、doPost。
    *       返回值:ModelAndView 本次处理的结果
    *       Model:数据,请求处理完成后,要显示给用户的数据
    *       View:视图,比如jsp等
    * */
    @RequestMapping(value="/some.do",method = RequestMethod.POST)
    //@RequestMapping({"some.do","show.do"})处理多个请求
    public ModelAndView dosome(){ //doGet()
        //处理some.do的请求
        ModelAndView mv=new ModelAndView();
        //添加数据,框架会在请求的最后把数据放入到request作用域。
        //request.setAttribute(key,value)
        mv.addObject("msg","这是一个使用springmvc框架做的开发");

        //指定视图
        //框架对视图执行forward操作,request.getRequestDispather("/show.jsp").forword(...)
        //mv.setViewName("/WEB-INF/show.jsp");

        //当配置了视图解析器后,可以使用逻辑名称(文件名),指定视图
        //框架会使用视图解析器的前缀+逻辑名称+后缀组成完整路径,这里就是字符连接操作
        //WEB-INF/+show+.jsp
        mv.setViewName("show");

        return mv;
    }
}

           

5.创建一个作为结果的jsp,显示请求的处理结果

6.创建springmvc的配置文件

1)声明注解扫描器,指定@Controller注解所在的包名

<!--声明注解扫描器-->
    <context:component-scan base-package="com.wxk.controller"/>
           

2)声明视图解析器,帮助处理视图的。

<!--声明SpringMVC框架中的视图解析器,帮助开发人员设置视图的路径-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--前缀:视图文件的路径-->
        <property name="prefix" value="/WEB-INF/"/>
        <!--后缀:视图文件的扩展名-->
        <property name="suffix" value=".jsp"/>
    </bean>
           

7.总结springmvc请求的处理流程

1)前端发起some.do请求

2)tomcat(扫描web.xml—>通过url-pattern知道*.do的请求给了DispatcherServlet)

3)DispatcherServlet(根据springmvc.xml配置知道some.do请求和控制层中的doSome()方法相对应)

4)DispatcherServlet把some.do转发给doSome()方法

5)框架执行doSome()把得到的ModeAndView进行处理,转发到show.jsp(前端页面)

3.SpringMVC相关知识点

3.1 @RequestMapping放在类的上面

/*
* @RequestMapping:
*   value:所有请求地址的公共部分,叫做模块名称
*   位置:放在类的上面
* */
@Controller
@RequestMapping("/test")
public class MyController {
    //@RequestMapping({"/test/some.do","/test/other.do"})
    @RequestMapping({"/some.do","other.do"})
    public ModelAndView dosome(){ //doGet()
        ModelAndView mv=new ModelAndView();
        mv.addObject("msg","这是一个使用springmvc框架做的开发");
        mv.setViewName("show");
        return mv;
    }
}

           

3.2 形参Request、Response、Session

Request、Response、Session可以在方法的形参中声明,框架会自动给其赋值

@RequestMapping(value = "/some.do",method = RequestMethod.POST)
    public ModelAndView dosome(HttpServletRequest request,
                               HttpServletResponse response,
                               HttpSession session){
        String name= (String) request.getParameter("name");
        System.out.println(name);

        ModelAndView mv=new ModelAndView();
        mv.addObject("msg","这是一个使用springmvc框架做的开发");
        mv.setViewName("show");
        return mv;
    }
           

3.3 接收参数:逐个接收方案

@Controller
public class MyController {
    /*
    * 逐个接收请求参数:
    *   要求:处理器(控制器)方法的形参名和请求中参数名必须保持一致。
    *        同名的请求参数赋值给同名的形参
    * 框架接收请求参数:
    *   1.使用request对象接收请求参数
    *       String strName=request.getParameter("name");
    *       String strAge=request.getParameter("age");
    *   2.SpringMVC框架通过DispatcherServlet调用MyController的dosome方法
    *       调用方法时,按名称对应,把接收的参数赋值给形参
    *       dosome(strName,Integer.valueOf(strAge));
    *       框架会提供类型转换的功能,能把String转为int,long,float,double等类型
    * */

    @RequestMapping(value = "/some.do",method = RequestMethod.POST)
    /*
    * 需要注意的是,如果形参使用int,那么前端传回来的参数如果是null或其他非整型数据,那么将无法转换,前台会出现400错误
    * 可以使用包装类型Integer代替int,这样就能够接受null,但还是无法接收其他非整型数据
    * */
    public ModelAndView dosome(String name,Integer age){
        ModelAndView mv=new ModelAndView();
        mv.addObject("name",name);
        mv.addObject("age",age);
        mv.setViewName("show");
        return mv;
    }
}
           

3.4 接收参数:注解接收参数

  前端传来的参数名并不总是和控制器方法中的形参名一致,此时可以使用@RequestParam注解来解决名字不一致的问题,同时还能解决如果前端未传参数报错的问题

@Controller
public class MyController {
    /*
    * 请求中参数名和处理器方法的参数名不一样时使用
    * @RequestParam:逐个接收请求参数中,来解决请求中参数名和形参名不一样的问题
    *   属性:1.value 请求中的参数名称
    *       2.required 是一个boolean值,默认true
    *               true:表示请求中必须包含此参数,否则出错(400)
    *   位置:在处理器方法形参定义的前面
    * */
    @RequestMapping(value = "/some.do",method = RequestMethod.POST)
    public ModelAndView dosome(@RequestParam(value = "sname",required = false) String name,
                               @RequestParam(value = "sage",required = false) Integer age){
        ModelAndView mv=new ModelAndView();
        mv.addObject("name",name);
        mv.addObject("age",age);
        mv.setViewName("show");
        return mv;
    }
}
           

3.5 接收参数:对象接收方案

  但接收的数据非常多时,可以考虑使用java对象来接收请求数据

@Controller
public class MyController {
    /*
    * 处理器方法形参对象是java对象,这个对象的属性名和请求中的参数名一样
    * 框架会创建形参的java对象,给属性赋值。请求中的参数是name,框架就会调用setName()
    * */
    @RequestMapping(value = "/some.do",method = RequestMethod.POST)
    public ModelAndView dosome(Student student){
        ModelAndView mv=new ModelAndView();
        mv.addObject("name",student.getName());
        mv.addObject("age",student.getAge());
        mv.addObject("student",student);
        mv.setViewName("show");
        return mv;
    }
}
           

3.6 返回值:ModelAndView

Model:数据,请求处理完成后,要显示给用户的数据

View:视图,比如jsp等

  ModelAndView同时包含数据和视图,如果你的请求在处理完后,需要返回数据的同时还需要跳转到其他页面,可以使用ModelAndView。

  相反,如果你只需要数据或是只需要进行视图的跳转,那么不建议使用ModelAndView

  具体使用方法可以点击此处查看

3.7 返回值:String

主要进行视图的跳转

@Controller
public class MyController {
    /*
    * 处理器方法返回String--表示逻辑名称,需要配置视图解析器
    * 本文使用了视图解析器,若想要返回完整路径,不加视图解析器即可
    * */
    @RequestMapping(value = "/some.do",method = RequestMethod.POST)
    public String dosome(Student student,HttpServletRequest request){
        student.setName("张三");
        //可以自己手动添加数据到request作用域
        request.setAttribute("name",student.getName());
        return "show";
    }
}
           

返回值为String也可以表示数据

@Controller
public class MyController {
    /*
    * 处理器方法返回的是String ,String表示数据而非视图的情况
    * 区分返回值String是数据还是视图要看有没有@ResponseBody注解
    * 如果有@ResponseBody注解,返回的就是一个数据,反之则是视图
    *
    * 默认使用"text/plain;charset=ISO-8859-1"作为contentType,导致中文有乱码
    * 解决方案:给RequestMapping增加一个属性produces,使用这个属性指定新的contentType.
    * */
    @RequestMapping(value = "/some.do",method = RequestMethod.POST,produces = "text/plain;charset=utf-8")
    @ResponseBody
    public String dosome(HttpServletResponse response,String name,Integer age) throws IOException {
        Student student=new Student();
        student.setName(name);
        student.setAge(age);
        return "123abc";
    }
}
           

3.8 返回值:void

void本身既表示数据也不能表示视图

在处理ajax请求的时候,可以使用void返回值。通过HttpServletResponse输出数据,响应ajax请求。

<!--ajax请求-->
    <script type="text/javascript">
        $(function () {
            $("input[type='button']").click(function () {
                $.ajax({
                    url:"some.do",
                    data:{
                        name:"张三",
                        age:12
                    },
                    type:"post",
                    dataType:"json",
                    success:function (resp) {
                    //response从服务器端返回的json格式的字符串
                    //jquery会把该字符串转换为json对象,赋值给resp形参
                        console.log(resp.name+","+resp.age)
                    }
                })
            })
        })
    </script>
           

处理器调用HttpServletResponse返回一个json数据

@Controller
public class MyController {
    @RequestMapping(value = "/some.do",method = RequestMethod.POST)
    public void dosome(HttpServletResponse response,String name,Integer age) throws IOException {
        Student student=new Student();
        student.setName(name);
        student.setAge(age);
        //处理ajax,将student转换成json数据,我使用的是fastjson
        String st=JSON.toJSONString(student);
        response.getWriter().write(st);
    }
}
           

3.9 返回值:对象Object

  Object:例如String、Integer、Map、List、Student等等都是对象,对象有属性,属性就是数据。所以返回Object表示数据,和视图无关。

  可以使用对象表示的数据,响应ajax请求

现在做ajax,主要使用json的数据格式。实现步骤:

  1.加入处理json的工具库的依赖,springmvc默认使用的jackson。

<!--jackson依赖-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.0</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.0</version>
    </dependency>
           

  2.在SpringMVC配置文件之间加入

<mvc:annotation-deriven>

注解驱动。

<!--注解驱动-->
    <mvc:annotation-driven/>
           

  3.在处理器方法的上面加入@ReponseBody注解

@Controller
public class MyController {
    /*
    * 处理器方法返回一个Student,通过框架转化为json,响应ajax请求
    * @ResponseBody:
    *   作用:把处理器方法返回对象转化为json后,通过HttpServletResponse输出给浏览器
    *   位置:方法定义的上面。和其他注解没有顺序的关系
    *
    * 返回对象框架的处理流程:
    *   1.框架会调用ArrayList<HttpMessageConverter>中每个类的canWrite()方法去检查
    * 哪个HttpMessageConverter接口的实现类能处理返回值类型的数据,——MappingJackson2HttpMessageConverter
    *
    *   2.框架会调用实现类的write()方法,即MappingJackson2HttpMessageConverter的write()方法
    * 把Student对象转为json,调用jackson的ObjectMapper实现转为json
    *
    *   3.框架会调用@RequestBody把2的处理结果输出到浏览器,ajax请求完成
    * */
    @RequestMapping(value = "/some.do",method = RequestMethod.POST)
    @ResponseBody
    public Student dosome(HttpServletResponse response,String name,Integer age) throws IOException {
        Student student=new Student();
        student.setName(name);
        student.setAge(age);
        return student;
    }
}
           

  SpringMVC处理器方法返回Object,可以转为json输出到浏览器,响应ajax的内部原理:

1.

<mvc:annotation-driven>

注解驱动

  注解驱动实现的功能是 完成java对象到json、xml、text、二进制等数据格式的转换。

  HttpMessageConverter接口:消息转换器。

  功能:定义了java转为json,xml等数据格式的方法。这个接口有很多的实现类。

  这些实现类完成java对象对json、对xml、对二进制数据的转换

  

<mvc:annotation-driven>

在加入到springmvc配置文件后,会自动创建HttpMessageConverter接口的7个实现类对象,包括

类 作用
ByteArrayHttpMessageConverter 负责读取二进制格式的数据和写出二进制格式的数据
StringHttpMeesageConverter 负责读取字符串格式的数据和写出字符串格式的数据
ResourceHttpMessagerConverter 负责读取资源文件和写出资源文件数据
SourceHttpMeesagerConverter 能够读、写来自HTTP的请求与相应的javax.xml.transform.Source,支持DOMSource,SAXSource和StreamSource的XML格式
ALLEncompassingFormHttpMessagerConverter 负责处理表单(form)数据
jaxb2RootElementHttpMessagerConverter 使用JAXB负责读取和写入xml标签格式的数据
MappingJackson2HttpMessagerConverter 负责读取和写入json格式的数据。利用jackson的ObjectMapper读写json数据,操作Object类型数据,可读取application/json,响应媒体类型为application/json

加粗的两个实现类是常用的

  下面两个方法是控制把结果输出给浏览器时使用的:

  boolean canWrite(class<?> var1,@Nullable MediaType var2);

  void write(T var1,@Nullable MediaType var2,HttpOutputMessage var3);

例如处理器方法

@RequestMapping("/some.do")
public Student doSome(){
	Student st=new Student();
	st.setName("张三");
	st.setAge(20);
	return student;
}
           

  1)canWrite:检查处理器方法的返回值,能不能为var2表示的数据格式。

  检查student(“张三”,20)能不能转为var2表示的数据格式。如果检查能转为json,canWrite返回true

  MediaType:表示数据格式,例如json、xml等

  2)write:把处理器方法的返回值对象,调用jackson中的ObjectMapping转为json字符串。

  json=om.writeValueAsString(student);

[email protected]注解

  放在处理器方法的上面,通过HttpServletReponse输出数据,响应ajax请求。执行流程大概如下

PrintWriter pw=response.getWriter();
	pw.println(json);
	pw.flush();
	pw.close();