Spring MVC 学习总结 一共 3 篇 分别为


一、 文件上传

1. 简介

  • Spring MVC 为文件上传提供了直接的支持,这种支持是通过即插即用的 MultipartResolver 实现的。Spring 用Jakarta Commons FileUpload 技术实现了一个MultipartResolver 实现类:CommonsMultipartResovler

  • Spring MVC 上下文中默认没有装配 MultipartResovler,因此默认情况下不能处理文件的上传工作,如果想使用 Spring的文件上传功能,需现在上下文中配置 MultipartResolver

  • defaultEncoding: 必须和用户 JSP 的 pageEncoding 属性一致,以便正确解析表单的内容

  • 为了让 CommonsMultipartResovler 正确工作,必须先将 Jakarta Commons FileUpload 及 Jakarta Commons io的类包添加到类路径下。

    链接:http://pan.baidu.com/s/1dE520X7 密码:b8ve


2. 示例

  • springmvc.xm 增加

<!-- 配置 MultipartResolver -->
<bean id="multipartResolver"
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="defaultEncoding" value="UTF-8"></property>
    <property name="maxUploadSize" value="1024000"></property>
</bean>


  • JSP

<form action="springmvc/upload.do" 
    enctype="multipart/form-data" method="post">
	Desc: <input type="text" name="desc"/><br/>
	File: <input type="file" name="file"/> <br/>
	<input type="submit" value="submit"/>
</form>


  • Controller

@RequestMapping("/upload.do")
public String upload(@RequestParam("desc") String desc, 
    @RequestParam("file") MultipartFile file) 
    throws IllegalStateException, IOException {
        if(!file.isEmpty()) {
            System.out.println("desc:"+desc);
            file.transferTo(new File("D:\\"+file.getOriginalFilename()));
        }
     return "success";
}


二、 自定义拦截器

1.  简介

  • Spring MVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口

    – preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false。

    – postHandle():这个方法在业务处理器处理完请求后,但是DispatcherServlet 向客户端返回响应前被调用,在该方法中对用户请求request进行处理。

    – afterCompletion():这个方法在 DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。


2.  拦截器方法执行顺序

---------------------preHandle return true-----------------

----------------preHandle return false--------------


3.  示例

  • springmvc.xml 增加

<mvc:interceptors>
<!-- 配置自定义的拦截器 -->
    <bean class="com.atguigu.springmvc.interceptors.FirstInterceptor"></bean>
    <!-- 配置拦截器作用的路径 -->
    <mvc:interceptor>
        <mvc:mapping path="/emps"/>
        <bean class="com.atguigu.springmvc.interceptors.SecondInterceptor" />
    </mvc:interceptor>
		
        <!-- 配置 LocaleChanceInterceptor -->
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"></bean>
</mvc:interceptors>
  • FirstInterceptor.java 拦截器

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class FirstInterceptor implements HandlerInterceptor{

    /**
     * 该方法在目标方法之前被调用.
     * 若返回值为 true, 则继续调用后续的拦截器和目标方法. 
     * 若返回值为 false, 则不会再调用后续的拦截器和目标方法. 
     * 
     * 可以考虑做权限. 日志, 事务等. 
     */
    @Override
    public boolean preHandle(HttpServletRequest request,
        HttpServletResponse response, Object handler) throws Exception {
        System.out.println("[FirstInterceptor] preHandle");
        return true;
    }

    /**
     * 调用目标方法之后, 但渲染视图之前. 
     * 可以对请求域中的属性或视图做出修改. 
     */
    @Override
    public void postHandle(HttpServletRequest request,
          HttpServletResponse response, Object handler,
          ModelAndView modelAndView) throws Exception {
        System.out.println("[FirstInterceptor] postHandle");
    }
    
    /**
     * 渲染视图之后被调用. 释放资源
     */
    @Override
    public void afterCompletion(HttpServletRequest request,
        HttpServletResponse response, Object handler, Exception ex)
    throws Exception {
        System.out.println("[FirstInterceptor] afterCompletion");
    }
}


