SpringMVC学习

SpringMVC学习

一、SpringMVC概述

  • SpringMVC是基于Java实现了MVC模型的轻量级Web框架。

二、SpringMVC开发

2.1、基于配置开发

2.1.1、相关依赖导入

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itheima</groupId>
    <artifactId>SpringMVC01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <!--servlet 3.1规范-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!--jsp坐标-->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
        <!--spring坐标-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!--springWeb坐标-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!--spring-mvc坐标-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
		 <!--lombok相关依赖(需要下载相关的插件)-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.6</version>
        </dependency>
        <!--json相关坐标3个-->
        <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>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <target>1.8</target>
                    <source>1.8</source>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <port>80</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2.1.2、核心配置文件

1、在resources中定义spring-mvc.xml文件

<?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"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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 http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    // Controller 处理之后的结果会被拼接这个地址,转发到对应JSP文件总展示       
    <!-- 视图分解解析器 -->
    <bean id="viewResolver" 		           class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 这是前缀 -->
        <property name="prefix" value="/"></property>
        <!-- 这是后缀 -->
        <property name="suffix" value=".jsp"></property>
        <!-- 在spring的控制器中,返回的是一个字符串,那么请求的路径则是,前缀+返回字符串+后缀 -->
    </bean>
        
       // “ /test ”是前端拼接的地址,对应的请求会被 class 对应的 Controller 处理 
      <bean name="/test" class="com.itheima.controller.TestController"></bean>  
</beans>

2、在WEB-INFO文件的web.xml文件中定义相关配置

<?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">
    <servlet>
        <!--服务器已启动就会加载dispatcherServlet-->
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <!--服务器启启动之后,就会读取spring-mvc.xml中配置,加载"xx"路径下的所有标记为bean的类-->
        <!-- 配置DispatcherServlet的一个初始化参数:配置SpringMVC配置文件位置和名称,这个也可以不配置,如果不配置,则默认是WEB-INFO下的springMVC-servlet.xml -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:applicationcontent.xml</param-value>
        </init-param>

        <!--在服务器创建时自动创建-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

1、springmvc配置拦截的三种方式/

  • " / ": 拦截所有请求但是不包括JSP页面,但是会拦截静态页面如:img、js、css
  • " /* " : 拦截所有请求包括JSP页面
  • “ *.do ” : 所以以“ .do ”结尾的前端请求都会被拦截

2.1.3、定义一个类作为控制器

1、方式一:实现“ Controller “ 接口,重写里面方法。

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TestController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndViw mv = new ModelAndView();
        // 设置访问jsp文件的地址
        mv.setViewNew("/hello.jsp");
        // 设置需要返回的信息
        mv.addObject("msg","welcome to springmvc");
        return mv;
    }
}

2、方式二:实现” HttpRequestHandler “ 接口,重写里面的方法。

public class TestController implements HttpRequestHandler
    
    public void handleRequest(HttpServlertRequest request,HttpServlertResponse response) throws ServletException,IOException{
    System.out.println("welcome to springmvc");
}

注:需要把创建对应的Controller 在 spring-mvc.xml文件中注册成Bean,交给Spring容器进行管理。

2.2、基于注解开发

2.2.1、相关依赖导入

  • 和基于配置文件开发导入的依赖一致

2.2.2、核心配置文件

1、在resources中定义spring-mvc.xml文件

<?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"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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 http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
           
    <!-- 定义扫描的包 -->
    <context:component-scan base-package="被扫描包的全路径类名"/>      
    <!--开启SpringMVC注解驱动-->
    <mvc:annotation-driven conversion-service="conversionService"/>    
        
    // Controller 处理之后的结果会被拼接这个地址,转发到对应JSP文件总展示       
    <!-- 视图分解解析器 -->
    <bean id="viewResolver" 		           class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 这是前缀 -->
        <property name="prefix" value="/"></property>
        <!-- 这是后缀 -->
        <property name="suffix" value=".jsp"></property>
        <!-- 在spring的控制器中,返回的是一个字符串,那么请求的路径则是,前缀+返回字符串+后缀 -->
    </bean>
</beans>

