SpringMVC学习笔记

287 阅读17分钟

SpringMVC学习笔记

SpringMVC 也叫 Spring web mvc。是 Spring 框架的一部分,是在 Spring3.0 后发布的。

一、第一个注解的SpringMVC程序

所谓 SpringMVC 的注解式开发是指, 在代码中通过对类与方法的注解,便可完成处理器在 SpringMVC 容器的注册。 注解式开发是重点

1. 实现步骤

  • 需求: 用户在页面发起一个请求, 请求交给SpringMVC中的控制器对象处理。结果显示到一个jsp中。

  • 实现步骤:

  1. 新建maven web项目
  2. 加入依赖
    • spring-mvc依赖
    • servlet依赖
    • jsp依赖
  3. 修改web.xml文件 注册springmvc框架中的核心对象DispatcherServlet(中央调度器)
  4. 创建jsp,发起请求
  5. 创建一个类,作为控制器使用 1)它不是servlet, 起到servlet的作用 2)需要在类的上面加入@Controller,表示创建控制器对象 3)在类中自定义方法, 方法上面加入@RequestMapping(value="请求的uri地址")
  6. 创建作为结果的jsp页面
  7. 创建springmvc框架需要的配置文件(spring的配置文件) 1)声明组件扫描器,指定Controller注解所在的包名 2)声明视图解析器,处理视图
  8. 修改控制器对象, 使用逻辑视图名称

2. 加入依赖

<!--servlet依赖-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!--spring-webmvc依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.16.RELEASE</version>
        </dependency>

3. web.xml中注册DispatcherServlet(掌握)

DispatcherServlet它是一个Servlet, 父类继承了HttpServlet,他是一个单例多线程的

  • DispatcherServlet的作用:
    1. 是接收请求,显示处理结果给用户。
    2. 把接收到的请求分派给处理器(控制器)对象。
    3. DispatcherServlet也叫做前端控制器(front controller)
  • DispatcherServlet的配置:
    1. 通过<load-on-startup>指定在tomcat服务器启动时,创建对象 <load-on-startup>的值是大于等于0整数,数值越小,tomcat创建这个对象的时间越早
    2. DispatcherServlet是一个servlet,在创建servlet对象时,会执行init()。在init()方法中, 会创建spring的容器对象WebApplicationContext,在创建容器对象时,会读取spring需要的配置文件。 而这个配置文件的默认路径是WEB-INF,即默认的名称是 <servlet-name>-servlet.xml
    3. 通过contextConfigLocation参数来指定配置文件的位置
    4. 通过<url-pattern>是把请求都交给中央调度器 常用的方式: 1. 扩展名: 常用的*.do*.mvc*.action等等, 不要使用*.jsp 2. 斜杠:"/"
<servlet>
    <servlet-name>myweb</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

    <!--自定义springmvc需要的配置文件-->
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>  

<servlet-mapping>
    <servlet-name>myweb</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

3. 创建Controller(控制器)

它不是servlet, 但起到了servlet的作用。 控制器作用:接收请求,处理业务逻辑。

@Controller
public class MyController {

    @RequestMapping(value = {"/some.do","/first.do"})
    public ModelAndView doSome(){
        //使用doSome方法处理用户的某个请求
        System.out.println("使用doSome方法处理some.do的请求");
        //调用service处理请求
        //创建返回值,表示请求的处理结果
        ModelAndView mv = new ModelAndView();
        //添加数据到Model部分, 框架对Model中的数据会在后边执行
        //request.setAttribute(),数据是放入到request作用域
        mv.addObject("msg", "第一个springmvc项目");
        mv.addObject("fun", "doSome");

        //指定结果页面的视图,框架对视图执行forward操作
        //setViewName("视图的完整路径")
		//WEB-INF是一个受保护的文件夹,一般将不让直接访问的文件放到WEB-INF/view/文件夹下
        //mv.setViewName("/WEB-INF/view/show.jsp");

        //当配置了视图解析器后,可以使用逻辑视图名称(文件名),指定视图
        mv.setViewName("other");

        //返回结果
        return mv;
    }
}

4. 创建springmvc框架需要的配置文件(掌握)

声明视图解析器:InternalResourceViewResolver

<!--声明组件扫描器-->
<context:component-scan base-package="com.bjpowernode.controller" />