三、 异常处理

1. HandlerExceptionResolver

  • Spring MVC 通过 HandlerExceptionResolver 处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。

  • SpringMVC 提供的 HandlerExceptionResolver 的实现类如下:


  • DispatcherServlet 默认装配的 HandlerExceptionResolver :

    – 没有使用 <mvc:annotation-driven/> 配置:



  • – 使用了 <mvc:annotation-driven/> 配置:


2. ExceptionHandlerExceptionResolver

  • 主要处理 Handler 中用 @ExceptionHandler 注解定义的方法。

  • @ExceptionHandler 注解定义的方法优先级问题:例如发生的是NullPointerException,但是声明的异常有RuntimeException 和 Exception,此候会根据异常的最近继承关系找到继承深度最浅的那个 @ExceptionHandler注解方法,即标记了 RuntimeException 的方法

@Controller
public class SpringMVCTest {

    /**
    * 1. 在 @ExceptionHandler 方法的入参中可以加入 Exception 类型的参数, 该参数即对应发生的异常对象
    * 2. @ExceptionHandler 方法的入参中不能传入 Map. 若希望把异常信息传导页面上, 需要使用 ModelAndView 作为返回值
    * 3. @ExceptionHandler 方法标记的异常有优先级的问题. 
    * 4. @ControllerAdvice: 如果在当前 Handler 中找不到 @ExceptionHandler 方法来出来当前方法出现的异常, 
    * 则将去 @ControllerAdvice 标记的类中查找 @ExceptionHandler 标记的方法来处理异常. 
    */
    @ExceptionHandler({ArithmeticException.class})
    public ModelAndView handleArithmeticException(Exception ex){
        System.out.println("出异常了: " + ex);
        ModelAndView mv = new ModelAndView("error");
        mv.addObject("exception", ex);
        return mv;
    }
    
    @RequestMapping("/testExceptionHandlerExceptionResolver")
    public String testExceptionHandlerExceptionResolver(){
	System.out.println("result: " + (10 / 0));
	return "success";
    }
    
    
}


  • ExceptionHandlerMethodResolver 内部若找不到@ExceptionHandler 注解的话,会找@ControllerAdvice 中的@ExceptionHandler 注解方法

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
/**注意这里**/
@ControllerAdvice
public class SpringMVCTestExceptionHandler {

	@ExceptionHandler({ArithmeticException.class})
	public ModelAndView handleArithmeticException(
	                        Exception ex){
		System.out.println("----> 出异常了: " + ex);
		ModelAndView mv = new ModelAndView("error");
		mv.addObject("exception", ex);
		return mv;
	}
	
}


3. ResponseStatusExceptionResolver

  • 在异常及异常父类中找到 @ResponseStatus 注解,然后使用这个注解的属性进行处理。

  • 定义一个 @ResponseStatus 注解修饰的异常类

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value=HttpStatus.FORBIDDEN, 
        reason="用户名和密码不匹配!")
public class UserNameNotMatchPasswordException 
                extends RuntimeException{
	private static final long serialVersionUID = 1L;
}


  • 若在处理器方法中抛出了上述异常:若ExceptionHandlerExceptionResolver 不解析述异常。由于触发的异常 UserNameNotMatchPasswordException带有@ResponseStatus注解。因此会被ResponseStatusExceptionResolver 解析到。最后响应HttpStatus.FORBIDDEN 代码给客户端。HttpStatus.UNAUTHORIZED 代表响应码403,禁止的。关于其他的响应码请参考 HttpStatus 枚举类型源码。

    

  • @ResponseStatus也可以标注在handler方法上,效果是方法正常执行但是页面会跳到HTTP Status错误页面如上图。


4. DefaultHandlerExceptionResolver

  • 对spring的一些特殊的异常进行处理,比如NoSuchRequestHandlingMethodException、HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException、HttpMediaTypeNotAcceptableException等。(默认的无需配置)