2、在WEB-INFO文件的web.xml文件中定义相关配置

  • 和基于配置文件开发进行的配置文件一致。

2.2.3、定义一个类作为控制器

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

/**
* @Controller是控制器的注解
*/
@Controller
@RequestMapping("/test")
public class UserController {

    @RequestMapping("/save")
    public String save() {
        System.out.println("save is run .....");
        return "/success.jsp";
    }
    @RequestMapping("/requestParam")
    public String requestParam(String name){
        System.out.println(name);
        return "/success.jsp";
    }
}

注:@RequestMapping("/test")这里的“ /test ”,作为父类的路径,前端请求的url地址为:http://localhost:8080/test/save,类里面的每个方法请求的都会加上这个前缀,其也可以不进行配置。如果不配置前端的访问地址为:http://localhost:8080/save。

2.3、基于java代码开发

2.3.1、相关依赖导入

  • 和基于配置文件开发导入的依赖一致

2.3.2、配置类的创建

1、定义配置类代替“ 在resources中定义spring-mvc.xml文件 ”。

@Configuration
@ComponentScan("com.itheima.controller") // 指定包扫描的位置
@EnableWebMvc  //mvc的注解驱动
public class SpringMVCConfig implements WebMvcConfigurer {
     // 注解配置放行指定资源格式
   /* public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/img/**").addResourceLocations("/img/");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/");
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
    }*/
    
    /**
     *  放行静态资源文件  (一般配置这种方式就可以)
     * @param configurer
     */
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

 ==**注意:**==替代Spring-mvc.xml文件,必须实现WebMvcConfigurer接口。
 ==**注意:**==之前spring-mvc.xml文件是在web.xml中读取的,我们知道,服务器启动时,会自动解析web.xml;那么当我们采用纯注解配置时,如何自动去解析该配置类呢。这里我们会用到之前咱们提到的spi机制。

2、定义配置类代替“ 在WEB-INFO文件的web.xml文件 ”

package com.itheima.config;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;

import javax.servlet.Filter;

// 该类中onStartup方法会在服务器启动时自动执行
public class WebConfig extends AbstractDispatcherServletInitializer {
    
    // 配置Springmvc容器,加载web层bean
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext acwc = new AnnotationConfigWebApplicationContext();
        acwc.register(SpringMVCConfig.class);
        return acwc;
    }

    // 配置DispatcherServlet的访问路径
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    // 配置Spring容器,加载service层和dao层bean
    protected WebApplicationContext createRootApplicationContext() {
        AnnotationConfigWebApplicationContext acwc = new AnnotationConfigWebApplicationContext();
        acwc.register(SpringConfig.class);
        return acwc;
    }

    // 添加Post请求参数乱码过滤器
    protected Filter[] getServletFilter(){
        CharacterEncodingFilter cef = new CharacterEncodingFilter();
        cef.setEncoding("utf-8");
        cef.setForceEncoding(true);
        return new Filter[]{cef};
    }
}




2.3.2、定义一个类作为控制器

  • 和基于注解开发定义的“ Controller ”一致。

三、SpringMVC请求

3.1、请求地址获取

  • 1、@RequestMapping 获取请求url:

● 注解用于获取请求的url地址,其可以加在类上和方法上。加上类有作为父路径的作用,所以类中的方法的请求地址都会拼接类上的路径。

2、@RequestMapping 设置请求方式:

● @RequestMapping(value="/testMethod",method=RequestMethod.POST),其中" method "就是用于指定请求方式。
● @RequestMapping(value="/testMethod",method=RequestMethod.POST,method=RequestMethod.GET),支持POST和GET两种方式。
● @RequestMapping(value="/testMethod"),默认支持两种请求方式。
3、@RequestMapping 参数映射配合注解 @PathVariable使用
@RequestMapping(value="/detail/{id}", method = {RequestMethod.GET})
public ModelAndView getDetail(@PathVariable(value="id") Integer id){

ModelAndView modelAndView = new ModelAndView();  
modelAndView.addObject("id", id);  
modelAndView.setViewName("detail");  
return modelAndView;

}