<!--声明视图解析器:创建视图View对象, 指定视图的路径,和扩展名-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--前缀:指定视图文件的路径-->
    <property name="prefix" value="/WEB-INF/view/" />
    <!--后缀:指定视图文件的扩展名-->
    <property name="suffix" value=".jsp" />
</bean>

二、SpringMVC执行流程

  1. 浏览器提交请求到中央调度器
  2. 中央调度器直接将请求转给处理器映射器。
  3. 处理器映射器会根据请求,找到处理该请求的处理器,并将其封装为处理器执行链后返回给中央调度器。
  4. 中央调度器根据处理器执行链中的处理器,找到能够执行该处理器的处理器适配器。
  5. 处理器适配器调用执行处理器。
  6. 处理器将处理结果及要跳转的视图封装到一个对象 ModelAndView 中,并将其返回给处理器适配器。
  7. 处理器适配器直接将结果返回给中央调度器。
  8. 中央调度器调用视图解析器,将 ModelAndView中的视图名称封装为视图对象。
  9. 视图解析器将封装了的视图对象返回给中央调度器
  10. 中央调度器调用视图对象,让其自己进行渲染,即进行数据填充,成响应对象。
  11. 中央调度器响应浏览器。

三、SpringMVC注解式开发(掌握)

1. @Controller

需要在类的上面加入@Controller,表示创建控制器对象。默认是单例对象

  • 属性:
    • value:指定对象的名称,一般不需要指定名称
  • 位置:在类定义的上面

2. @RequestMapping

(1) @RequestMapping修饰方法

  • 方法的要求:
    • public 方法
    • 需要有返回值, 返回值表示请求的处理结果
    • 方法可以有参数,也可以没有。
    • 方法名称是自定义的
  • 属性:
    • method: 表示请求的方式, 用的是RequestMethod类的枚举值。不指定时不限制提交方式
      • RequestMethod.GET:表示提交方式必须为GET
      • RequestMethod.POST:表示提交方式必须为 POST 提交
    • value:定义所匹配请求的 URI, 地址是唯一值,可以指定多个,使用{"地址1","地址2",}
      • 推荐使用“/”开头,表示绝对地址

(2) @RequestMapping修饰类

@RequestMapping在一个类中修饰多个方法时,若这些请求具有相同的 URI 部分,可以将相同部分的URI抽取到注解在类之上的@RequestMapping 的 value 属性中。

  • 属性:value:表示请求地址的公共部分, 这个公共部分的值可以看做是模块名称

3. 处理器方法的参数

处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用。

  • HttpServletRequest
  • HttpServletResponse
  • HttpSession
  • 请求中所携带的请求参数
@RequestMapping(value = "/first.do")
public ModelAndView doFirst(HttpServletRequest request,
                            HttpServletResponse response,
                            HttpSession session){}

(1) 逐个接收请求参数

  • 控制器方法的形参名和请求中参数名一样。请求中同名参数赋值给同名的形参
  • 框架提供了参数的转换功能,将String类型的请求参数转换为对应数据类型,可以使用String,int , float, double ,long等的类型的转换
  • 当方法形参与请求中的参数名不一致时,可以在形参中使用注解@RequestParam
    • 作用:
      • 解决方法的形参名和请求中参数名不一样的问题
    • 属性:
      • value:请求中参数名
      • required:是boolean值, 默认是true
        • true:请求中必须有这个参数
        • false:可以没有参数
    • 位置:在形参定义的前面
@RequestMapping(value = "/receive-param-requestparam.do")
public ModelAndView doParam(@RequestParam(value = "rname",required = false) String name,
                            @RequestParam(value = "rage",required = false) Integer age){}

@RequestMapping(value = "/receive-param-property.do")
public ModelAndView doSome(String name,Integer age){}

(2) 对象参数接收

接收多个参数,3个以上, 可以使用java对象

  • java对象的属性名和请求中参数名一样。
  • 对象中必须要有对应属性的set()方法(对象是通过无参构造创建出来的,通过对应属性的set()方法来给属性赋值)
  • 也有自动类型转换的能力
@RequestMapping(value = "/other2.do")
public ModelAndView doOther2( Student student){}

注意: 以下代码中student对象中的name属性和形参中的name参数都会被赋值