5. SimpleMappingExceptionResolver

  • 如果希望对所有异常进行统一处理,可以使用 SimpleMappingExceptionResolver,它将异常类名映射为 视图名,即发生异常时使用对应的视图报告异常

  • springmvc.xml

<!-- 配置使用 SimpleMappingExceptionResolver 来映射异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <!-- 默认exceptionAttribute=exception -->
    <property name="exceptionAttribute" value="ex"></property>
    <property name="exceptionMappings">
        <props>
            <prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
        </props>
    </property>
</bean>	
  • error.jsp

<h4>Error Page</h4>
${requestScope.ex}
  • handler

@RequestMapping("/testSimpleMappingExceptionResolver")
public String testSimpleMappingExceptionResolver(){
    String [] vals = new String[10];
    System.out.println(vals[12]);
    return "success";
}


四、 SpringMVC 运行流程


五、 在 Spring 的环境下使用SpringMVC

1. 问题

  • 需要进行 Spring 整合 SpringMVC 吗 ?

  • 还是否需要再加入 Spring 的 IOC 容器 ?

  • 是否需要再 web.xml 文件中配置启动 Spring IOC 容器的 ContextLoaderListener ?


2. 解答

  • 需要: 通常情况下, 类似于数据源, 事务, 整合其他框架都是放在 Spring 的配置文件中(而不是放在 SpringMVC 的配置文件中). 即实际上放入 Spring 配置文件对应的 IOC 容器中的还有 Service 和 Dao. 

  • 不需要: 都放在 SpringMVC 的配置文件中. 也可以分多个 Spring 的配置文件, 然后使用 import 节点导入其他的配置文件


3. Bean 被创建两次 ?

  • 问题: 若 Spring 的 IOC 容器和 SpringMVC 的 IOC 容器扫描的包有重合的部分, 就会导致有的 bean 会被创建 2 次.

  • 解决:

    ----使 Spring 的 IOC 容器扫描的包和 SpringMVC 的 IOC 容器扫描的包没有重合的部分. 

    ----使用 exclude-filter 和 include-filter 子节点来规定只能扫描的注解

<!-- springmvc.xml 增加 -->
<context:component-scan 
    base-package="me.ziry.springmvc" 
    use-default-filters="false">
    
    <context:include-filter type="annotation" 
        expression="org.springframework.stereotype.Controller"/>
    <context:include-filter type="annotation" 
        expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
        
</context:component-scan>
<!-- beans.xml 增加 -->
<context:component-scan base-package="com.atguigu.springmvc">

    <context:exclude-filter type="annotation" 
        expression="org.springframework.stereotype.Controller"/>
        
    <context:exclude-filter type="annotation" 
        expression="org.springframework.web.bind.annotation.ControllerAdvice"/>

</context:component-scan>


4. 在 Spring MVC 配置文件中引用业务层的 Bean

  • 多个 Spring IOC 容器之间可以设置为父子关系,以实现良好的解耦。

  • Spring MVC WEB 层容器可作为 “业务层” Spring容器的子容器:即 WEB 层容器可以引用业务层容器的 Bean,而业务层容器却访问不到 WEB 层容器的 Bean


六、 SpringMVC 对比 Struts2

  1. Spring MVC 的入口是 Servlet, 而 Struts2 是 Filter

  2. Spring MVC 会稍微比 Struts2 快些. Spring MVC 是基于方法设计, 而 Sturts2 是基于类, 每次发一次请求都会实例一个 Action.

  3. Spring MVC 使用更加简洁, 开发效率Spring MVC确实比 struts2 高: 支持 JSR303, 处理 ajax 的请求更方便

  4. Struts2 的 OGNL 表达式使页面的开发效率相比Spring MVC 更高些. 


附:


(完)


===Spring MVC 学习总结 一共 3 篇 分别为===


注意:本文归作者所有,未经作者允许,不得转载