什么是Spring MVC?
Spring MVC是Spring Framework即Spring框架的一部分,是基于Java来实现MVC的轻量级Web框架。官方文档
Spring MVC的特点?
Spring MVC框架围绕DispatcherServlet【调度Servlet】
来设计的!
DispatcherServlet
的作用是将请求分发到不同的Controller控制器,并且,从Spring 2.5开始,使用Java5或者以上版本的用户可以采用基于注解的形式在Spring MVC中进行开发,简洁的同时提高了开发的效率!
为什么要学习Spring MVC?
Spring MVC框架围绕DispatcherServlet设计
。
Spring MVC框架像其它许多MVC框架一样,以请求为驱动,围绕一个中心Servlet,即DispatcherServlet
来分配请求和进行调度。通俗理解如下图示:【请求分发和调度】
其中DispatcherServlet
本质上也是一个Servlet.
只要实现了Servlet接口就是一个Servlet,而它继承自HttpServlet基类,如下所示:
当发起请求时被前端控制器即DispatcherServlet
拦截到请求,其根据请求参数生成代理请求,找到请求对应的实际控制器即JAVA类
,控制器处理请求,创建数据模型,访问数据库,将模型即ModelAndView
即响应给中心控制器,控制器使用模型和视图来渲染视图结果,将结果返回给前端控制器,前端控制器最后再将结果返回给请求者。
上图为Spring MVC的一个具体完整执行的流程图,
其中实线箭头
的表示Spring MVC框架提供的技术,即已经帮我们实现了的,不需要开发者实现;
其中虚线箭头
的表示需要开发者自己去编码实现的部分!!
其执行流程可分为三大部分
第一部分:根据URL寻找Handler,即适配咱们传递的URL请求是干嘛的
1.DispatcherServlet
表示前端控制器/中心控制器,其是整个Spring MVC的控制中心,用户发出请求,DispatcherServlet
接收请求并拦截请求。
http://localhost:8080/SpringMVC/hello
http://localhost:8080
表示服务器域名SpringMVC
表示部署在服务器上的web站点hello
表示控制器Controller
2.HandlerMapping
为处理器映射。其会被DispatcherServlet
自动调用。
Handler
即处理器.3.HandlerExecution
表示具体的Handler
,其主要作用是根据URL查找控制器,如上面的URL被查找的控制器为: hello
4.HandlerExecution
将解析后的信息传递给DispatcherServlet
。
第二部分:寻找Controller,即适配这个请求到底要做什么
5.HandlerAdapter
为处理器适配器
Controller
,然后去执行Handler
.6.Handler
让具体的Controller
执行.
7.Controller
将执行过后的信息返回给HandlerAdapter
,如:ModelAndView模型和视图
信息
8.HandlerAdapter
将ModelAndView
中的视图逻辑名或带有数据的模型传递给DispatcherServlet
第三部分:视图解析器解析视图和渲染数据
9.DispatcherServlet
调用ViewResolver
即视图解析器来解析HandlerAdapter
传递的逻辑视图名
10.ViewResolver
视图解析器将解析的逻辑视图名传给DispatcherServlet
11.DispatcherServlet
根据ViewResolver
视图解析器解析的视图结果,调用具体的视图
12.最终将视图返回呈现给用户.
注:
ViewResolver
视图解析器具体执行了什么?
- 获取ModelAndView的数据
- 解析ModelAndView的视图名字
- 拼接视图所在的完整路径
- 将数据渲染到这个视图
虚线1: 开发者设置需要返回的视图的名字。
虚线2: 开发者设置和封装相应的数据到Model
中。
虚线3: 开发者自己编写代码,即 Controller
层去调用具体的Service
业务层的逻辑代码。
1.在Maven父项目的基础上新建一个子Module,并右键选择Add Framework Support
添加web应用支持
2.pom.xml
中确定导入了Spring MVC的依赖和其它相关依赖
pom.xml
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>servlet-apiartifactId>
<version>2.5version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.0.RELEASEversion>
dependency>
3.配置web.xml
,注册DispatcherServlet
web.xml
<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>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springmvc-servlet.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
4.在resources
目录下编写Spring MVC绑定的配置文件
springmvc-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
bean>
beans>
5.在WEB-INF
目录下建立jsp
目录并在其目录下再建立测试用的test.jsp
文件,视图都放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端无法直接访问!
test.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
test jsp
${msg}
6.src
目录下编写相应的Controller
HelloController.java
package com.carson.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//这里需要实现Spring mvc中的Controller接口才能作为一个Controller
//只要实现了Controller接口的类,说明这就是一个控制器了
public class HelloController implements Controller {
//重写其中唯一的方法
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//实例化一个ModelAndView模型和视图的对象
ModelAndView mv = new ModelAndView();
//业务代码书写的位置
//这里示例只给其中添加数据,addObject()的参数是一个键值对
mv.addObject("msg","Hello Spring MVC!!");
//视图跳转
mv.setViewName("test");//对应/WEB-INF/html/test.html
//返回ModelAndView
return mv;
}
}
7.编写完Controller后,要在Spring配置文件 即springmvc-servet.xml
中注册bean作为Handler: id对应请求路径,class对应处理请求的类
springmvc-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
bean>
<bean id="/hello" class="com.carson.controller.HelloController" />
beans>
8.启动tomcat
测试【这里项目的启动路径配置为空】
说明:
- 实现接口Controller来定义控制器是较老的方法,不建议使用!
- 实现接口Controller定义控制器这样用法的缺点:
- 一个控制器中只能有一个方法,如果需要有多个方法的话则需要定义多个Controller;
- 定义控制器的方式比较麻烦!!推荐使用注解开发控制器!
1.新建一个Module,添加web应用支持,跟之前上面操作一样。
2.pom.xml
中确定导入了Spring MVC, servlet, JSTL等依赖,这里已经在父依赖中导入了.
3.配置web.xml
注:
- web.xml的版本问题,要为4.0最新版!
- 注册DispatcherServlet
- 关联Spring的配置文件
- 启动级别为1
- 映射路径为/,不要用/*
web.xml
<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>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springmvc-servlet.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
4.resources
目录下配置Spring MVC绑定的Spring配置文件springmvc-servlet.xml
注:
- 让Spring IOC的注解生效
- 静态资源过滤:HTML,CSS,JS,图片,视频…
- MVC的注解驱动
- 配置视图解析器
springmvc-servlet.xml
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
https://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.carson.controller" />
<mvc:default-servlet-handler />
<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>
5.src
目录下编写相应的Controller作为Java控制类
HelloController.java
package com.carson.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
//@Controller注解代表注册Controller到IOC容器中,其方法返回的String参数对应一个视图名
//@Controller注解代表这个类会被Spring接管
@Controller
//在类上使用@RequestMapping注解书写映射路径
@RequestMapping("/HelloController")
public class HelloController {
//在方法上使用@RequestMapping注解书写映射路径
//真实的访问地址为:localhost:8080/web项目名/HelloController/hello
@RequestMapping("/hello")
//任意命名一个返回字符串类型的方法,其中Model类型参数可用来携带数据到视图中
public String test01(Model model){
//向Model类型的参数中添加 键值对数据,其可以在视图中取出来并渲染
model.addAttribute("msg","Hello Spring MVC Annotation!");
//返回的字符串就是逻辑视图名,默认是请求转发的形式,转发视图名才会被视图解析器拼接
return "hello";
}
}
注:
- @Controller注解是用于声明Spring类的实例是一个控制器,其为了让Spring IOC容器初始化时自动扫描到.其方法返回的字符串若对应一个逻辑视图名,则其会被视图解析器进行进一步解析,此时如果想方法返回的字符串不被视图解析器解析,需要在方法上加一个@ResponseBody注解;而如果使用@RestController注解,其方法返回的就仅仅是一个字符串,其不会被视图解析器进行进一步解析!!
- @RequestMapping是为了映射请求路径,即用于映射URL到控制器类或一个特定的处理程序方法。其可以加在类上或者方法上 ; 若用于类上,表示类中的所有响应请求的方法都是以该地址为父地址;即URL地址遵循
先指定类路径再指定方法路径
的原则; 上面例子中因为类和方法上都有映射,所以访问路径是先类后方法:/HelloController/hello- 方法中声明的Model类型的参数是为了把Controller中的数据带到视图中.
- 方法返回的String类型参数对应逻辑视图名称hello,其会被视图解析器加上配置文件中的前后缀从而形成完整视图路径: /WEB-INF/jsp/hello.jsp
6.创建视图层
在WEB-INF
目录下建立jsp
目录并在其目录下再建立测试用的hello.jsp
文件,视图都放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端无法直接访问!
hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
hello jsp
${msg}
7.启动tomcat
测试【这里项目的启动路径配置为SpringMVC_03_annotation
】
总结开发步骤:
web.xml
,注册DispatcherServlet
使用Spring MVC必须配置的三大件:
- HandlerMapping处理器映射器
- HandlerAdapter处理器适配器
- ViewResolver视图解析器
通过使用注解开发,我们只需要手动配置视图解析器即可,而处理器映射器和处理器适配器只需要开启注解驱动即可,从而省去相关xml的配置!!
注: 当返回JSON数据出现乱码时,可以在注解驱动中进行如下配置防止乱码!!
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
bean>
property>
bean>
mvc:message-converters>
mvc:annotation-driven>
Restful
就是一个资源定位及资源操作的风格
其不是标准也不是协议,只是一种风格.
基于这个风格设计的应用可以更简洁,更有层次,更易于实现缓存等机制.
传统方式: 主要通过GET/POST请求同时问号传递不同的参数来实现不同的效果!,如下示例:
http://127.0.0.1/item/query?id=1
【查询数据请求,GET请求,问号传递参数】http://127.0.0.1/item/save
【新增数据请求,POST请求】http://127.0.0.1/item/update
【更新数据请求,POST请求,问号传递参数】http://127.0.0.1/item/delete?id=1
【删除数据请求,GET请求,问号传递参数】使用Restful: 可以通过不同的请求方式来实现不同的效果!
如下示例,请求地址一样,但实现的效果可以不同!!
http://127.0.0.1/item/1
【查询数据请求,GET请求】http://127.0.0.1/item
【新增数据请求,POST请求】http://127.0.0.1/item
【更新数据请求,PUT请求】http://127.0.0.1/item/1
【删除数据请求,DELETE请求】RestfulController
@Controller
public class RestfulController {
}
@PathVariable
注解,让方法形参的值对应绑定到一个URL模板变量上若方法参数名称和需要绑定的uri中变量名称不一致时:
@Controller
public class RestfulController {
//映射访问路径, URL模板变量使用格式: {模板变量名}
@RequestMapping("/test/{a}/{b}")
public String index(@PathVariable("a") int p1, @PathVariable("b") int p2, Model model){
int result = p1+p2;
//Model类型参数向视图中传递数据(前端视图hello.jsp只写了展示msg中的数据)
model.addAttribute("msg","结果"+result);
//返回视图位置,默认是请求转发的形式,转发视图名才会被视图解析器拼接
return "hello";
}
}
若方法参数名称和需要绑定的uri中变量名称一致时,可以简写如下:
@Controller
public class RestfulController {
//映射访问路径, URL模板变量使用格式: {模板变量名}
@RequestMapping("/test/{p1}/{p2}")
public String index(@PathVariable int p1, @PathVariable int p2, Model model){
int result = p1+p2;
//Model类型参数向视图中传递数据(前端视图hello.jsp只写了展示msg中的数据)
model.addAttribute("msg","结果"+result);
//返回视图位置,默认是请求转发的形式,转发视图名才会被视图解析器拼接
return "hello";
}
}
tomcat
测试【这里项目发布路径为:SpringMVC_04_Restful
】使用路径变量的好处?
- 使映射路径变得更加简洁,更加具有层次。
- 获得参数更加方便。
- 通过方法形参设置路径变量的类型可以约束前端访问参数的类型,如果参数类型不一致,则访问不到对应的资源,如上所示,限制了传递的两个参数类型都为int类型。
我们可以约束请求的类型,从而收窄可以请求的范围;
@RequestMapping注解
中的method属性对应的是一个枚举数组类型RequestMethod
我们可以通过RequestMethod.xx
指定请求的类型,如:GET, POST, PUT, DELETE等.
测试案例如下:
//注意:@RequestMapping注解当有多个属性时,路径的设置可以通过value属性或者path属性来设置
//映射访问路径(限定请求类型为POST)
@RequestMapping(value="/hello",method = {RequestMethod.POST})
//等价于:@RequestMapping(path="/hello",method = {RequestMethod.POST})
public String index1(Model model){
model.addAttribute("msg","hello");
//返回视图位置,默认是请求转发的形式,转发视图名才会被视图解析器拼接
return "hello";
}
tomcat
地址栏输入映射地址进行测试,由于地址栏默认是GET请求,所以会报错//映射访问路径(限定请求类型为GET)
@RequestMapping(value="/hello",method = {RequestMethod.GET})
public String index1(Model model){
model.addAttribute("msg","hello");
//返回视图位置,默认是请求转发的形式,转发视图名才会被视图解析器拼接
return "hello";
}
@GetMapping注解
【直接限定请求类型只能为GET请求】
@GetMapping
等价于@RequestMapping(method=RequestMethod.GET)
@PostMapping注解
【直接限定请求类型只能为Post请求】
@PostMapping
等价于@RequestMapping(method=RequestMethod.POST)
@PutMapping注解
【直接限定请求类型只能为Put请求】
@PutMapping
等价于@RequestMapping(method=RequestMethod.PUT)
@DeleteMapping注解
【直接限定请求类型只能为Delete请求】
@DeleteMapping
等价于@RequestMapping(method=RequestMethod.DELETE)
测试案例如下:
//映射访问路径(限定请求类型为POST)
@PostMapping("/hello")
public String index2(Model model){
model.addAttribute("msg","hello");
//返回视图位置,默认是请求转发的形式,请求转发视图名才会被视图解析器拼接
return "hello";
}
tomcat
地址栏输入映射地址发送GET请求进行测试://映射访问路径(限定请求类型为GET)
@GetMapping("/hello")
public String index2(Model model){
model.addAttribute("msg","hello");
//返回视图位置,默认是请求转发的形式,转发视图名才会被视图解析器拼接
return "hello";
}
至于其它的限定请求类型的注解用法和上面一样,同样操作即可!!
通过设置ModelAndView
对象,根据view的名称viewName和视图解析器的解析跳转到指定的页面。
跳转的页面 = (视图解析器前缀) + viewName + (视图解析器后缀)
spring配置文件中配置的视图解析器
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
bean>
对应的Controller类
//这里需要实现Spring mvc中的Controller接口才能作为一个Controller
public class HelloController implements Controller {
//重写其中唯一的方法
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//实例化一个ModelAndView模型和视图的对象
ModelAndView mv = new ModelAndView();
//业务代码书写的位置
//这里示例只给其中添加数据,addObject()的参数是一个键值对
mv.addObject("msg","Hello Spring MVC!!");
//视图跳转
mv.setViewName("test");//对应/WEB-INF/html/test.html
//返回ModelAndView
return mv;
}
}
上面说过,这种方式是最老的方法,不建议使用!
由于Spring MVC
的核心是DispatcherServlet
,而其本质又是Servlet
,故可以通过设置
Servlet
的相关API,来实现结果跳转,且不需要视图解析器!
HttpServletResponse
向前端输出内容。HttpServletResponse
实现重定向。HttpServletRequest
实现请求转发。@Controller
public class HelloController2 {
//通过设置`HttpServletResponse`向前端输出内容
@RequestMapping("/result/t1")
public void test1(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.getWriter().write("Hello,Spring MVC By Servlet API!");
}
//通过设置`HttpServletResponse`实现重定向
@RequestMapping("result/t2")
public void test2(HttpServletRequest request, HttpServletResponse response) throws IOException{
response.sendRedirect("/index.jsp");
}
//通过设置`HttpServletRequest`实现请求转发
@RequestMapping("result/t3")
public void test3(HttpServletRequest request, HttpServletResponse response) throws Exception{
request.getRequestDispatcher("/WEB-INF/jsp/hello.jsp").forward(request,response);
}
}
虽然可以通过Servlet API实现结果跳转,但不建议这么使用!!
使用Spring MVC实现结果跳转,但是没有视图解析器的情况
测试前,需要将Spring配置文件
中的视图解析器部分
注释掉:
spring配置文件
<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
https://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="com.carson.controller" />
<mvc:default-servlet-handler />
<mvc:annotation-driven />
beans>
对应的Controller类
@Controller
public class HelloController3 {
@RequestMapping("result/t1")
public String test1(){
//请求转发
return "forward:/index.jsp";
}
@RequestMapping("result/t2")
public String test2(){
//重定向
return "redirect:/index.jsp";
}
}
上面的没有使用视图解析器的也不推荐使用!
使用Spring MVC实现结果跳转,有视图解析器的情况
spring配置文件中配置的视图解析器
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
bean>
对应的Controller类
@Controller
public class HelloController3 {
@RequestMapping("result/t1")
public String test1(){
//默认返回的字符串视图名,就是请求转发,,请求转发视图名才会被视图解析器拼接
return "test";// 对应拼接成/WEB-INF/jsp/test.jsp
}
@RequestMapping("result/t2")
public String test2(){
//重定向,虽然有视图解析器,但是不会被视图解析器拼接
return "redirect:/index.jsp";
//return "redirect:hello"; //hello为另一个请求
}
}
推荐使用Spring MVC有视图解析器的用法!!
提交数据:http://localhost:8080/hello?name=Carson
处理方法:
@Controller
public class HelloController4 {
@RequestMapping("/hello")
//前端传入的参数名为name和下面方法中的形参name是一样的
public String test(String name){
System.out.println("传入的参数值是:"+name);
return "hello";
}
}
提交数据: http://localhost:8080/hello?username=Carson
处理方法:
@Controller
public class HelloController4 {
@RequestMapping("/hello")
//前端传入的参数名为username和下面方法中的形参name是不一样的
//需要在方法形参中增加@RequestParam("前端传入参数名")进行说明
public String test(@RequestParam("username") String name){
System.out.println("传入的参数值是:"+name);
return "hello";
}
}
建议: 接收前端请求参数在方法形参中都添加一个@RequestParam()注解,增强代码可读性!
要求提交的表单域属性和对象的属性名一致,控制器中的方法的形参使用对象即可!
实体类
public class User {
private int id;
private String name;
private int age;
public User() {
}
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
http://localhost:8080/user?id=1&name=carson&age=20
@Controller
public class HelloController4 {
@RequestMapping("/user")
//要求提交的表单域属性和对象的属性名一致,否则匹配不到对象的属性
//控制器中的方法的形参使用对象即可!
public String test(User user){
System.out.println(user);
return "hello";
}
}
后台输出:
示例如下
:
//这里需要实现Spring mvc中的Controller接口才能作为一个Controller
//只要实现了Controller接口的类,说明这就是一个控制器了
public class HelloController implements Controller {
//重写其中唯一的方法
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//实例化一个ModelAndView模型和视图的对象
ModelAndView mv = new ModelAndView();
//业务代码书写的位置
//这里示例只给其中添加数据,addObject()的参数是一个键值对
mv.addObject("msg","Hello Spring MVC!!");
//视图跳转
mv.setViewName("test");//对应/WEB-INF/html/test.html
//返回ModelAndView
return mv;
}
}
示例如下:
@RequestMapping("/HelloController")
public class HelloController {
@RequestMapping("/hello")
//任意命名一个返回字符串类型的方法,其中Model类型参数可用来携带数据到视图中
public String test01(ModelMap modelMap){
//向Model类型的参数中添加 键值对数据,其可以在视图中取出来并渲染
modelMap.addAttribute("msg","Hello Spring MVC!");
//返回的字符串就是逻辑视图名,默认是请求转发的形式,转发视图名才会被视图解析器拼接
return "hello";
}
}
示例如下:
@RequestMapping("/HelloController")
public class HelloController {
@RequestMapping("/hello")
//任意命名一个返回字符串类型的方法,其中Model类型参数可用来携带数据到视图中
public String test01(Model model){
//向Model类型的参数中添加 键值对数据,其可以在视图中取出来并渲染
model.addAttribute("msg","Hello Spring!");
//返回的字符串就是逻辑视图名,默认是请求转发的形式,转发视图名才会被视图解析器拼接
return "hello";
}
}
简单对比:
- Model只有几个方法适合用于存储数据,简化了开发者对于Model对象的理解和操作!Mo
- ModelMap继承了LinkedHashMap,除了自身部分存储数据的方法,还继承了LinkHashMap的方法和特性。
- ModelAndView可以在存储数据的同时设置返回的逻辑视图,从而进行视图层的跳转。
当然更多的差异考虑的是性能和优化的问题,从而作出不同的选择!!
//Servlet中添加如下请求和响应编码设置
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=UTF-8");
package com.carson.filter;
import javax.servlet.*;
import java.io.IOException;
public class CharacterEncodingFilter implements Filter {
//初始化:当web服务器启动时,就自动初始化了,随时等待过滤对象的出现
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("CharacterEncodingFilter已经初始化");
}
/*
1:过滤器中的所有代码,在过滤特定请求的时候都会执行
2:必须写chain.foFilter()方法让过滤器继续前进
*/
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//转换request和response为HttpServlet类型
HttpServletRequest request = (HttpServletRequest) request;
HttpServletResponse response = (HttpServletResponse) response;
//解决请求参数的中文乱码问题
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=UTF-8");
//让我们的请求继续走,如果不写,程序到这里就会被拦截停止
//即这句代码表示放行
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("CharacterEncodingFilter执行后");
}
//销毁:当web服务器关闭的时候,会进行销毁
public void destroy() {
System.out.println("CharacterEncodingFilter已经销毁");
}
}
SpringMVC
给我们提供了一个过滤器,可以直接在web.xml
中进行相关配置即可使用!
web.xml
<filter>
<filter-name>encodingfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
注意:
有些极端情况下,这个过滤器对GET请求的支持效果不太好!!
所以要注意开发环境的所有编码设置最好都设置为UTF-8!!【特别是tomcat环境中】
这个通用乱码过滤器
是大神开发的其定义的编码过滤器,能够较好的解决get/post请求乱码的问题!
通用乱码过滤器
package com.carson.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
* 解决get和post请求 全部乱码的过滤器
*/
public class GenericEncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//处理response的字符编码
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");
// 转型为与协议相关对象
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 对request包装增强
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
//是否编码的标记
private boolean hasEncode;
//定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
public MyRequest(HttpServletRequest request) {
super(request);// super必须写
this.request = request;
}
// 对需要增强方法 进行覆盖
@Override
public Map getParameterMap() {
// 先获得请求方式
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
// post请求
try {
// 处理post乱码
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
// get请求
Map<String, String[]> parameterMap = request.getParameterMap();
if (!hasEncode) { // 确保get手动编码逻辑只运行一次
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
// 处理get乱码
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}
//取一个值
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
return values[0]; // 取回参数的第一个值
}
//取所有值
@Override
public String[] getParameterValues(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}
可以将上面的通用编码过滤器
添加到自己的项目文件中,作为万能编码过滤器使用!
然后再在web.xml
中配置这个过滤器和过滤路径即可。
乱码问题,平时需要多注意,尽可能在可以设置编码的地方都设置为UTF-8的编码格式!!
这阶段的ajax基于JQuery
和SpringMVC的注解实现
来演示使用Ajax
。
环境搭建:
web.xml
<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>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationConfig.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>encodingfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingfilter-name>
<url-pattern>/url-pattern>
filter-mapping>
web-app>
applicationConfig.xml
<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
https://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="com.carson.controller" />
<mvc:default-servlet-handler />
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
bean>
property>
bean>
mvc:message-converters>
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>
前端index.jsp文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$title>
<script src="${pageContext.request.contextPath}/static/js/jquery-3.6.0.js">script>
<script>
function a() {
$.post({
url:"${pageContext.request.contextPath}/t2",
data:{
name:$("#username").val()
},
success:function (data) {
alert(data);
}
})
}
script>
head>
<body>
<%--失去焦点时,发起ajax请求--%>
用户名:<input type="text" id="username" onblur="a()">
body>
html>
controller
@RestController //@RestController注解不会对返回的字符串经过视图解析器进行解析
public class AjaxController {
@RequestMapping("/t2")
public void test1(String name, HttpServletResponse response) throws IOException {
System.out.println("a1=>param:"+name);
if("carson".equals(name)){
response.getWriter().print("true");
}else{
response.getWriter().print("false");
}
}
}
效果:
前端test2.jsp文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>ajax test2title>
<script src="${pageContext.request.contextPath}/static/js/jquery-3.6.0.js">script>
<script>
$(function () {
$("#btn").click(function () {
/*简写形式:$.post(url,携带的param[可省略],success回调函数)*/
$.post("${pageContext.request.contextPath}/t3",function (data) {
//console.log(data);
let html = "";
for(let i=0;i < data.length;i++){
html += ""+""+data[i].name+" "+""+data[i].age+" "+""+data[i].sex+" "+" "
}
$("#content").html(html);
});
})
});
script>
head>
<body>
<input type="button" value="加载数据" id="btn">
<table>
<thead>
<tr>
<td>姓名:td>
<td>年龄:td>
<tD>性别:tD>
tr>
thead>
<tbody id="content">
<%--数据:来自后台--%>
tbody>
table>
body>
html>
controller
@RestController //@RestController注解不会对返回的字符串经过视图解析器进行解析
public class AjaxController {
@RequestMapping("/t3")
public List<User> test2(){
ArrayList<User> userList = new ArrayList<User>();
userList.add(new User("Carson1",1,"男"));
userList.add(new User("Carson2",2,"男"));
userList.add(new User("Amy",3,"女"));
return userList;
}
}
效果:
前端login.jsp文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
login test
用户名:
密码:
controller
@RestController //@RestController注解不会对返回的字符串经过视图解析器进行解析
public class AjaxController {
@RequestMapping("/t4")
public String test3(String username,String pwd){
System.out.println("username是:"+username);
System.out.println("pwd是:"+pwd);
String msg = "";
if(username!=null){
//正常这些数据是在数据库中查,这里只是demo
if("admin".equals(username)){
msg = "ok";
}else if("".equals(username)){
msg = "用户名不能为空!";
}else{
msg = "用户名有误!";
}
}
if(pwd!=null){
//正常这些数据是在数据库中查,这里只是demo
if("123456".equals(pwd)){
msg = "ok";
}else if("".equals(pwd)){
msg = "密码不能为空!";
}else{
msg = "密码有误!";
}
}
return msg;
}
}
效果:
什么是拦截器?
SpringMVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理,开发者可以自己定义一些拦截器来实现特定的功能!!
过滤器和拦截器的区别?
拦截器是AOP面向切面编程
思想的具体应用。
url-pattern中配置了/*
以后,可以对所有要访问的资源进行拦截。web.xml
中进行配置。spring
的配置文件中进行配置。想要自定义拦截器,必须实现HandlerInterceptor接口!
web.xml
web.xml
<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>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationConfig.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>encodingfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingfilter-name>
<url-pattern>/url-pattern>
filter-mapping>
web-app>
spring配置文件
applicationConfig.xml
<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
https://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="com.carson.controller" />
<mvc:default-servlet-handler />
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
bean>
property>
bean>
mvc:message-converters>
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>
HandlerInterceptor
接口MyInterceptor.java
package com.carson.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//拦截器是AOP面向切面编程思想的具体体现
public class MyInterceptor implements HandlerInterceptor {
//这个方法在请求处理的方法执行之前执行
//其如果返回true则执行下一个拦截器(即放行请求)
//其如果返回false就不执行下一个拦截器(即不放行请求)
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("------------请求处理前------------");
return true;//返回true,放行请求
}
//这个方法在请求处理方法执行之后执行
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("----------请求处理后--------------");
}
//这个方法在dispatcherServlet处理后执行,主要做清理工作.
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("-------------DispatcherServlet处理后,做清理工作--------------");
}
}
spring配置文件中
增加关于拦截器的配置applicationConfig.xml
<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
https://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="com.carson.controller" />
<mvc:default-servlet-handler />
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
bean>
property>
bean>
mvc:message-converters>
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>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.carson.interceptor.MyInterceptor"/>
mvc:interceptor>
mvc:interceptors>
beans>
Controller
//测试拦截器的Controller
@RestController //使用@RestController注解不对方法中返回的字符串经过视图解析器解析
public class HelloController {
@RequestMapping("/interceptor")
public String test1(){
System.out.println("控制器中的方法执行了");
return "OK";
}
}
tomcat
测试,测试结果如下:实现思路
1、前端有一个登陆页面,登陆页面有一提交表单的动作。
2、表单提交后需要在controller中处理。判断用户名密码是否正确。如果正确,向session中写入用户信息。返回登陆成功。
3、拦截器拦截用户请求,判断用户是否登陆。如果用户已经登陆。放行, 如果用户未登陆,跳转到登陆页面。
测试
登陆页面login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>login pagetitle>
head>
<body>
<h1>登录页面h1>
<hr>
<form action="${pageContext.request.contextPath}/user/Login">
用户名:<input type="text" name="username"> <br>
密码:<input type="password" name="pwd"> <br>
<input type="submit" value="提交">
form>
body>
html>
登陆成功页面success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>login successtitle>
head>
<body>
<h1>登录成功页面h1>
<hr>
${user}登陆成功,欢迎你!
<a href="${pageContext.request.contextPath}/user/Logout">注销a>
body>
html>
首页index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页title>
head>
<body>
<h1>首页h1>
<hr>
<%--登录--%>
<a href="${pageContext.request.contextPath}/user/toLogin">登录a>
<a href="${pageContext.request.contextPath}/user/toSuccess">成功页面a>
body>
html>
Controller
处理请求UserController.java
package com.carson.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
//处理表单请求的Controller
@Controller
@RequestMapping("/user")
public class UserController {
//跳转到登陆页面
@RequestMapping("/toLogin")
public String toLogin() throws Exception {
return "login";//返回登陆页面视图
}
//跳转到成功页面
@RequestMapping("/toSuccess")
public String toSuccess() throws Exception {
return "success";//返回登陆成功页面视图
}
//登陆提交
@RequestMapping("/Login")
public String login(HttpSession session, String username, String pwd) throws Exception {
// 向session记录用户身份信息
System.out.println("接收前端的username为:"+username);
session.setAttribute("user", username);
return "success";//返回登陆成功页面视图
}
//退出登陆
@RequestMapping("Logout")
public String logout(HttpSession session) throws Exception {
// session 过期
session.invalidate();
return "login";//返回登陆页面视图
}
}
LoginInterceptor.java
package com.carson.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
// 如果是登陆页面则放行
System.out.println("uri: " + request.getRequestURI());
if (request.getRequestURI().contains("Login")) {
return true;//返回true,请求放行
}
//获取session信息
HttpSession session = request.getSession();
// 从session信息中取出数据,如果用户已登陆也放行
if(session.getAttribute("user") != null) {
return true;//返回true,请求放行
}
// 用户没有登陆则跳转到登陆页面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false; //返回false,请求不放行
}
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
web.xml
<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>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationConfig.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>encodingfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingfilter-name>
<url-pattern>/url-pattern>
filter-mapping>
web-app>
Spring的配置文件
,并在其中注册拦截器
applicationConfig.xml
<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
https://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="com.carson.controller" />
<mvc:default-servlet-handler />
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
bean>
property>
bean>
mvc:message-converters>
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>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.carson.interceptor.LoginInterceptor"/>
mvc:interceptor>
mvc:interceptors>
beans>
tomcat
测试文件上传是项目开发中最常见的功能,springMVC
可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver
,因此默认情况下其不能处理文件上传工作。
如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver
。
前端表单要求:
为了能上传文件,必须将表单的提交方式method设置为POST,并将enctype设置为multipart/form-data
。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据流的方式发送给服务器;
enctype的各个属性说明
application/x-www-form-urlencoded
:表单默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。text/plain
: 除了把空格转换为 “+” 号外,其他字符都不做编码处理,适用直接通过表单发送邮件。multipart/form-data
: 这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。支持文件上传的表单示例:
<form action="" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit">
form>
如上所示, 一旦设置了enctype为multipart/form-data
,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则需要在服务器端解析原始的HTTP进行响应。
SpringMVC处理文件上传的优势
虽然Servlet中可以完成文件上传,但实现复杂!而Spring MVC则提供了更简单的封装。
Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现
的。
Spring MVC使用Apache Commons FileUpload技术
实现了一个MultipartResolver实现类:CommonsMultipartResolver。
因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件
。
文件上传测试
pom.xml
<dependencies>
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.4version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.0.1version>
dependency>
dependencies>
springMVC配置文件中
配置bean:multipartResolver
注意: 配置的这个bean的id必须为:multipartResolver , 否则上传文件会报措!
applicationConfig.xml
<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
https://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="com.carson.controller" />
<mvc:default-servlet-handler />
<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>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"/>
<property name="maxUploadSize" value="10485760"/>
<property name="maxInMemorySize" value="40960"/>
bean>
beans>
web.xml
<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>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationConfig.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>encodingfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingfilter-name>
<url-pattern>/url-pattern>
filter-mapping>
web-app>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
upload test
Controller
:【有两种写法】需要用到一个:CommonsMultipartFile类型参数
CommonsMultipartFile类型参数的 常用方法:
String getOriginalFilename()
:获取上传文件的原名InputStream getInputStream()
:获取文件流void transferTo(File dest)
:将上传文件保存到一个目录文件中5.1 第一种保存上传文件的方式
//文件上传处理的Controller
@Controller
public class UploadController {
//@RequestParam("file") 将表单name=file控件得到的文件封装成CommonsMultipartFile 对象
//批量上传CommonsMultipartFile则为数组即可
@RequestMapping("/upload")
public String fileUpload(@RequestParam("file") CommonsMultipartFile file , HttpServletRequest request) throws IOException {
//获取文件名 : file.getOriginalFilename();
String uploadFileName = file.getOriginalFilename();
//如果文件名为空,直接回到首页!
if ("".equals(uploadFileName)){
return "redirect:/index.jsp";
}
System.out.println("上传文件名是: "+uploadFileName);
//上传路径保存设置
String path = request.getServletContext().getRealPath("/upload");
//如果路径不存在,创建一个路径对应下的新的文件夹
File realPath = new File(path);
if (!realPath.exists()){
realPath.mkdir();
}
System.out.println("上传文件的保存地址:"+realPath);
InputStream is = file.getInputStream(); //获取文件输入流对象
OutputStream os = new FileOutputStream(new File(realPath,uploadFileName)); //设置文件输出流对象
//读取写出
int len=0;
byte[] buffer = new byte[1024];//暂存的字节数组
while ((len=is.read(buffer))!=-1){
os.write(buffer,0,len);
os.flush();
}
os.close();
is.close();
//回到首页
return "redirect:/index.jsp";
}
}
第二种保存上传文件的方式,采用file.Transto 来保存上传的文件
/*
* 采用file.Transto 来保存上传的文件
*/
@RequestMapping("/upload2")
public String fileUpload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
//上传路径保存设置
String path = request.getServletContext().getRealPath("/upload");
//如果路径不存在,创建一个路径对应下的新的文件夹
File realPath = new File(path);
if (!realPath.exists()){
realPath.mkdir();
}
//上传文件地址
System.out.println("上传文件保存地址:"+realPath);
//通过CommonsMultipartFile的方法直接写文件
file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));
return "redirect:/index.jsp";
}
文件下载实现步骤:
1、设置response 响应头
2、读取文件 – InputStream
3、写出文件 – OutputStream
4、执行操作
5、关闭流 (先开后关)
前端页面:
<a href="/download">测试下载的文件,点击下载a>
测试的待下载的文件:【已手动放入输出的war包的对应目录中】
后台的控制器Controller
:
//文件下载
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{
//要下载的资源地址
String path = request.getServletContext().getRealPath("/upload");
String fileName = "resource.txt";
//1、设置response 响应头
response.reset(); //设置页面不缓存,清空buffer
response.setCharacterEncoding("UTF-8"); //字符编码
response.setContentType("multipart/form-data"); //二进制传输数据
//设置响应头
response.setHeader("Content-Disposition",
"attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));
File file = new File(path,fileName);
//2、 读取文件--输入流
InputStream input=new FileInputStream(file);
//3、 写出文件--输出流
OutputStream out = response.getOutputStream();
byte[] buff =new byte[1024];
int index=0;
//4、执行 写出操作
while((index= input.read(buff))!= -1){
out.write(buff, 0, index);
out.flush();
}
out.close();
input.close();
return null;
}
欢迎关注个人公众号,回复“SpringMVC”,获取本文所有的完整测试代码!