文章目录
SpringMvc
Spring MVC 是目前主流的实现 MVC 设计模式的企业级开发框架,Spring 框架的一个子模块,无需整合,开发起来更加便捷。
1.SpringMvc核心思想
1.1 Spring MVC 的核心组件
- DispatcherServlet:前置控制器,是整个流程控制的核心,控制其他组件的执行,进行统一调度,降低组件之间的耦合性,相当于总指挥。
- Handler:处理器,完成具体的业务逻辑,相当于 Servlet 或 Action。
- HandlerMapping:DispatcherServlet 接收到请求之后,通过 HandlerMapping 将不同的请求映射到不同的 Handler。
- HandlerInterceptor:处理器拦截器,是一个接口,如果需要完成一些拦截处理,可以实现该接口。
- HandlerExecutionChain:处理器执行链,包括两部分内容:Handler 和 HandlerInterceptor(系统会有一个默认的 HandlerInterceptor,如果需要额外设置拦截,可以添加拦截器)。
- HandlerAdapter:处理器适配器,Handler 执行业务方法之前,需要进行一系列的操作,包括表单数据的验证、数据类型的转换、将表单数据封装到 JavaBean 等,这些操作都是由 HandlerApater 来完成,开发者只需将注意力集中业务逻辑的处理上,DispatcherServlet 通过 HandlerAdapter 执行不同的 Handler。
- ModelAndView:装载了模型数据和视图信息,作为 Handler 的处理结果,返回给 DispatcherServlet。
- ViewResolver:视图解析器,DispatcheServlet 通过它将逻辑视图解析为物理视图,最终将渲染结果响应给客户端。
1.2Spring MVC 的工作流程
- 客户端请求被 DisptacherServlet 接收。
- 根据 HandlerMapping 映射到 Handler。
- 生成 Handler 和 HandlerInterceptor。
- Handler 和 HandlerInterceptor 以 HandlerExecutionChain 的形式一并返回给 DisptacherServlet。
- DispatcherServlet 通过 HandlerAdapter 调用 Handler 的方法完成业务逻辑处理。
- Handler 返回一个 ModelAndView 给 DispatcherServlet。
- DispatcherServlet 将获取的 ModelAndView 对象传给 ViewResolver 视图解析器,将逻辑视图解析为物理视图 View。
- ViewResovler 返回一个 View 给 DispatcherServlet。
- DispatcherServlet 根据 View 进行视图渲染(将模型数据 Model 填充到视图 View 中)。
- DispatcherServlet 将渲染后的结果响应给客户端。
2.创建一个SpringMvc工程
- 导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>
- 配置springmvc的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.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<!--上面还是声明头,黏贴即可-->
<context:component-scan base-package="com.Lei"></context:component-scan>
<!--自动扫描器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
- 添加自动扫描器
- 添加视图解析器
InternalResourceViewResolver
将物理视图转换,property添加的是解析后得到的prefix是前缀,suffix是后缀
- 编写web.xml注册springmvc的servlet
<!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>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--选择DispatcherServlet核心-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- init-param表示初始化,主要是为servlet初始化时,进行一些配置时候用的
- 用classpath:springmvc.xml指向springmvc的配置文件
- 编写测试主程序
@Controller//自动扫描的时候可以被扫描到,并具有控制的作用
@RequestMapping("/hello")//映射的地址
public class HelloHandler {
@RequestMapping(value = "index",params = {"name","id"})//params是指必要的参数,不用像以前getParameter获取
public String index(String name,int id){
System.out.println(name);
System.out.println(id);
return "index";
}
}
3.Spring MVC 注解
-
@RequestMapping
-
Spring MVC 通过 @RequestMapping 注解将 URL 请求与业务方法进行映射,在 Handler 的类定义处以及方法定义处都可以添加 @RequestMapping ,在类定义处添加,相当于客户端多了一层访问路径。
-
value:指定 URL 请求的实际地址,是 @RequestMapping 的默认值。
-
@RequestMapping("/index") //等价于 @RequestMapping(value="/index")
-
-
method:指定请求的 method 类型,GET、POST、PUT、DELET。
-
@RequestMapping(value = "/index",method = RequestMethod.GET) //上述代码表示 index 方法只能接收 GET 请求。
-
-
params:指定请求中必须包含某些参数,否则无法调用该方法。
-
@RequestMapping(value = "/index",method = RequestMethod.GET,params = {"name","id=10"}) //上述代码表示请求中必须包含 name 和 id 两个参数,同时 id 的值必须是 10。
-
-
-
@Controller
- @Controller 在类定义处添加,将该类交个 IoC 容器来管理(结合 springmvc.xml 的自动扫描配置使用),同时使其成为一个控制器,可以接
-
映射 Cookie
-
Spring MVC 通过映射可以直接在业务方法中获取 Cookie 的值。收客户端请求。
-
@RequestMapping("/cookie") public String cookie(@CookieValue(value = "JSESSIONID") String sessionId){ System.out.println(sessionId); return "index"; }
-
-
-
使用 JavaBean 绑定参数
- Spring MVC 会根据请求参数名和 JavaBean 属性名进行自动匹配,自动为对象填充属性值,同时支持及联属性。
-
无级联
-
//实体类 public class User { private long id; private String name; }
-
<%-- Created by IntelliJ IDEA. User: Leilei Date: 2022/7/16 Time: 15:40 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <form action="/save" method="post"> 用户id:<input type="text" name="id"><br> 用户名:<input type="text"name="name"><br> <input type="submit" value="提交"> </form> </body> <%--提交表单--%> </html>
-
//hanlder的操作 @Controller public class HelloHandler { @RequestMapping("/save") public String save(User user){ System.out.println(user); return "index"; } }
-
注:action要与@RequestMapping(“/save”)对应
-
-
级联一个Address类
-
//级联类 public class Address { private String value; }
-
//新增address字段 public class User { private long id; private String name; private Address address; }
-
<%-- Created by IntelliJ IDEA. User: Leilei Date: 2022/7/16 Time: 15:40 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <%--提交表单--%> <form action="/save" method="post"> 用户id:<input type="text" name="id"><br> 用户名:<input type="text"name="name"><br> 地址: <input type="text" name="address.value"><br> <%--新增地址栏--%> <input type="submit" value="提交"> </form> </body> </html>
-
注:这里的提交是用字段名加上对应的类中的属性值来覆盖,springmvc会自动匹配覆盖
-
-
JSP 页面的转发和重定向:
-
Spring MVC 默认是以转发的形式响应 JSP。
-
转发
-
@RequestMapping("/forward") public String forward(){ return "forward:/index.jsp"; // return "index"; }
-
-
重定向
-
@RequestMapping("/redirect") public String redirect(){ return "redirect:/index.jsp"; }
-
-
4.Spring MVC 数据绑定
数据绑定:在后端的业务方法中直接获取客户端 HTTP 请求中的参数,将请求参数映射到业务方法的形参中,Spring MVC 中数据绑定的工作是由 HandlerAdapter 来完成的。
4.1基本数据类型
@RequestMapping("/baseType")
@ResponseBody
public String baseType(int id){
return id+"";
}
@ResponseBody 表示 Spring MVC 会直接将业务方法的返回值响应给客户端,如果不加 @ResponseBody 注解,Spring MVC 会将业务方法的放回值传递给 DispatcherServlet,再由 DisptacherServlet 调用 ViewResolver 对返回值进行解析,映射到一个 JSP 资源。
4.2包装类
@RequestMapping("/packageType")
@ResponseBody
public String packageType(@RequestParam(value = "num",required = false,defaultValue = "0") Integer id){
return id+"";
}
包装类可以接收 null,当 HTTP 请求没有参数时,使用包装类定义形参的数据类型,程序不会抛出异常。
@RequestParam
value = “num”:将 HTTP 请求中名为 num 的参数赋给形参 id。
requried:设置 num 是否为必填项,true 表示必填,false 表示非必填,可省略。
defaultValue = “0”:如果 HTTP 请求中没有 num 参数,默认值为0.
4.3数组
@RestController
@RequestMapping("/data")
public class DataBindHandler {
@RequestMapping("/array")
public String array(String[] name){
String str = Arrays.toString(name);
return str;
}
}
@RestController 表示该控制器会直接将业务方法的返回值响应给客户端,不进行视图解析。
@Controller 表示该控制器的每一个业务方法的返回值都会交给视图解析器进行解析,如果只需要将数据响应给客户端,而不需要进行视图解析,则需要在对应的业务方法定义处添加 @ResponseBody。
4.4List
Spring MVC 不支持 List 类型的直接转换,需要对 List 集合进行包装。
集合封装类
@Data
public class UserList {
private List<User> users;
}
JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/list" method="post">
用户1编号:<input type="text" name="users[0].id"/><br/>
用户1名称:<input type="text" name="users[0].name"/><br/>
用户2编号:<input type="text" name="users[1].id"/><br/>
用户2名称:<input type="text" name="users[1].name"/><br/>
用户3编号:<input type="text" name="users[2].id"/><br/>
用户3名称:<input type="text" name="users[2].name"/><br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
业务方法
@RequestMapping("/list")
public String list(UserList userList){
StringBuffer str = new StringBuffer();
for(User user:userList.getUsers()){
str.append(user);
}
return str.toString();
}
4.5Map
自定义封装类
@Data
public class UserMap {
private Map<String,User> users;
}
业务方法
@RequestMapping("/map")
public String map(UserMap userMap){
StringBuffer str = new StringBuffer();
for(String key:userMap.getUsers().keySet()){
User user = userMap.getUsers().get(key);
str.append(user);
}
return str.toString();
}
JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/data/map" method="post">
用户1编号:<input type="text" name="users['a'].id"/><br/>
用户1名称:<input type="text" name="users['a'].name"/><br/>
用户2编号:<input type="text" name="users['b'].id"/><br/>
用户2名称:<input type="text" name="users['b'].name"/><br/>
用户3编号:<input type="text" name="users['c'].id"/><br/>
用户3名称:<input type="text" name="users['c'].name"/><br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
4.6JSON
客户端发生 JSON 格式的数据,直接通过 Spring MVC 绑定到业务方法的形参中。
处理 Spring MVC 无法加载静态资源,在 web.xml 中添加配置即可。
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript">
$(function(){
var user = {
"id":1,
"name":"张三"
};
$.ajax({
url:"/json",
data:JSON.stringify(user),
type:"POST",
contentType:"application/json;charset=UTF-8",
dataType:"JSON",
success:function(data){
alert(data.id+"---"+data.name);
}
})
});
</script>
</head>
<body>
</body>
</html>
业务方法
@RequestMapping("/json")
public User json(@RequestBody User user){
System.out.println(user);
user.setId(6);
user.setName("老六");
return user;
}
Spring MVC 中的 JSON 和 JavaBean 的转换需要借助于 fastjson,pom.xml 引入相关依赖。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.32</version>
</dependency>
springmvc.xml 添加 fastjson 配置。
<mvc:annotation-driven>
<!-- 消息转换器 -->
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/html;charset=UTF-8"></property>
</bean>
<!-- 配置fastjson -->
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter4"></bean>
</mvc:message-converters>
</mvc:annotation-driven>
5.Spring MVC 模型数据解析
JSP 四大作用域对应的内置对象:pageContext、request、session、application。
模型数据的绑定是由 ViewResolver 来完成的,实际开发中,我们需要先添加模型数据,再交给 ViewResolver 来绑定。
Spring MVC 提供了以下几种方式添加模型数据:
-
Map
-
Model
-
ModelAndView
-
@SessionAttribute
-
@ModelAttribute
上面的都是request层的,通过jsp页面就能发现
5.1Map
Jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${requestScope.user}
</body>
</html>
Controller.java
@Controller
@RequestMapping("view")
public class ControllerDemo {
@RequestMapping("/map")
public String map(Map<String, User> map){
User user = new User();
user.setId(6);
user.setName("老六");
map.put("user",user);
return "view";
}
}
map模型解析,就是将数据手动添加到map里,DisPatcher Servlet会自己解析map
5.2Model
jsp页面是一样的,只需要导入org.springframework.ui.Model
来进行添加model即可
@RequestMapping("/model")
public String model(Model model){
User user = new User();
user.setId(6);
user.setName("老六");
model.addAttribute("user",user);
return "view";
}
5.3ModelandView(重要)
jsp还是一样
import org.springframework.web.servlet.ModelAndView;//需要导入
@RequestMapping("/modelandview")
public ModelAndView modelAndView(){
User user = new User();
user.setId(6);
user.setName("老六");
ModelAndView modelAndView = new ModelAndView("view");
modelAndView.addObject("user",user);
return modelAndView;
}
就是简单的将view视图和model结合起来,用的相对较多,而且有许多中不同的重载方法,这里我只写一种我用的最习惯的;
5.4HttpServletRequest
jsp还是一样
@RequestMapping("/request")
public String request(HttpServletRequest request) {
User user = new User();
user.setId(6);
user.setName("老六");
request.setAttribute("user", user);
return "view";
}
直接通过原生的request方法传递对象
5.5ModelAttribute
定义一个方法,该方法专门用来返回要填充到模型数据中的对象。
@ModelAttribute
public User getUser(){
User user = new User();
user.setId(6);
user.setName("老六");
return user;
}
//这里的User创建也有很多不同的方法,如通过手动创建map,将对象存储到map里亦或是创建一个model,在model里setaddAttribute,记住最简单也是最实用的即可
@RequestMapping("/modelattribute")
public String modelAttribute(){
return "view";
}
下面就是基于session的对象的绑定了
5.6原生session的Api
这里的作用域是session,所以需要改动jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${sessionScope.user}
</body>
</html>
@RequestMapping("/session")
public String session(HttpSession session){
User user = new User();
user.setId(6);
user.setName("老六");
session.setAttribute("user",user);
return "view";
}
5.7SessionAttribute注释
//方式一
@SessionAttributes(value = {"user","address"})
public class ControllerDemo {
}
//方式二
@SessionAttributes(types = {User.class,Address.class})
public class ControllerDemo {
}
对于 ControllerDemo中的所有业务方法,只要向 request 中添加了数据类型是 User 、Address 的对象时,Spring MVC 会自动将该数据添加到 session 中,保存 key 不变。
将模型数据绑定到 application 对象
5.8application
@RequestMapping("/application")
public String application(HttpServletRequest request){
ServletContext application = request.getServletContext();
User user = new User();
user.setId(6);
user.setName("老六");
application.setAttribute("user",user);
return "view";
}
6.SpringMVC自定义数据转换器
数据转换器是指将客户端 HTTP 请求中的参数转换为业务方法中定义的形参,自定义表示开发者可以自主设计转换的方式,HandlerApdter 已经提供了通用的转换,String 转 int,String 转 double,表单数据的封装等,但是在特殊的业务场景下,HandlerAdapter 无法进行转换,就需要开发者自定义转换器。
6.1String->Date
- 首先编写实现converter接口进行自定义,默认都是object,如果想改变需要在泛型里指明
//导入的包是import org.springframework.core.convert.converter.Converter;
public class DateConverter implements Converter<String, Date> {
private String pattern;
public DateConverter(String pattern){
this.pattern =pattern;
}
@Override
public Date convert(String s) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
try {
return simpleDateFormat.parse(s);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
- 配置springmvc.xml
<bean id="converterService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters" >
<list>
<bean class="com.Lei.Converter.DateConverter">
<constructor-arg type="java.lang.String" value="yyyy-MM-dd"></constructor-arg>
</bean>
</list>
</property>
</bean>
添加这个bean,并通过是有参构造来传入pattern的形式
<mvc:annotation-driven conversion-service="converterService">
在注册消息转换头这里需要加上配置的id
- 编写jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/converter/date" method="post">
请输入日期<input type="text" name="date">(yyyy-MM-dd)
<input type="submit" value="提交">
</form>
</body>
</html>
- DisPatcherServlet解析
@RestController//这里我们用restcontroller不进行视图解析
@RequestMapping("/converter")
public class ConvertService {
@RequestMapping("/date")
public String date(Date date){
return String.valueOf(date);
}
}
6.2String->Student
- student实体类
@Data
public class Student {
private long id;
private String name;
private int age;
}
- 对应的jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/converter/student" method="post">
请输入学生信息:<input type="text" ame="student">
<input type="submit" value="提交">
</form>
</body>
</html>
- converter层获取对象
public class StudentConverter implements Converter<String, Student> {
@Override
public Student convert(String s) {
String[] args = s.split("-");
Student student = new Student();
student.setId(Long.parseLong(args[0]));
student.setName(args[1]);
student.setAge(Integer.parseInt(args[2]));
return student;
}
}
- Dispatcher Servlet层的converter
@RequestMapping("student")
public String student(Student student){
return student.toString();
}
- 配置springmvc.xml
<bean id="converterService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.Lei.Converter.DateConverter">
<constructor-arg type="java.lang.String" value="yyyy-MM-dd"></constructor-arg>
</bean>
<bean class="com.Lei.Converter.StudentConverter"></bean>
</list>
</property>
</bean>
7.REST风
REST:Representational State Transfer,资源表现层状态转换,是目前比较主流的一种互联网软件架构,它结构清晰、标准规范、易于理解、便于扩展。
- 资源(Resource)
网络上的一个实体,或者说网络中存在的一个具体信息,一段文本、一张图片、一首歌曲、一段视频等等,总之就是一个具体的存在。可以用一个 URI(统一资源定位符)指向它,每个资源都有对应的一个特定的 URI,要获取该资源时,只需要访问对应的 URI 即可。
- 表现层(Representation)
资源具体呈现出来的形式,比如文本可以用 txt 格式表示,也可以用 HTML、XML、JSON等格式来表示。
- 状态转换(State Transfer)
客户端如果希望操作服务器中的某个资源,就需要通过某种方式让服务端发生状态转换,而这种转换是建立在表现层之上的,所有叫做"表现层状态转换"。
7.1特点
- URL 更加简洁。
- 有利于不同系统之间的资源共享,只需要遵守一定的规范,不需要进行其他配置即可实现资源共享。
7.2具体用法
REST 具体操作就是 HTTP 协议中四个表示操作方式的动词分别对应 CRUD 基本操作。
GET 用来表示获取资源。
POST 用来表示新建资源。
PUT 用来表示修改资源。
DELETE 用来表示删除资源。
学生类crud编写
- 编写接口
public interface StudentRepository {
public Collection<Student> findAll();
public Student findById(long id);
public void saveOrUpdate(Student student);
public void deleteById(long id);
}
- 实现类,并添加Repository注释作用于持久层,可以被调用
@Repository
public class StudentRepositoryImpl implements StudentRepository {
private static Map<Long,Student> studentMap;
static {
studentMap =new HashMap<>();
studentMap.put(1L,new Student(1L,"张三",22));
studentMap.put(2L,new Student(2L,"李四",23));
studentMap.put(3L,new Student(3L,"王五",24));
}
@Override
public Collection<Student> findAll() {
return studentMap.values();
}
@Override
public Student findById(long id) {
return studentMap.get(id);
}
@Override
public void saveOrUpdate(Student student) {
studentMap.put(student.getId(), student);
}
@Override
public void deleteById(long id) {
studentMap.remove(id);
}
}
- 编写DispatcherServlet层的Handler
@RestController
@RequestMapping("/rest")
public class RestHandler {
@Autowired//自动对接口装载
private StudentRepository studentRepository;
@RequestMapping("/findAll")
public Collection<Student> findAll() {
return studentRepository.findAll();
}
@RequestMapping("/findById/{id}")
public Student findById(@PathVariable("id") long id) {
return studentRepository.findById(id);
}
@RequestMapping("/save")
public void save(@RequestBody Student student) {
studentRepository.saveOrUpdate(student);
}
@RequestMapping("/update")
public void update(@RequestBody Student student) {
studentRepository.saveOrUpdate(student);
}
@RequestMapping("/deleteById/{id}")
public void deleteById(@PathVariable("id") long id) {
studentRepository.deleteById(id);
}
}
8.文件上传下载
8.1单文件的上传
底层是使用 Apache fileupload 组件完成上传,Spring MVC 对这种方式进行了封装。
这里就以一张图片为例
- 导入依赖
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
- 前端jsp上传页面
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/file/upload" enctype="multipart/form-data" method="post">
<input type="file" name="img"> <br>
<input type="submit" value="上传">
</form>
<img src="${path}">
<%--这里直接读取查看是否上传成功--%>
</body>
</html>
- DispatcherServlet层的Handler
@Controller
@RequestMapping("/file")
public class fileHandler {
@RequestMapping("/upload")
public String upload(MultipartFile img, HttpServletRequest request) throws IOException {
String filename = img.getOriginalFilename();
String path = request.getServletContext().getRealPath("file");
File file = new File(path,filename);
img.transferTo(file);
String newPath ="/file/"+filename;
System.out.println(newPath);
request.getSession().setAttribute("path", newPath);//加入到session中从前端页面直接读取查看是否上传成功
return "upload";
}
}
主要就是一个封装了的transferTo方法,上传成功后还是回到本页面
- springmvc.xml里配置上传组件
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
- 记得将图片格式的文件取消过滤,通过web.xml配置
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
- 注意事项
1、input 的 type 设置为 file。
2、form 的 method 设置为 post(get 请求只能将文件名传给服务器)
3、from 的 enctype 设置为 multipart-form-data(如果不设置只能将文件名传给服务器)
8.2多文件上传
与单文件类似,其实就是将上传的文件加入到数组里,通过数组遍历取出
- 导入依赖
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
- jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/file/uploads" method="post" enctype="multipart/form-data">
file1:<input type="file" name="imgs"><br/>
file2:<input type="file" name="imgs"><br/>
file3:<input type="file" name="imgs"><br/>
<input type="submit" value="上传">
</form>'
<c:forEach items="${paths}" var="path">
<img src="${path}">
</c:forEach>
</body>
</html>
这里通过jstl库里的foreach遍历,uri="http://java.sun.com/jsp/jstl/core"
导入的库
将图片都以imgs上传
- handler
@RequestMapping("/uploads")
public String uploads(MultipartFile[] imgs, HttpServletRequest request) throws IOException {
List<String> files = new ArrayList<>();
for (MultipartFile img : imgs) {
String filename = img.getOriginalFilename();
String path = request.getServletContext().getRealPath("file");
File file = new File(path, filename);
img.transferTo(file);
files.add("/file/" + filename);
}
request.getSession().setAttribute("paths", files);
return "uploads";
}
MultipartFile 工具类变成了数组,每次上传图片的时候把他们都存到files集合里,方便一次性添加,不然每次上传一个图片都需要单个的添加,主要的还是被封装了的transferto方法
8.文件下载
- jsp前端页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="/file/download/1">1</a>
</body>
</html>
就是通过a标签的herf连接,这里采用的是rest风格
- handler
@RequestMapping("/download/{name}")
public void download(@PathVariable String name, HttpServletRequest request, HttpServletResponse response) throws IOException {
if (name != null) {
name += ".jpg";
String path = request.getServletContext().getRealPath("file");
File file = new File(path, name);
OutputStream outputStream = null;
if (file.exists()) {
response.setContentType("application/force-download");
response.setHeader("Content-Disposition","attachment;filename="+file);
outputStream = response.getOutputStream();
outputStream.write(FileUtils.readFileToByteArray(file));
outputStream.flush();
if (outputStream!=null)outputStream.close();
}
}
}
response.setContentType("application/force-download"); response.setHeader("Content-Disposition","attachment;filename="+file);
这里是response的头设置,也就是强制下载,这才能下载文件,否则就会把读入的直接变成二进制码返回到浏览器
outputStream.write(FileUtils.readFileToByteArray(file));
最主要的封装方法,将读入的流直接写到file文件里
9.SpringMvc标签库
9.1测试样例
- handler
@RequestMapping("/get")
public ModelAndView get() {
ModelAndView modelAndView = new ModelAndView("show");
Student student = new Student(1L, "张三", 18);
modelAndView.addObject("student", student);
return modelAndView;
}
- jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@page isELIgnored="false" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>学生信息</h1>
<form:form modelAttribute="student">
学生id:<form:input path="id"></form:input><br>
学生姓名:<form:input path="name"></form:input><br>
学生年龄:<form:input path="age"></form:input><br>
<input type="submit" value="提交">
</form:form>
</body>
</html>
JSP 页面导入 Spring MVC 表单标签库,与导入 JSTL 标签库的语法非常相似,前缀 prefix 可以自定义,通常定义为 from。
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
将 form 表单与模型数据进行绑定,通过 modelAttribute 属性完成绑定,将 modelAttribute 的值设置为模型数据对应的 key 值。
Handeler:modelAndView.addObject("student",student);
JSP:<form:form modelAttribute="student">
form 表单完成绑定之后,将模型数据的值取出绑定到不同的标签中,通过设置标签的 path 属性完成,将 path 属性的值设置为模型数据对应的属性名即可。
9.2常用的表单标签
- from
<form:from modelAttribute="student"/>
渲染的是 HTML 中的<form></from>
,通过 modelAttribute 属性绑定具体的模型数据。
- input
<form:input path="name"/>
渲染的是 HTML 中的 <input type="text"/>
,from 标签绑定的是模型数据,input 标签绑定的是模型数据中的属性值,通过 path 属性可以与模型数据中的属性名对应,并且支持及联操作。
<from:input path="address.name"/>
- password
<form:password path="password"/>
渲染的是 HTML 中的 <input type="password"/>
,通过 path 属性与模型数据的属性值进行绑定,password 标签的值不会在页面显示。
- checkbox
<form:checkbox path="hobby" value="读书"/>
student.setFlag(false);
checkbox:<form:checkbox path="flag" value="flag"></form:checkbox><br/>
渲染的是 HTML 中的 <input type="checkbox"/>
,通过 path 与模型数据的属性值进行绑定,可以绑定 boolean、数组和集合。
如果绑定 boolean 值,若该变量的值为 true,则表示该复选框选中,否则表示不选中。
如果绑定数组或者集合,数组/集合中的元素等于 checkbox 的 value 值,则选中。
student.setHobby(Arrays.asList("读书","看电影","玩游戏"));
modelAndView.addObject("student",student);
爱好:<form:checkbox path="hobby" value="摄影"></form:checkbox>摄影<br/>
<form:checkbox path="hobby" value="读书"></form:checkbox>读书<br/>
<form:checkbox path="hobby" value="听音乐"></form:checkbox>听音乐<br/>
<form:checkbox path="hobby" value="看电影"></form:checkbox>看电影<br/>
<form:checkbox path="hobby" value="旅游"></form:checkbox>旅游<br/>
<form:checkbox path="hobby" value="玩游戏"></form:checkbox>玩游戏<br/>
<input type="submit" value="提交"/>
- checkboxes
<form:checkboxes items=${student.hobby} path="selecHobby"/>
渲染的是 HTML 中的一组 <input type="checkbox"/>
,是对 <form:checkbox/>
的一种简化,需要结合 items 和 path 属性来使用,items 绑定被遍历的集合或数组,path 绑定被选中的集合或数组,可以这样理解,items 为全部可选集合,path 为默认的选中集合。
student.setHobby(Arrays.asList("摄影","读书","听音乐","看电影","旅游","玩游戏"));
student.setSelectHobby(Arrays.asList("摄影","读书","听音乐"));
modelAndView.addObject("student",student);
爱好:<form:checkboxes path="selectHobby" items="${student.hobby}"/><br/>
需要注意的是 path 可以直接绑定模型数据的属性值,items 则需要通过 EL 表达式的形式从域对象中获取数据,不能直接写属性名。
- rabiobutton
<from:radiobutton path="radioId" value="0"/>
渲染的是 HTML 中的一个 <input type="radio"/>
,绑定的数据与标签的 value 值相等则为选中,否则不选中。
student.setRadioId(1);
modelAndView.addObject("student",student);
radiobutton:<form:radiobutton path="radioId" value="1"/>radiobutton<br/>
- radiobuttons
<form:radiobuttons itmes="${student.grade}" path="selectGrade"/>
渲染的是 HTML 中的一组 <input type="radio"/>
,这里需要结合 items 和 path 两个属性来使用,items 绑定被遍历的集合或数组,path 绑定被选中的值,items 为全部的可选类型,path 为默认选中的选项,用法与 <form:checkboxes/>
一致。
Map<Integer,String> gradeMap = new HashMap<>();
gradeMap.put(1,"一年级");
gradeMap.put(2,"二年级");
gradeMap.put(3,"三年级");
gradeMap.put(4,"四年级");
gradeMap.put(5,"五年级");
gradeMap.put(6,"六年级");
student.setGradeMap(gradeMap);
student.setSelectGrade(3);
modelAndView.addObject("student",student);
学生年级:<form:radiobuttons items="${student.gradeMap}" path="selectGrade"/><br/>
- select
<form:select items="${student.citys}" path="selectCity"/>
渲染的是 HTML 中的一个 <select/>
标签,需要结合 items 和 path 两个属性来使用,items 绑定被遍历的集合或数组,path 绑定被选中的值,用法与 <from:radiobuttons/>
一致。
Map<Integer,String> cityMap = new HashMap<>();
cityMap.put(1,"北京");
cityMap.put(2,"上海");
cityMap.put(3,"广州");
cityMap.put(4,"深圳");
student.setCityMap(cityMap);
student.setSelectCity(3);
modelAndView.addObject("student",student);
所在城市:<form:select items="${student.cityMap}" path="selectCity"></form:select><br/>
- options
form:select
结合 form:options
的使用,from:select
只定义 path 属性,在 form:select
标签内部添加一个子标签 form:options
,设置 items 属性,获取被遍历的集合。
所在城市:<form:select path="selectCity">
<form:options items="${student.cityMap}"></form:options>
</form:select><br/>
-
option
form:select
结合form:option
的使用,from:select
定义 path 属性,给每一个form:option
设置 value 值,path 的值与哪个 value 值相等,该项默认选中。
所在城市:<form:select path="selectCity">
<form:option value="1">杭州</form:option>
<form:option value="2">成都</form:option>
<form:option value="3">西安</form:option>
</form:select><br/>
- textarea
渲染的是 HTML 中的一个 <textarea/>
,path 绑定模型数据的属性值,作为文本输入域的默认值。
student.setIntroduce("你好,我是...");
modelAndView.addObject("student",student);
信息:<form:textarea path="introduce"/><br/>
- errors
处理错误信息,一般用在数据校验,该标签需要结合 Spring MVC 的验证器结合起来使用。
10.SpringMvc表单后台校验
Spring MVC 提供了两种数据校验的方式:1、基于 Validator 接口。2、使用 Annotation JSR - 303 标准进行校验。
基于 Validator 接口的方式需要自定义 Validator 验证器,每一条数据的验证规则需要开发者手动完成,使用 Annotation JSR - 303 标准则不需要自定义验证器,通过注解的方式可以直接在实体类中添加每个属性的验证规则,这种方式更加方便,实际开发中推荐使用。
10.1Validator
接口
- 实体类Account
@Data
public class Account {
private String name;
private String password;
}
- 实现了validator接口的控制类
//import org.springframework.validation.Validator;接口的包
public class AccountValidator implements Validator {
@Override
public boolean supports(Class<?> aClass) {
return Account.class.equals(aClass);
}
@Override
public void validate(Object o, Errors errors) {
//这里直接通过Validator的工具类进行错误处理
ValidationUtils.rejectIfEmpty(errors,"name",null,"用户名不能为空");
ValidationUtils.rejectIfEmpty(errors,"password",null,"密码不能为空");
}
}
- handler类
@Controller
@RequestMapping("/validator")
public class AccountHandler {
@GetMapping("/login")
public String login(Model model) {
model.addAttribute("account", new Account());
return "login";
}
@PostMapping("/login")
public String login(@Validated Account account, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "login";
}
return "index";
}
}
这里存在两个login方法,而且看似第二个方法中的account是没有用的
两个login 方法第一个是使用的Get请求,首先将能够验证的数据传入,加载到model里,前端通过form标签的modelAttribute来读取->这里同时我们不能直接进入login.jsp的而是走validator/login来进入页面
第二个则是调用的Post请求,也就是提交的时候的数据验证,通过BindingResult来查看是否后端里的有错误,而第二个传入的Account被注解解析比对提交的时候是一致
- 前端jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@page isELIgnored="false" %>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form:form modelAttribute="account" method="post" action="/validator/login">
用户名:<form:input path="name" ></form:input><form:errors path="name"></form:errors><br>
密 码:<form:input path="password"></form:input><form:errors path="password"></form:errors><br>
<input type="submit"value="登录">
</form:form>
</body>
</html>
modelAttribute来读取存储的类,提交必须是post请求
后面就是input标签传递参数,和用error标签传出错误信息
- 配置springmvc.xml
<bean id="accountValidator" class="com.Lei.Validator.AccountValidator"></bean>
<mvc:annotation-driven validator="accountValidator"></mvc:annotation-driven>
10.2Annotation JSR - 303 标准
- 导入依赖
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.6.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.3.2.Final</version>
</dependency>
- 创建实体类并添加Validator注解
//import org.hibernate.validator.constraints.Email;
//import org.hibernate.validator.constraints.NotEmpty;
@Data
public class Person {
@NotEmpty(message = "用户名不能为空")
private String username;
@Size(min = 6, max = 12, message = "密码6-12位")
private String password;
@Email(regexp = "^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\\\\.[a-zA-Z0-9-]+)*\\\\.[a-zA-Z0-9]{2,6}$", message = "请输入正确的邮箱格式")
private String email;
@Pattern(regexp = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\\\\\\\\d{8}$", message = "请输入正确的电话")
private String phone;
}
上面的email和phoe是用的正则表达式
- handler兼控制器
@GetMapping("/register")
public String register(Model model){
model.addAttribute("person",new Person());
return "register";
}
@PostMapping("/register")
public String register(@Valid Person person,BindingResult bindingResult){
if (bindingResult.hasErrors()){
return "register";
}return "index";
}
- 前端jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@page isELIgnored="false" %>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form:form modelAttribute="person" action="/validator/register" method="post">
用户名:<form:input path="username"></form:input><form:errors path="username"></form:errors><br/>
密码:<form:input path="password"></form:input><form:errors path="password"></form:errors><br/>
邮箱:<form:input path="email"></form:input><form:errors path="email"></form:errors><br/>
电话:<form:input path="phone"></form:input><form:errors path="phone"></form:errors><br/>
<input type="submit" value="提交">
</form:form>
</body>
</html>
- 在springmvc.xml添加配置
<mvc:annotation-driven />
- 补充注解规则
注释名 | 效果 |
---|---|
@Null | 被注解的元素必须为null |
@NotNull | 被注解的元素不能为null |
@Min(value) | 被注解的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注解的元素必须是一个数字,其值必须小于于等于指定的最大值 |
被注解的元素必须是电子邮箱地址 | |
@Pattern | 被注解的元素必须符合对应的正则表达式 |
@Length | 被注解的元素的大小必须在指定的范围内 |
@NotEmpty | 被注解的字符串的值必须非空 |
注:Null 和 Empty 是不同的结果,String str = null,str 是 null,String str = “”,str 不是 null,其值为空。