1、SpringMVC
-
什么是MVC
MVC是一种软件的构想将软 件按照模型、视图、控制器来划分
M:Model 模型, 指的是JavaBean 实体Bean和业务处理Bean(实体类和Dao、Service)
V:View 视图, 指Html或者JSP等页面
C:Controller 控制器,值工程中的Servlet,作用接收请求、响应请求
MVC工作流程:
用户通过视图发生请求到服务器,在服务器中被Controller接收,Controller调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller在根据请求处理的结果找到相应的View视图层,渲染数据后最终响应给游览器
2.什么是SpringMVC
SpringMVC是Spring的一个后续产品,是Spring的一个子模块,是Spring为表述层开发提供的一整套完备的解决方案
3.SpringMVC的特点
- Spring家族原生产品,与IOC容器等基础设施无缝对接
- 基于原生的Servlet,通过功能强大的DispatchServlet,对请求和响应进行统一处理
- 代码清晰简洁
- 性能卓越
2、HelloWorld
web.xml配置
spring中要求配置文件位于recesouces下,则可以在web.xml的servlet配置中,配置init-param指定配置文件的位置,如果没有配置init-param,则需要在WEB-INF下创建名称为[servlet-name]-servlet.xml的配置文件
<!-- 1.配置控制器 DispatcherServlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定SpringMVC配置文件的位置 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<!-- 指定当前servlet的启动顺序 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<!-- "/" 所有的请求(不包括.jsp),交由控制器DispatcherServlet去处理 -->
<!-- "/*" 所有的请求(包括.jsp) -->
<url-pattern>/</url-pattern>
</servlet-mapping>
springMVC.xml
<!-- 1.组件加载-->
<context:component-scan base-package="com.potato.controller"/>
<!-- 2.静态资源处理 需要和 <mvc:annotation-driven /> 一起使用-->
<mvc:default-servlet-handler />
<!-- 3.开启mvc注解驱动-->
<mvc:annotation-driven />
<!-- 4.视图配置-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/static/html/" />
<property name="suffix" value=".jsp" />
</bean>
HelloController.java
@Controller
@RequestMapping("/test")
public class HelloController {
@RequestMapping("/index")
public String index() {
return "index";
}
@RequestMapping("/hello")
public String hello() {
return "hello";
}
}
index.jsp
在/WEB-INF/static/html/下创建index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
</head>
<body>
<h1>首页</h1>
<a href="${pageContext.request.contextPath}/hello">跳转</a>
</body>
</html>
配置启动Tomacat 即可完成web项目的访问
3、 @ReuqestMapping 注解
用于标识类或者方法
@Controller
@RequestMapping("/test")
public class HelloController {
@RequestMapping("/index") //访问路径为 /test/index
public String index() {
return "index";
}
@RequestMapping("/error") //访问路径为 /test/error
public String error() {
return "error";
}
}
@ReuqestMapping 属性值:
-
value: 通过请求路径匹配请求映射。可以是一个数组,表示该请求映射可以处理多个请求地址
-
method:通过请求方式匹配请求映射,请求方式 get/post/delete/put
-
params:通过请求参数匹配请求映射
-
headers:通过请求头匹配请求映射
@Controller
@RequestMapping("/test")
public class HelloController {
// value
// 可以处理 /test/index 和 /test/main 请求
@RequestMapping(value = {"index", "main"})
public String index() {
return "index";
}
// method
// 只能处理 /test/index2 并且 请求方式为GET的请求
@RequestMapping(value = "index2", method = RequestMethod.GET)
public String index2() {
return "index";
}
// params
// 只能处理 /test/index3 并且 携带username参数,password参数的值必须是123456,age参数不能等于20,不允许有sex参数
@RequestMapping(value = "index3", params = {"username", "password=123456", "age!=20", "!sex"})
public String index3() {
return "index";
}
//headers
// 只能处理 /test/index4 并且 请求头的Host值必须为localhost:8080
@RequestMapping(value = "index3", headers = {"Host=localhost:8080"})
public String index4() {
return "index";
}
}
ant风格,路径规则、匹配
“?” :表示任意单个字符
“*” :表示任意0个或多个字符
“**” :表示任意一层或多层目录
//注意:在使用 ”**“时,只能使用 /**/xxx 的形式
// 表示无论是 /aat/test 或者 /abt/test、/act/test 等等请求,都可以匹配到当前映射方法
@RequestMapping("/a?t/test")
public String test() {
return "index";
}
restful风格,路径占位符、传值
路径占位符常用于restful风格,在响应的@RequestMapping注解的value属性中通过占位符{xxx}表示要传输的数据,在通过@PathVarlable注解获取占位符中的值
/** 3. restful风格 */
//在@RequestMapping注解的value属性中通过占位符{xxx}表示要传输的数据,在通过@PathVarlable注解获取占位符中的值
@RequestMapping("/restful/{id}/{pageIndex}")
public String restful(@PathVariable("id") String id,@PathVariable("pageIndex") Integer pageIndex) {
System.out.println("id:"+id);
System.out.println("pageIndex:"+pageIndex);
return "index";
}
派生注解
@GetMapping、@PostMapping、@DeleteMapping、@PutMapping
4、获取请求数据
原生ServletAPI获取请求参数
控制器方法形参获取请求参数
@RequestParam
@RequesHeader
@CookieValue
通过POJO获取请求参数
解决请求乱码问题
/**
* 1.原生ServletAPI获取请求参数
*/
@RequestMapping("index")
public String index(HttpServletRequest request) {
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username:" + username);
System.out.println("password:" + password);
return "index";
}
/**
* 2.控制器方法形参获取请求参数,请求参数名和形参名 必须要一致
*/
@RequestMapping("index2")
public String index2(String username, String password) {
System.out.println("username:" + username);
System.out.println("password:" + password);
return "index";
}
/**
* 3.@RequestParam, 请求参数名和形参名 不一致
* value/name: 请求参数的名称,name和value两个属性基本是等价的,
* required:当前参数是否必须存在
* defaultValue:当前参数的默认值
*/
@RequestMapping(value = "index3")
public String index3(@RequestParam("user_name") String username) {
System.out.println("username:" + username);
return "index";
}
/**
* 4.@RequestHeader 获取请求头信息
* 和@RequestParam 一样的使用方法
*/
@RequestMapping(value = "index4")
public String index4(@RequestHeader("Host") String host) {
System.out.println("host:" + host);
return "index";
}
/**
* 5.@CookieValue 获取cookie数据
* 和@RequestParam 一样的使用方法
*/
@RequestMapping(value = "index5")
public String index5(@CookieValue("JSESSIONID") String JSESSIONID) {
System.out.println("JSESSIONID:" + JSESSIONID);
return "index";
}
/**
* 6.通过POJO获取请求参数
* 请求参数的名称和POJO类对象的属性名 要一致
*/
@RequestMapping(value = "index6")
public String index6(User user) {
System.out.println(user);
return "index";
}
解决乱码
get请求乱码 由tomcat照成,修改tomcat的配置文件
post乱码 配置CharacterEncodingFilter
<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>
<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、域对象数据共享
1.ServletAPI向request域对象共享数据
2.ModelAndView向request域对象共享数据
3.Model向request域对象共享数据
4.Map向request域对象共享数据
5.ModelMap向request域对象共享数据
6.Model、ModelMap、Map的关系
7.向session域对象共享数据
8.向application域对象共享数据
//1.ServletAPI向request域对象共享数据
@RequestMapping("/testServletApi")
public String testServletApi(HttpServletRequest request) {
request.setAttribute("testRequestScope", "向域对象中共享的数据");
return "index";
}
//2.ModelAndView向request域对象共享数据
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(ModelAndView mav) {
// 向域对象中添加数据
mav.addObject("testRequestScope", "向域对象中共享的数据");
// 跳转的视图页面
mav.setViewName("index");
return mav;
}
//3.Model向request域对象共享数据
@RequestMapping("/testModel")
public String testModel(Model model) {
model.addAttribute("testRequestScope", "向域对象中共享的数据");
return "index";
}
//4.Map向request域对象共享数据
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map) {
map.put("testRequestScope", "向域对象中共享的数据");
return "index";
}
//5.ModelMap向request域对象共享数据
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap) {
modelMap.addAttribute("testRequestScope", "向域对象中共享的数据");
return "index";
}
//6.Model、ModelMap、Map的关系 都是BindingAwareModelMap
// public interface Model
// public class ModelMap extends LinkedHashMap<String, Object>
// public class ExtendedModelMap extends ModelMap implements Model
// public class BindingAwareModelMap extends ExtendedModelMap
//7.向session域对象共享数据
@RequestMapping("/testSessionScopeByServletApi")
public String testSessionScopeByServletApi(HttpSession session) {
session.setAttribute("testSessionScope", "向域对象中共享的数据");
return "index";
}
//8.向application域对象共享数据
@RequestMapping("/testApplicationScopeByServletApi")
public String testApplicationScopeByServletApi(HttpSession session) {
ServletContext servletContext = session.getServletContext();
servletContext.setAttribute("testApplicationScope", "向域对象中共享的数据");
return "index";
}
5、视图
转发视图 InternalResourceView
重定向视图 RedirectView
@Controller
@RequestMapping("/test")
public class ViewController {
@RequestMapping("/index")
public String index() {
return "index";
}
// 转发视图,游览器发送了一次请求 请求地址不变
@RequestMapping("testForward")
public String testForward() {
return "forward:/test/index";
}
// 重定向视图,游览器发送了两次请求 请求地址改变
@RequestMapping("testForward")
public String testRedirect() {
return "redirect:/test/index";
}
}
视图控制器 InternalResourceViewResolver
路径只是为了跳转页面 mvc:view-controller
<!--
当访问路径仅仅只是为了跳转页面,可以通过配置 mvc:view-controller
但是配置此标签后,其他控制器将失效,因此还需要配置 <mvc:annotation-driven/>
-->
<mvc:view-controller path="/" view-name="index"/>
<mvc:annotation-driven/>
6、HttpMessageConverter 报文信息转换器
HttpMessageConverter :将请求报文转换为java对象,或 将java对象转换为响应报文
HttpMessageConverter提供两个注解和两个类型:
@RequestBody,@ResponseBody,RequestEntity,ResponseEntity,@RestController
@RequestBody
将请求报文(请求体)转换为java对象
// @RequestBody,
@RequestMapping("form")
public String request(@RequestBody String formData) {
// 表单的数据以JSON字符串 的形式赋值给formData
System.out.println(formData);
return "success";
}
@ResponseBody
将java对象转换为响应报文(响应体)
// @ResponseBody
//此处响应的"success" 不在作为视图进行解析,而是直接作为响应体返回给客户端
//如果响应的数据是一个java对象类型的 则需要导入json依赖,则返回的java对象自动转换为json对象
@ResponseBody
@RequestMapping("response")
public String response(){
return "success";
}
RequestEntity
可以接受整个请求报文,包括请求头和请求体
// RequestEntity,
@RequestMapping("request2")
public String request2(RequestEntity<String> request) {
// 获取请求头信息
HttpHeaders headers = request.getHeaders();
// 获取请求体信息
String body = request.getBody();
return "success";
}
ResponseEntity
ResponseEntity用于控制方法的返回值类型,该控制器方法的返回值就是响应到游览器的响应报文
(也就是自定义响应报文)
@RestController
@RestController 替代 @Controller
表示当前控制器所有方法返回的结果 都是响应体,不是视图
7、文件下载、上传
文件下载
通过ResponseEntity 进行文件下载
@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
//获取ServletContext对象
ServletContext servletContext = session.getServletContext();
//获取服务器中文件的真实路径
String realPath = servletContext.getRealPath("/static/img/1.jpg");
//创建输入流
InputStream is = new FileInputStream(realPath);
//创建字节数组
byte[] bytes = new byte[is.available()];
//将流读到字节数组中
is.read(bytes);
//创建HttpHeaders对象设置响应头信息
MultiValueMap<String, String> headers = new HttpHeaders();
//设置要下载方式以及下载文件的名字
headers.add("Content-Disposition", "attachment;filename=1.jpg");
//设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
//关闭输入流
is.close();
return responseEntity;
}
文件上传
要求form表单的请求方式必须为post,并且添加属性enctype=“multipart/form-data”
SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息
上传步骤:
a>添加依赖
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
b>在SpringMVC的配置文件中添加配置:
<!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
c>控制器方法:
@RequestMapping("/testUp")
public String testUp(MultipartFile photo, HttpSession session) throws IOException {
//获取上传的文件的文件名
String fileName = photo.getOriginalFilename();
//处理文件重名问题
String hzName = fileName.substring(fileName.lastIndexOf("."));
fileName = UUID.randomUUID().toString() + hzName;
//获取服务器中photo目录的路径
ServletContext servletContext = session.getServletContext();
String photoPath = servletContext.getRealPath("photo");
File file = new File(photoPath);
if(!file.exists()){
file.mkdir();
}
String finalPath = photoPath + File.separator + fileName;
//实现上传功能
photo.transferTo(new File(finalPath));
return "success";
}
8、拦截器
1.拦截器的配置
SpringMVC中的拦截器用于拦截控制器方法的执行
SpringMVC中的拦截器需要实现HandlerInterceptor接口,重写preHandle方法
public class FirstInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 拦截后进行逻辑处理的内容 返回true 代表放行,返回false 表示拦截
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置:
<mvc:interceptors>
<!-- 指定规则进行拦截-->
<mvc:interceptor>
<!-- 拦截所有请求但是不包括 "/test" 请求 /* 表示只有一层路径 /**表示任意多层路径-->
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/test"/>
<!-- 拦截请求后 进行处理的类-->
<bean id="firstInterceptor" class="com.potato.interceptor.FirstInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
2.拦截器的三个抽象方法
SpringMVC中的拦截器有三个抽象方法:
preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
postHandle:控制器方法执行之后执行postHandle()
afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()
3.多个拦截器的执行顺序
a>若每个拦截器的preHandle()都返回true
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:
preHandle()会按照配置的顺序执行,而postHandle()和afterComplation()会按照配置的反序执行
b>若某个拦截器的preHandle()返回了false
preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行
客户端–> 过滤器 –> DispatcherServlet –> 拦截器 –>Controller
9、异常处理器
1.基于配置的异常处理
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--
prop的键表示 需要进行处理的异常
prop的值表示 若出现异常时,需要跳转到的视图名称
-->
<prop key="java.lang.ArithmeticException">error</prop>
</props>
</property>
<!--
exceptionAttribute属性设置一个值,将异常信息在 请求域 中进行共享,通过ex可以获取异常的信息内容
-->
<property name="exceptionAttribute" value="ex"/>
</bean>
2.基于注解的异常处理
@ControllerAdvice
public class ExceptionController {
/**
* @param ex 异常信息
* @param model 把异常信息添加到域对象中
* @return 异常页面
*/
@ExceptionHandler(value = {ArithmeticException.class, NullPointerException.class})
public String exceptionMethod(Exception ex, Model model) {
model.addAttribute(ex);
return "error";
}
}
10、注解配置SpringMVC
1.WebInit
创建初始化类,代替web.xml
在Servlet3.0+环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果找到的话就用它来配置Servlet容器。
Spring提供了这个接口的实现,名为SpringServletContainerInitializer,这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给它们来完成。Spring3.2引入了一个便利的WebApplicationInitializer基础实现,名为AbstractAnnotationConfigDispatcherServletInitializer,当我们的类扩展了AbstractAnnotationConfigDispatcherServletInitializer并将其部署到Servlet3.0容器的时候,容器会自动发现它,并用它来配置Servlet上下文。
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 指定spring的配置类
*
* @return
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
/**
* 指定SpringMVC的配置类
*
* @return
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
/**
* 指定DispatcherServlet的映射规则,即url-pattern
*
* @return
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/**
* 添加过滤器
*
* @return
*/
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceResponseEncoding(true);
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};
}
}
2.WebConfig
代替SpringMVC的配置文件
// 1.组件扫描 2.视图解析 3.view-controller 4.default-servlet-handler
// 5.mvc注解驱动 6.文件上传解析器 7.拦截器 8.异常处理
@Configuration
// 1.组件扫描
@ComponentScan("com.potato.controller")
// 5.mvc注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
//2.视图解析
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/static/");
viewResolver.setSuffix(".jsp");
registry.viewResolver(viewResolver);
}
//3.view-controller
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
WebMvcConfigurer.super.addViewControllers(registry);
}
//4.default-servlet-handler
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
WebMvcConfigurer.super.configureDefaultServletHandling(configurer);
}
//6.文件上传解析器
public CommonsMultipartResolver multipartResolver(CommonsMultipartResolver commonsMultipartResolver) {
commonsMultipartResolver.setDefaultEncoding("UTF-8");
commonsMultipartResolver.setMaxUploadSize(419430400);
return commonsMultipartResolver;
}
//7.拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册自己定义的拦截器
//registry.addInterceptor(new FirstInterceptor()).addPathPatterns("/**");
WebMvcConfigurer.super.addInterceptors(registry);
}
//8.异常处理 可以使用注解配置代替
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties prop = new Properties();
prop.setProperty("java.lang.ArithmeticException", "error");
//设置异常映射
resolver.setExceptionMappings(prop);
//设置共享异常信息的键
resolver.setExceptionAttribute("ex");
resolvers.add(resolver);
WebMvcConfigurer.super.configureHandlerExceptionResolvers(resolvers);
}
}
11、SpringMVC的执行流程
-
用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获。
-
DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:
-
根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回。
-
DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。
-
如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】
-
提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
-
Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。
-
此时将开始执行拦截器的postHandle(…)方法【逆向】。
-
根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图。
-
渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。
-
将渲染结果返回给客户端。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/289030.html