请求地址:http://localhost:8080/detail/123

4、URL通配符设置:
● 我们还可以通过通配符对URL映射进行配置。
○ “?”: 表示 1 个字符。
○ “*”:表示匹配多个字符。
○ “**”:表示匹配0个或多个路径。
5、URL正则表达式映射:
@RequestMapping(value="/reg/{name:\w+}-{age:\d+}", method = {RequestMethod.GET})
public ModelAndView regUrlTest(@PathVariable(value="name") String name, @PathVariable(value="age") Integer age){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("name", name);
modelAndView.addObject("age", age);
modelAndView.setViewName("regurltest");
return modelAndView;
}

请求地址:http://localhost:8080/reg/Lihua-18 该访问地址能正常访问。

请求地址:http://localhost:8080/reg/Lihua-xx 该访问地址不能正常访问。

6、限制URL所接收的请求参数:
● 指定映射请求必须包含某个参数:@RequestMapping(value="/paramstest", params="example")
○ 地址:http://localhost:8080/paramstest?example ,必须包含“example”才能正确访问。
● 指定映射请求必须不包含某个参数:@RequestMapping(value="/paramstest", params="!example")
○ 地址:http://localhost:8080/paramstest?example ,包含“example”不能正确访问。
● 指定映射请求中或者某参数必须等于某个值:@RequestMapping(value="/paramstest", params="example=test")
○ 地址:http://localhost:8080/paramstest?example=test ,example=test才能正确访问。
● 指定映射请求中或者某参数必须不等于某个值:@RequestMapping(value="/paramstest", params="example=!test")
○ 地址:http://localhost:8080/paramstest?example=test ,example=!test才能正确访问。
● 当我们为params指定多个参数时如:params={"example1", "example2"},表示的是and关系,即两个参数限制必须同时满足。
7、限制URL所接收请求头参数:
● 指定映射请求头必须包含某参数:@RequestMapping(value="/headerTest", headers = "example")
● 指定映射请求头必须不包含某参数:@RequestMapping(value="/headerTest", headers = "!example")。
● .指定映射请求头中或者某参数必须等于某个值:@RequestMapping(value="/headerTest", headers = "Accept=text/html")。
● .指定映射请求头中或者某参数必须不等于某个值:@RequestMapping(value="/headerTest", headers = "Accept!=text/html")。
● 当我们为headers指定多个参数时如:headers={"example1", "example2"},表示的是and关系,即两个参数限制必须同时满足。

3.2、请求参数获取

1、获取请求中参数, @RequestParam(value="password",required=false,defaultValue="我是默认值")

  • value : 参数名,如果是指写了参数名的话,请求时就必须带此参数,否则会失败。
  • required : 默认是 true ,为 false 时,参数可以不传。
  • defaultValue :当浏览器没有传参时,可以给这个参数一个默认值。

2、获取请求地址中参数 :@PathVariable

@RequestMapping(value="/detail/{id}", method = {RequestMethod.GET})
public ModelAndView getDetail(@PathVariable(value="id") Integer id)

3、获取Cookie中数据 :@CookieValue(value="JSESSIONID",required=true,defaultValue="no")

4、获取请求头中数据 :@RequestHeader(value="Accept-Language",required=false,defaultValue="null")

5、获取请求体数据:@RequestBody

  • 当请求方式是POST时,携带的参数用@RequestBody接收。
  • 当请求方式是GET是,携带的参数用@RequestParam接收。

6、@RequestPart,绑定“multipart/data”数据,并可以根据数据类型进项对象转换;

7、获取时间类型参数:@DateTimeFormat(pattern = "yyyy-MM-dd")

  • 指定时间转换格式

3.3、响应参数

1、资源跳转方式:重定向、转发。(默认采用的是转发)

  • 默认跳转方式:

  • 指定跳转方式:

2、域对象响应数据方式(默认Request域)

  • 返回资源路径

  • public String showPageAndData(Model model){
        // 默认是将数据存入request域中
        model.addAttribute("msg":"welcom to springmvc");
        return "page";
    }
    
  • 返回ModelAndView

  • public ModelAndView showPageAndData(ModelAndView modelAndView){
        // 默认是将数据存入request域中
        mondelAndView.addObjest("msg","welcom to springmvc");
        return "page";
    } 
    
  • 指定Session域存储