@RequestMapping(value = "/other2.do")
public ModelAndView doOther2(Student student,String name){

(3) 添加过滤器解决请求参数中文乱码问题

通过使用CharacterEncodingFilter过滤器,指定请求对象使用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>
    <!--强制请求(request)对象使用encoding的值-->
    <init-param>
        <param-name>forceRequestEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
    <!--强制应答对象(response)使用encoding的值-->
    <init-param>
        <param-name>forceResponseEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <!--强制所有请求,先通过过滤器处理-->
    <url-pattern>/*</url-pattern>
</filter-mapping>

4. 处理器方法的返回值

使用@Controller 注解的处理器的处理器方法,其返回值常用的有四种类型

(1) ModelAndView

若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据, 此时处理器方法返回 ModelAndView 比较好。当然,若要返回 ModelAndView,则处理器方法中需要定义ModelAndView 对象。

(2) String

处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物Java 框架里视图地址,一般用于只有页面的跳转,不返回数据的情况 String对视图执行的是重定向(forward)

(3) 无返回值 void

若处理器对请求处理后,只需返回数据,无需跳转到其它任何资源, 此时可以让处理器方法返回 void。例如,对于 AJAX 的异步请求的响应。

(4) 返回自定义类型对象

返回的对象可以是几何对象,它不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。 返回对象,需要使用@ResponseBody注解, 将转换后的 JSON 数据放入到响应体中,具体步骤:

加入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>
声明注解驱动

SpringMVC 使用消息转换器实现请求数据和对象,处理器方法返回对象和响应输出之间的自动转换。消息转换器就是 HttpMessageConverter 接口。

框架默认在项目中有消息转换器的四个实现类,一个实现类处理一种数据类型。其中包含StringHttpMessageConverter类,能处理String类型的返回值的,但是不能处理java对象到json的转换。默认实现的四个类:

  1. org.springframework.http.converter.ByteArrayHttpMessageConverter

  2. org.springframework.http.converter.StringHttpMessageConverter

  3. org.springframework.http.converter.xml.SourceHttpMessageConverter

  4. org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter

    如果需要处理json的格式,在springmvc配置文件加入注解驱动的标签<mvc:annotation-driven/>,框架会创建7个实现类对象,其中有org.springframework.http.converter.json.MappingJackson2HttpMessageConverter,可以处理java对象到json的转换。

<mvc:annotation-driven/>
在处理器方法的上面加入@ResponseBody

使用HttpServletResponse对象把转换后的数据输出到浏览器。放在处理器方法的上面使用。

@RequestMapping(value = "/return-student-json.do")
@ResponseBody
public Student doStudentJson(String name, Integer age){}
返回String文本数据(乱码解决)

如果处理器方法上面有@ResponseBody注解,返回String就是数据。其他情况都是视图 框架会调用StringHttpMessageConverter类的write()方法,把String转为文本数据,默认使用text/plain;charset=ISO-8859-1,因此,会存在乱码。 解决方法: @RequestMapping的produces属性:指定Content-Type的值

@RequestMapping(value = "/return-string-data.do",produces = "text/plain;charset=utf-8")
@ResponseBody
public String doStringData(){
    return "HelloSpringMVC 开发web应用";
}

5. 中央调度器*.do/的区别

(1) *.do等

SpringMVC 的中央调度器会将匹配的请求进行处理分发,若请求不匹配,则交由Tomcat中默认的Servelt,例如,静态资源。

(2) /

DispatcherServlet 会将向静态资源的获取请求,例如.css、 .js、 .jpg、 .png 等资源的获取请求,当作是一个普通的 Controller 请求。 中央调度器会调用处理器映射器为其查找相应的处理器。当然也是找不到的,所以在这种情况下,所有的静态资源获取请求也均会报 404 错误

(3) / 无法访问静态资源的解决办法

  1. 使用<mvc:default-servlet-handler/>
    • 声 明 了 <mvc:default-servlet-handler /> 后 , springmvc 框 架 会 在 容 器 中 创 建DefaultServletHttpRequestHandler 处理器对象。 这个对象会把静态资源的请求转交给tomcat的default这个Servlet
    • default-servlet-handler和@RequestMapping有冲突,导致动态资源访问是404,所以要再加入注解驱动<mvc:annotation-driven />
<mvc:default-servlet-handler />
<mvc:annotation-driven />
  1. 使用使用<mvc:resources/>(掌握)
  • mvc:resources,由框架创建ResoucesHttpRequestHandler处理器对象,使用这个对象处理静态资源的访问, 由框架自己内部处理静态资源,不依赖服务器tomcat的。
    • 属性:
      • mapping:访问静态资源的uri地址,可以使用通配符** ** 表示任意的目录,子目录和资源的
      • location:静态资源在项目中的位置(目录),最好不要使用/WEB-INF目录
  • 同样需要加入注解驱动<mvc:annotation-driven />
<mvc:resources mapping="/html/**" location="/html/" />
<mvc:resources mapping="/img/**" location="/img/" />

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

四、SpringMVC核心技术

1. 请求转发与重定向

在这里插入图片描述

(1) 请求转发

处理器方法返回ModelAndView,实现转发操作

  • 语法格式:mv.setViewName("forward:视图的完整路径")
  • 特点:不和视图解析器一同工作,就当项目中没有视图解析器
mv.setViewName("forward:/hello.jsp");
mv.setViewName("forward:/WEB-INF/view/show.jsp");

(2) 重定向

处理器方法返回ModelAndView,实现重定向

  • 语法:mv.setViewName(redirect:视图完整路径)
  • 特点:不和视图解析器一同工作,就当项目中没有视图解析器
  • 总结: 1. 不能重定向到/WEB-INF受保护的资源 2. 框架提供了两次请求之间的数据传递功能, 框架把Model中简单类型的数据转为String,作为get请求参数附加到目标页面的后面, 对象类型的值不能转为string,不能作为参数传递 3. 在目标页面中,可以获取get请求的参数,使用语法${param.参数名}
mv.setViewName("redirect:/hello.jsp");

2. 异常处理

springmvc框架中异常处理是集中的方式, 把异常集中在异常处理器中。

(1) @ExceptionHandler

使用注解 @ExceptionHandler 可以将一个方法指定为异常处理方法。

  • 属性:value,Class<?>数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。不指定参数执行其他异常(相当于if条件判断中的else)
  • 被注解的方法:
    • 方法参数:value 用来指定异常名称,可以是自定义异常

(2) @ControllerAdvice

字面理解就是“控制器增强”, 是给控制器对象增强功能的。使用 @ControllerAdvice 修饰的类为全局异常处理对象,集中处理所有的异常

当使用 @RequestMapping 注解修饰的方法抛出异常时,会执行 @ControllerAdvice 修饰的 类中的异常处理方法。异常处理方法的返回值就是看到的最后的结果。

@ControllerAdvice 是使用 @Component 注解修饰的,可以使用<context:component-scan> 扫描到 @ControllerAdvice 所在的类路径(包名), 创建对象。

@ExceptionHandler(value = NameException.class)
public ModelAndView doNameException(Exception ex){
    //处理NameExceptoin异常
    //处理异常1记录异常,异常的发生时间,原因等。 记录到日志文件
    //        2发送通知, 邮件,短信等
    //        3给用户友好的提示,说明
}

//处理其他异常,并返回json字符串
@ExceptionHandler
@ResponseBody
public ModelAndView doOtherException(Exception ex){}

3. 一个拦截器

拦截器:是springmvc中的一种对象,需要实现HandlerInterceptor接口。

  • 拦截器作用:拦截请求, 验证请求是否正确。 如果请求不正确可以截断请求。通常使用拦截器可以验证用户是否登录, 验证用户的权限(用户可以执行的操作),打印日志。

  • 拦截器的分类:

    • 系统拦截器,springmvc内置的拦截器
    • 自定义拦截器(用户主要使用的)
  • 拦截器的特点:

    • 在一个项目中可以有0或多个拦截器
    • 拦截器是全局的,可以对所有的Controller请求都拦截
    • 拦截器的执行时间:在适配器对象获取之后, 在处理器方法执行之前,拦截用户的请求
      • 拦截器有三个执行时间:
        • 在处理器方法执行之前
        • 在处理器方法执行之后
        • 在请求处理完成后。
  • 拦截器的使用:

    • 定义类实现HandlerInterceptor接口
    • 在springmvc配置文件中声明拦截器对象,指定拦截的uri地址

(1) 自定义一个拦截器对象

实现HandlerInterceptor接口的类叫做拦截器,接口中有三个方法:

  • boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    • preHandle:预处理方法,用户的请求首先到达此方法,在处理器方法执行之前先执行的。
    • 参数:
      • Object handler:处理器对象,被拦截器的处理器对象
    • 返回值 boolean
      • true:请求可以通过拦截器, 请求能被处理
      • false:请求没有验证通过,被截断,请求不能被处理。
    • 作用:验证请求,比如登录检查, 权限验证,输出日志
  • void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;

    • postHandle:后处理方法,在处理器方法之后执行的。
    • 参数:
      • Object handler:被拦截的处理器对象
      • ModelAndView:表示处理器方法的返回值
    • 作用:能够获取到处理器方法的返回值,可以修改返回值的数据和视图,可以对请求的结果做二次处理。
  • void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;

    • afterCompletion:最后执行的方法,在视图处理完成后执行的。视图处理完成,请求也就处理完成了
    • 参数:
      • Object handler:被拦截的处理器对象
      • Exception ex:异常对象
    • 作用:在请求处理完成后执行此方法, 可以用来清除请求处理过程中的临时数据。把内存占用的资源清除掉, 或者打印日志。 注意:只有在preHandle返回true时,afterCompletion方法才可能有机会执行。

(2) 声明拦截器对象

使用<mvc:interceptors>标签来声明拦截器对象(springmvc配置文件中),拦截器声明顺序为拦截器的顺序

<!--声明拦截器对象-->
<mvc:interceptors>
    <!--声明第一个拦截器-->
    <mvc:interceptor>
        <!--mapping:指定拦截的请求uri
            path:请求的uri地址,可以使用通配符 ** , **表示任意资源
        -->
        <mvc:mapping path="/**"/>
        <!--拦截器对象-->
        <bean class="com.bjpowernode.interceptor.MyInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>

(3) 一个拦截器的执行方式

在这里插入图片描述

4. 两个拦截器

两个拦截器对象

(1) 第一个拦截器preHandle=true , 第二个拦截器preHandle=true

执行了拦截器MyInterceptor-1111中的preHandle 执行了拦截器MyInterceptor-22222中的preHandle 使用doSome方法处理some.do的请求za|22 执行了拦截器MyInterceptor-22222中的postHandle 执行了拦截器MyInterceptor-1111中的postHandle 执行了拦截器MyInterceptor-22222中的afterCompletion 执行了拦截器MyInterceptor-1111中的afterCompletion

(2) 第一个拦截器preHandle=true , 第二个拦截器preHandle=false

执行了拦截器MyInterceptor-1111中的preHandle 执行了拦截器MyInterceptor-22222中的preHandle 执行了拦截器MyInterceptor-1111中的afterCompletion

(3) 第一个拦截器preHandle=false , 第二个拦截器preHandle=true|false

执行了拦截器MyInterceptor-1111中的preHandle

(4) 执行流程

在这里插入图片描述

5. 源码剖析

在这里插入图片描述

5. 拦截器和过滤器的区别

  1. 过滤器是servlet中的对象,由tomcat创建的对象, 是tomcat调用的过滤器执行 拦截器是springmvc指定对象,由容器springmvc创建的, 在中央调度器中调用执行的。

  2. 过滤器是一个执行时间,在所有请求之前 拦截器有三个执行时间, 处理器方法之前, 处理器方法之后,视图执行之后

  3. 过滤器是侧重对象request,response对象的属性,参数设置值的。例如:request.setCharacterEncoding('utf-8'); 拦截器是侧重验证请求是否正确, 能截断请求。

  4. 过滤器是能过滤所有请求的,静态资源和动态资源的都能经过过滤器。 拦截器是拦截对controller的请求的,也就是只能拦截动态资源的(jsp也不行)。

五、SSM整合开发。

SSM:SpringMVC + Spring + MyBatis

  • SpringMVC:做web开发的,能够接受用户的请求参数, 返回结果

    • SpringMVC是基于spring的,它是容器。SpringMVC能创建容器对象WebApplicationContext
    • springmvc能够管理Controller对象 ,视图相关对象,所以应该把controller,视图等对象定义在springmvc配置文件中。
  • Spring:作为容器管理,创建service, dao对象, 可以创建具有事务功能的service对象。

    • Spring是容器对象, 创建WebApplicationContext对象, 管理Service,dao等对象,也就是非web中的对象。 应该把service,dao等对象都交给spring容器,定义在spring的配置文件中。
  • MyBatis:访问数据库

SpringMVC和Spring各自创建容器, 在一个项目中同时存在两个容器对象,这个两个容器对象有什么关系呢 ?

  • SpringMVC容器对象是Spring容器对象的子容器。
  • SpringMVC容器是子,Spring容器父,那么子能够访问父的内容。

用户发起请求---springmvc中的Controller处理请求----spring容器中的Service真正处理请求的---spring容器中的dao对象