SpringMVC框架基本使用指南
一 MVC的基本概念
SpringMVC是基于MVC模式设计的。MVC模式将应用的输入,处理,输出分离开来,形成模型层,控制层,视图层。M:代表数据模型,即根据业务逻辑处理的数据以及一些安全,日志模块。V:代表视图,即jsp页面,html页面等表现数据结果的界面。C:代表控制器,接受视图的请求,根据请求选择对应的数据模型进行处理。并将结果返回给对应的视图。
二 MVC的优点
- 可以有效的降低模块之间的耦合度,当视图发生改变时,模型层和控制层无需改动。
- 可以提高模型的复用性,多个视图可以共用同一套数据模型,无需重写一套模型。
- 简单易用。
- 方便后期的维护。
三 SpringMVC的工作流程
- 用户向DispatcherServlet发送请求。
- DispatcherServlet将请求交个HandlerMapping处理。
- HandlerMapping根据URL找到对应的Controller,并返回给DispatcherServlet。
- DispatcherServlet调用HandlerAdapter进行参数封装,数据格式转换,数据验证等操作,最后调用具体的Controller方法。
- Controller方法执行完后,返回ModelAndView对象给HandlerAdapter,HandlerAdapter再返回给DispatcherServlet。
- DispatcherServlet将ModelAndView对象交给ViewReslover视图解析器处理。
- ViewReslover视图解析器解析成视图,返回给DispatcherServlet。
- DispatcherServlet将数据填充到视图中。
- DispatcherServlet将渲染后的视图返回给用户。
四 搭建SpringMVC框架
自从Spring3以后,SpringMVC框架就支持xml和java两种配置方法,下面我们先看看如何使用xml配置SpringMVC框架.
1.xml配置SpringMVC框架
第一步:导入Maven依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
第二步:配置web.xml文件
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/SpringMVC_Config.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
servlet配置了DispatcherServlet,初始参数为config/SpringMVC_Config.xml,servlet-mapping里配置这个servlet会拦截所有的请求。如果没有指定初始化参数,SpringMVC默认会去找/WEB-INF/SpringMVC-servlet.xml。
第三步:配置SpringMVC_Config.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 http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.study.Controller"/>
<!-- 不拦截静态资源 -->
<mvc:default-servlet-handler />
<!--<mvc:resources mapping="images/," location="classpath:images/"></mvc:resources>-->
<!-- 开启mvc注解支持-->
<mvc:annotation-driven />
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
上文中mvc:default-servlet-handler标签和mvc:resources标签都可以实现不拦截静态资源的效果,任选其一。
第四步:测试框架配置
视图 index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<body>
<h2>Hello World!</h2>
${key}
<h1><% out.print(request.getAttribute("key"));%></h1>
</body>
</html>
控制器
package com.study.Controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class TestController {
@RequestMapping("/hello")
public ModelAndView print()
{
ModelAndView model=new ModelAndView();
model.setViewName("hello");
model.addObject("key","546565151231");
return model;
}
@RequestMapping("/")
public ModelAndView first()
{
ModelAndView model=new ModelAndView();
model.setViewName("index");
model.addObject("key","5465651");
return model;
}
}
@Controller注解:标注该类为一个Controller类。
@RequestMapping注解:用于配置方法的请求映射路径。
运行服务器,根据请求url http://localhost:8080 DispatcherServlet最终会调用first方法,first方法创建了一个ModelAndView 对象,调用setViewName方法设置视图名,调用addObject方法添加数据。最终将对象返回给DispatcherServlet。DispatcherServlet此时调用视图解析器解析视图,最终渲染到index视图中。
运行结果:
2.java配置SpringMVC框架
使用java配置SpringMVC框架,可以完全摆脱web.xml文件。只需添加几个配置类即可
第一步:配置MVC框架配置类
package com.study.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
/**
* Created by forget on 2019/6/9.
*/
@EnableWebMvc
@Configuration
@ComponentScan(basePackages = "com.study.Controller")
public class SpringMVCConfig implements WebMvcConfigurer{
// @Bean
// public InternalResourceViewResolver internalResourceViewResolver(){
配置视图解析器
// InternalResourceViewResolver internalResourceViewResolver=new InternalResourceViewResolver();
// internalResourceViewResolver.setPrefix("/WEB-INF/jsp/");
// internalResourceViewResolver.setSuffix(".jsp");
// return internalResourceViewResolver;
// }
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry){
// 将本地文件路径映射为资源路径
registry.addResourceHandler("/static/**").addResourceLocations("file:E:/ItemStatic/static/");
registry.addResourceHandler("/upload/**").addResourceLocations("file:E:/ItemStatic/upload/");
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry){
InternalResourceViewResolver internalResourceViewResolver=new InternalResourceViewResolver();
internalResourceViewResolver.setPrefix("/WEB-INF/jsp/");
internalResourceViewResolver.setSuffix(".jsp");
registry.viewResolver(internalResourceViewResolver);
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
// 不拦截静态资源
configurer.enable();
}
}
@EnableWebMvc注解表示启用MVC框架的java配置,此注解必须配合@Configuration注解使用。
@ComponentScan注解表示开启注解扫描,相当于context:component-scan标签
WebMvcConfigurer接口是Spring5.0以后的MVC配置类,Spring5.0以前使用WebMvcConfigurerAdapter作为配置类,Spring5.0以后这个类就被废弃了。你通过实现该接口,重写接口中的默认方法,配置MVC 的一些功能,比如静态资源路径的映射,跨域等。
关于视图解析器配置有两种方式,第一种将视图解析器配置成一个bean,第二种重写configureViewResolvers方法,直接注册一个视图解析器。
第二步:配置父应用配置类
package com.study.Config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
/**
* Created by forget on 2019/6/9.
*/
@Configuration
@ComponentScan(basePackages = "com.study")
public class RootConfig {
}
该类代表父应用上下文,子应用上下文可以访问父应用上下文,但父应用上下文不能访问子应用上下文。当存在多个DispatcherServlet时可以在多个DispatcherServlet的应用上下文之间共享bean,比如数据源之类的。你也可以不配置该类,只使用一个DispatcherServlet
第三步:加载配置类
package com.study.Config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* Created by forget on 2019/6/9.
*/
public class ConfigDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
// return new Class[]{RootConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMVCConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
该类用于代替web.xml,加载配置类。该类之所以可以不用web.xml就能完成配置类的自动加载,是因为在Servlet3.0环境下,Servlet 容器会在 classpath 下搜索实现了 javax.servlet .ServletContainerInitializer 接口,而Spring实现了这个接口,AbstractAnnotationConfigDispatcherServletInitializer类正是间接实现了这个接口,所以在Servlet3.0容器中,就可以通过继承该类来加载配置文件了。
getRootConfigClasses方法用于加载父应用配置类,当只有一个子应用上下文(即只有一个DispatcherServlet)时可以不配置。
getServletConfigClasses方法用于加载DispatcherServlet的配置文件,每个DispatcherServlet都有自己单独的配置类。
getServletMappings方法用于配置DispatcherServlet的映射路径。
到此为止java的配置就已经全部完成了,接下来测试一下配置结果,还是原来那个控制器和视图。
第四步:测试结果
五 配置拦截器
1.xml配置拦截器
<mvc:interceptors>
<bean id="myIntersrc" class="com.study.Interceptors.MyInterceptorsrc"></bean>
<mvc:interceptor>
<mvc:mapping path="/"/>
<bean id="myInter" class="com.study.Interceptors.MyInterceptor"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/"/>
<bean id="Int" class="com.study.Interceptors.ExtendInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
mvc:interceptor标签代表一个拦截器,mvc:mapping标签代表拦截器拦截的请求url,bean标签在这里代表拦截器。你也可以直接在mvc:interceptors标签下定义拦截器的bean,这样该拦截器会拦截所有请求。
2.java配置拦截器
@Override
public void addInterceptors(InterceptorRegistry registry){
// 注册拦截器
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/");
registry.addInterceptor(new ExtendInterceptor());
}
addInterceptors方法是WebMvcConfigurer接口的默认方法,你需要实现这个接口,然后重写方法,通过addInterceptor方法注册拦截器,再通过返回值调用addPathPatterns方法,设置拦截的请求URL,如果设置则会拦截所有请求。
PS:拦截器会将静态资源也一起拦截的,即使你使用了已经配置了不拦截静态资源。所以你应该使用mvc:exclude-mapping标签或excludePathPatterns方法将静态资源的路径排除。
在上面的配置中,MyInterceptor是HandlerInterceptor接口的实现类,ExtendInterceptor是抽象类HandlerInterceptorAdapter的子类,间接的实现了HandlerInterceptor接口。在SpringMVC中要定义拦截器必须实现这个接口。
HandlerInterceptor接口中有三个方法,方法参数中的handler是下一个拦截器
-
preHandle方法:在控制器方法调用前执行,通过返回值控制是否放行到下一个方法。
-
postHandle方法:在控制器方法调用后执行。
-
afterCompletion方法:在视图渲染完成后调用。
当存在多个拦截器时,执行顺序如下
-
拦截器1执行前方法
-
拦截器2执行前方法
-
拦截器2执行后方法
-
拦截器1执行后方法
-
拦截器1页面渲染后方法
-
拦截器2页面渲染后方法
拦截器的执行顺序就是拦截器的定义顺序
六 异常处理
SpringMVC有三种统一处理异常的方式。
第一种
使用@ExceptionHandler注解
@ExceptionHandler
public ModelAndView Error(Exception e)
{
ModelAndView modelAndView=new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("errMsg",e.getMessage());
return modelAndView;
}
@ExceptionHandler注解可以指定方法处理的异常类,如果未指定,则默认为方法的参数类型。你可以选择返回视图,也可以选择配合@ResponseBody注解返回json数据。这种方式是局部的,只能处理该方法所在的Controller类出现的异常。
第二种
从Spring3.2开始就有@ControllerAdvice注解,该注解用于控制器增强,它作用于所有被@RequestMapping,@GetMapping等映射注解过的方法 ,因此可以用做全局异常处理,全局数据处理等。
@ControllerAdvice
public class Excepy{
@ExceptionHandler
public ModelAndView Error(Exception e)
{
ModelAndView modelAndView=new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("errMsg",e.getMessage());
return modelAndView;
}
}
PS:该类必须被Spring扫描到,才能处理全局异常。
第三种
实现HandlerExceptionResolver接口,这个接口用于请求过程中产生的异常。
@Order(-1000)
public class Excepy implements HandlerExceptionResolver{
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView modelAndView=new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("errMsg",e.getMessage());
return modelAndView;
}
}
@Order(-1000)注解是为了提高该异常处理器的优先级别,防止异常被Spring的异常处理器捕获。该异常处理器必须被Spring容器管理才能处理异常。
七 转发和重定向
SpringMVC中想进行转发和重定向操作可以参考以下代码
@GetMapping("/red")
public String test01(){
return "redirect:/index";
}
@GetMapping("for")
public String test00(){
return "forward:index";
}
@GetMapping("redMode")
public ModelAndView test03(){
ModelAndView modelAndView=new ModelAndView();
modelAndView.setViewName("redirect:/index");
return modelAndView;
}
@GetMapping("forMode")
public ModelAndView test04(){
ModelAndView modelAndView=new ModelAndView();
modelAndView.setViewName("forward:/index");
return modelAndView;
}
带参数的重定向
SpringMVC3.1版本之后出来了RedirectAttribute功能,专门用于重定向之后还能带参数跳转的.
@GetMapping("redMode")
public ModelAndView test03(RedirectAttributes redirectAttributes){
ModelAndView modelAndView=new ModelAndView();
modelAndView.setViewName("redirect:/hello");
redirectAttributes.addAttribute("key","231321");
redirectAttributes.addFlashAttribute("key2","543231");
return modelAndView;
}
1、使用 RedirectAttributes 的 addAttribute()方法设置参数,则参数将直接拼接在转发url后面,然后可以在通过request.getParameter(“userName”)) 和 直接通过spring mvc配置参数映射接收到参数
2、使用 RedirectAttributes 的 addFlashAttribute()方法设置参数,则参数不会出现在转发url中,然后可以通过modelMap 取出参数