四、拦截器
4.1、拦截器和过滤器区别
1、Springmvc的拦截器与 Servlet 的 Filter 有相似之处,比如二者都是 AOP 思想的体现,都能实现权限检查,日志记录等。不同的是:
● 使用范围不同: Filter 是 Servlet 规范规定的,只能用于 Web 程序中。而拦截器既可以用于 Web 程序,也可以用于 Application 、 Swing 程序中。
● 规范不同:Filter 是在 Servlet 规范中定义的,是 Servlet 容器支持的。而拦截器是在 Spring 容器内的,是 Spring 框架支持的。
● 使用的资源不同:同其他代码块一样,拦截器也是一个 Spring 的组件,归 Spring 管理,配置在 Spring 文件中,因此能使用 Spring 里的任何资源、对象,例如 Service 对象、数据源、事务管理等,通过 IOC 注入到拦截器即可;而 Filter 则不能。
● 深度不同:Filter 只在 Servlet 前后起作用。而拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。所以在 Spring 架构的程序中,要优先使用拦截器。
4.2、拦截器的实现方式
4.2.1、实现 HandlerInterceptor 接口
1、自定义一个类实现 HandlerInterceptor 接口:
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {

//在请求处理的方法之前执行
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
    System.out.println("------------处理前------------");
    return true;
}

//在请求处理方法执行之后执行
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
    System.out.println("------------处理后------------");
}

//在dispatcherServlet处理后执行,做清理工作.
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    System.out.println("------------清理------------");
}

}

preHandle()方法:该方法在执行控制器方法之前执行。返回值为Boolean类型,如果返回false,表示拦截请求,不再向下执行,如果返回true,表示放行,程序继续向下执行(如果后面没有其他Interceptor,就会执行controller方法)。所以此方法可对请求进行判断,决定程序是否继续执行,或者进行一些初始化操作及对请求进行预处理。

postHandle()方法:该方法在执行控制器方法调用之后,且在返回ModelAndView之前执行。由于该方法会在DispatcherServlet进行返回视图渲染之前被调用,所以此方法多被用于处理返回的视图,可通过此方法对请求域中的模型和视图做进一步的修改。

afterCompletion()方法:该方法在执行完控制器之后执行,由于是在Controller方法执行完毕后执行该方法,所以该方法适合进行一些资源清理,记录日志信息等处理操作。
2、在 springmvc.xml 的配置文件配置拦截器

mvc:interceptors
mvc:interceptor



<mvc:mapping path="/**"/>


</mvc:interceptor>

....
</mvc:interceptors>
4.2.2、实现 WebRequestInterceptor 接口
1、自定义一个类实现 WebRequestInterceptor 接口
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor;

public class MyInterception implements WebRequestInterceptor {

// 该方法的返回值为void,不是 boolean。所以该方法不能用于请求阻断,一般用于资源储备。
@Override
public void preHandle(WebRequest webRequest) throws Exception {

}

// preHandle 中准备的数据都可以通过参数 WebRequest 访问。ModelMap 是Controller 处理之后返回的 Model 对象,可以通过改变它的属性来改变 Model 对象模型,达到改变视图渲染效果的目的。
@Override
public void postHandle(WebRequest webRequest, ModelMap modelMap) throws Exception {

}

// Exception 参数表示的是当前请求的异常对象,如果 Controller 抛出来的异常已经被处理过,则Exception 对象为 nu。
@Override
public void afterCompletion(WebRequest webRequest, Exception e) throws Exception {

}

}
4.3、拦截器的执行流程
1、单个拦截器执行流程:

● 程序首先执行拦截器类中的preHandle()方法,如果该方法返回值是true,则程序会继续向下执行处理器中的方法,否则不再向下执行;在业务控制器类Controller处理完请求后,会执行postHandle()方法,而后会通过DispatcherServlet向客户端返回相应;在DispatcherServlet处理完请求后,才会执行afterCompletion()方法。
2、多个拦截器执行流程:

● 当多个拦截器同时工作时,它们的preHandle()方法会按照配置文件中拦截器的配置顺序执行,而它们的postHandle()方法和afterCompletion()方法则会按照配置顺序的反序执行。
五、异常处理
5.1、Spring MVC 提供的 SimpleMappingExceptionResolver
1、在Spring的配置文件applicationContext.xml中增加以下内容
● SimpleMappingExceptionResolver 实现了 HandlerExceptionResolver 接口,局异常处理器都需要实现该接口。

/WEB-INF/jsp/custom_error.jsp 2、自定义一个异常类 public class CustomException extends Exception { //异常信息 public String message;
public CustomException(String message) {
    super(message);
    this.message = message;
}

public String getMessage() {
    return message;
}

public void setMessage(String message) {
    this.message = message;
}

}
● 使用SimpleMappingExceptionResolver进行异常处理,具有集成简单、有良好的扩展性(可以任意增加自定义的异常和异常显示页面)、对已有代码没有入侵性等优点,但该方法仅能获取到异常信息,若在出现异常时,对需要获取除异常以外的数据的情况不适用。
5.2、实现接口 HandlerExceptionResolver 自定义异常处理器
1、自定义异常处理类
public class CustomExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {

    ex.printStackTrace();
    CustomException customException = null;

    //如果抛出的是系统自定义的异常则直接转换
    if(ex instanceof CustomException) {
        customException = (CustomException) ex;
    } else {
        //如果抛出的不是系统自定义的异常则重新构造一个未知错误异常
        //这里我就也有CustomException省事了,实际中应该要再定义一个新的异常
        customException = new CustomException("系统未知错误");
    }

    //向前台返回错误信息
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("message", customException.getMessage());
    modelAndView.setViewName("/WEB-INF/jsp/error.jsp");
    
    return modelAndView;
}

}
2、在 applicationContext.xml 文件中配置这个自定义的异常处理器


5.3、使用 @ControllerAdvice + @ExceptionHandler 注解实现异常处理
1、定义一个 Controller
/**

  • 如果使用@ControllerAdvice + @ExceptionHandler的同时实现HandlerExceptionResolver接口
  • 则@ExceptionHandler指明异常的处理类型的功能会失效,即会处理所有异常
  • @author Howick

*/
@ControllerAdvice
public class CustomExceptionHandler {

/**
 * 可以@ExceptionHandler用来指明异常的处理类型
 */
@ExceptionHandler(Exception.class)
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
        Exception ex) {
    ModelAndView mav = new ModelAndView();
    mav.addObject("message", ex.getMessage());
    mav.setViewName("error");
    return mav;
}

}
5.4、异常处理流程图


● 处理异常优先级
(1)SpringMVC会先从配置文件找异常解析器HandlerExceptionResolver
(2)如果找到了异常异常解析器,那么接下来就会判断该异常解析器能否处理当前发生的异常
(3)如果可以处理的话,那么就进行处理,然后给前台返回对应的异常视图
(4)如果没有找到对应的异常解析器或者是找到的异常解析器不能处理当前的异常的时候,就看当前的Controller中有没有提供对应的异常处理器,如果提供了就由Controller自己进行处理并返回对应的视图
(5)如果配置文件里面没有定义对应的异常解析器,而当前Controller中也没有定义的话,就看有没有全局ControllerAdvice提供的全局异常处理器,如果没有那么该异常就会被抛出来。
六、执行流程
6.1、【流程图】

6.2、流程阐述
● 用户发送请求到前端控制器 DispatcherServlet。
● DispatcherServlet 接收请求后,将根据 请求信息 交给处理器映射器 HandlerMapping(处理器映射器)。
● HandlerMapping 根据用户的 URL 请求,查找匹配该 URL 的 Handler (可查找xml配置或注解配置),生成处理器对象及处理器拦截器等 执行链 ,返回给 DispatcherServlet。
● DispatcherServlet 在调用 HandlerAdapter(处理器适配器)。
● HandlerAdapter 调用具体的处理器(Handler / Controller)。
● 处理器(Handler / Controller)处理之后返回 ModelAndView 对象。
● HandlerAdapter 将 Handler 执行结果 ModelAndView 返回给 DispatcherServlet。
● DispatcherServlet 将 MondelAndView 传给 ViewReslover(视图解析器)。
● ViewReslover 解析后返回具体 View (视图)。
● DispatcherServlet 根据 View 进行渲染视图(即将模型数据填充至视图中)。
● DispatcherServlet 响应用户。
6.3、组件解析
1、DispatcherServlet :前端控制器
● 用户请求到达前端控制器,它就相当于 mvc 模式中的 c ,DispatcherServlet 是整个流程控制的中心。由它调用其他组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。
2、HandlerMapping :处理器映射器
● HandlerMapping 负责根据用户请求 URL 找到 Handler(处理器),SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式、实现接口方式、注解方式等。
3、HandlerAdapter :处理器适配器
● 通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
4、Handler :处理器
● Handler 是继 DispatchServlet 前端控制器的后端控制器,在 DispatchServlet 的控制下 Handler 对具体的用户请求进行处理。
5、ViewResolver:视图解析器
● ViewResolver 负责将处理结果生成 View 视图,ViewResolver 首先根据逻辑视图名解析成物理视图名(具体页面地址),再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
6、View:视图
● SpringMVC 框架提供了很多 View 视图支持,包括包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
七、父子容器
7.1、父子容器阐述

● Servlet WebApplicationContext:这是对J2EE三层架构中的web层进行配置,如控制器(controller)、视图解析器(view resolvers)等相关的bean。通过spring mvc中提供的DispatchServlet来加载配置,通常情况下,配置文件的名称为spring-servlet.xml。
● Root WebApplicationContext:这是对J2EE三层架构中的service层、dao层进行配置,如业务bean,数据源(DataSource)等。通常情况下,配置文件的名称为applicationContext.xml。在web应用中,其一般通过ContextLoaderListener来加载。
● Spring是父容器,SpringMVC是其子容器,并且在Spring父容器中注册的Bean对于SpringMVC容器中是可见的,而在SpringMVC容器中注册的Bean对于Spring父容器中是不可见的,也就是子容器可以看见父容器中的注册的Bean,父容器并不感知子容器注册的Bean。
"如下面的配置会默认打开以下配置。以下配置会默认声明了@Required、@Autowired、 @PostConstruct、@PersistenceContext、@Resource、@PreDestroy等注解。"

context:annotation-config/
"还有一个和SpringMVC相关如下配置,经过验证,这个是SpringMVC必须要配置的,因为它声明了@RequestMapping、@RequestBody、@ResponseBody等。并且,该配置默认加载很多的参数绑定方法,比如json转换解析器等。"

<mvc:annotation-driven />
" 替换了如下配置"

7.2、相关问题
1、为什么要有父子容器?
● 父子容器的主要作用应该是用来划分框架边界。在J2EE三层架构中,在 service 层我们一般是用 spring 框架来管理,为在 web 层则由多种选择,如 springmvc 、struts等。因此,通常对于 web 层我们会使用单独的配置文件。如果要更换配置文件只需要替换 web 层配置文件就行。
2、是否可以把所有类都通过父容器来管理?
● 不行,因为在解析@ReqestMapping注解的过程中initHandlerMethods()函数只是对Spring MVC 容器中的bean进行处理的,并没有去查找父容器的bean, 因此不会对父容器中含有@RequestMapping注解的函数进行处理,更不会生成相应的handler。所以当请求过来时找不到处理的handler,导致404。
3、是否可以把我们所需的类都放入 spring-mvc 子容器里管理?
● 把包扫描配置在 spring-servlet.xml 中是可行的,但不推荐这么做,因为如果项目中有用到事务、AOP这部分配置也需要同步到 Spring-mvc 子容器配置文件来,不然可能会有失效的情况出现。
4、通过两个容器同时来管理所有的类?
● 会在两个父子IOC容器中生成大量的相同bean,这就会造成内存资源的浪费。

posted @ 2021-12-02 18:58  菜鸟-肥龙  阅读(424)  评论(0)    收藏  举报