SpringMVC
➤SpringMVC简介:
Spring是apache推出的开源框架,整合了springmvc框架及spring框架其他部分,是一种轻量级的、基于MVC的 Web应用框架 ,使用springMVC+Mybatis可以实现SSH几乎所有的功能,相对SSH更加的轻量,灵活。
➤SpringMVC特性
- 清晰的角色划分:
前端控制器(DispatcherServlet);请求到处理器映射(HandlerMapping);处理器适配器(HandlerAdapter);视图解析器(ViewResolver);处理器或页面控制器(Controller);验证器(Validator)
;命令对象(Command 请求参数绑定到的对象就叫命令对象);表单对象(Form Object 提供给表单展示和提交到的对象就叫表单对象) - 分工明细,拓展相当灵活
- 和Spring无缝集成,这个是其他框架不具备的,本身spring中包含了springMVC
- 功能强大的数据验证、格式化、
- 支持REST风格(所有的url都可当成资源)软件的架构风格
- 基于注解的零配置,在以前时候,Struts2中需要配置大量的xml文件;但是其实后来struts2也支持注解,相对轻量级
➤案例:SpringMVC版本的Hello world
开发环境和运行环境:
JDK1.8 Eclipse:2019-6 Tomcat9 或者8.5
更多可看spring-framework
访问方式1
-
新建一个Dynamic web project项目
记得生成web.xml文件,后面配置会用到 -
加入jar包到buildpath下:以Spring 5.1.1为例:
-
修改web.xml文件,加入前端(配置/控制)器(DispatcherServlet) servlet有关配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>springmvc_001</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- spring前端控制器的配置 -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- 启动参数设置,springmvc中,需要有一个配置文件 springmvc.xml(对应第4点的文件名) -->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- DispatcherServlet 的映射配置 -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<!-- 经过前端控制器的请求路径 -->
<!-- 切记不可设置为“/*”,因此其会覆盖掉容器中所有资源映射,导致静态资源强制使用DispatcherServlet处理,无法使用-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
注:首先 classpath是指 WEB-INF文件夹下的classes目录,这里存放的的部署后的文件文件如下图,这是一个定位资源的入口
classpath 和 classpath* 区别:
classpath:只会到你的class路径中查找文件; (src ->编译 classes)
classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找.
1
解释:
1)load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法)。
2)它的值必须是一个整数,表示servlet应该被载入的顺序
2)当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet;
3)当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载。
4)正数的值越小,该servlet的优先级越高,应用启动时就越先加载。
5)当值相同时,容器就会自己选择顺序来加载。
所以,x,中x的取值1,2,3,4,5代表的是优先级,而非启动延迟时间。
- 创建springmvc.xml文件:将其放在src目录下,或者使用向导(依从spring的配置文件创建的过程)自己生成
<?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.xsd">
<!-- 组件扫描的路径 -->
<context:component-scan base-package="com.mvc.controller"></context:component-scan>
<!-- 关于"视图"一些配置,暂时省略 -->
</beans>
- 创建控制器类:(相当于之前的servlet)
package com.mvc.controller;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
//表明这个类是一个控制器类
@RestController // springmvc的控制器bean,Rest支持rest风格[ajax请求]————表示这是一个ajax请求
public class QuestionController {
// @RequestMapping() //请求地址
//DeleteMapping
//PostMapping
@GetMapping(value = "hello",produces = "application/json;charset=utf-8") // get请求地址
// value:访问地址 localhost:端口/工程名/上面注解的value值
//produces:RestController中的请求,响应数据出现了乱码,需要使用produces
public String sayHello(String name,HttpServletResponse response) {
return "Hello " + name;
}
}
- 访问:url,访问地址: localhost:端口/工程名/上面注解的value值?属性值=
访问方式2:此时我们使用注解@Controller来配置我们的控制器,模拟一个页面转发的请求,而不是ajax相同的请求:
package com.mvc.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
//表明这个类是一个控制器类
@Controller
public class AuthorController {
// @RequestMapping() //请求地址
@GetMapping(value = "sayhello", produces = "application/json;charset=utf-8") // get请求地址
// 访问地址 locahost:端口/工程名/hello
public String sayHello(String name, HttpServletRequest request) {
// 这个返回的字符串hi,其实是一个视图名
// 转发到指定目录下的,叫做hi.xx后缀名的文件
// 例如: /WEB-INF/jsp/ 的 hi.jsp
// 这边的hello指的是文件名,也就是hello.jsp
return "hello";
}
}
- 更改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-4.3.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">
<!-- 组件扫描的路径 -->
<context:component-scan
base-package="com.mvc.controller"></context:component-scan>
<!-- 关于"视图解析器"一些配置,暂时省略 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 注入 -->
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView"></property>
<!-- 视图的前缀和后缀 -->
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
说明:
InternalResourceViewResolver:支持Servlet、JSP视图解析;
viewClass:JstlView表示JSP模板页面需要使用JSTL标签库;
prefix和suffix:查找视图页面的前缀和后缀(前缀[逻辑视图名]后缀),比如传进来的逻辑视图名为hi,则该该jsp视图页面应该存放在“/WEB-INF/jsp/hi.jsp”
-
页面hello.jsp,应该放在/WEB-INF/jsp/目录下;
-
访问之后的结果
访问静态资源
如果我们直接访问静态资源,页面的显示结果为404
原因是因为jsp请求过程,发现没有经过 DispatcherServlet前端控制器(Jsp不受前端控制器控制,其他受)
*Html文件,.txt, css,.js文件,jpg等, 属于静态资源;都被过滤了;
那么,解决方案就是更改Springmvc.xml配置
<!--静态资源的处理,我们的前端控制器不处理静态资源 ,注意,请求还是有走前端控制器,只不过不处理 -->
<mvc:default-servlet-handler/>
<!-- 这句话要加,否则控制器失效 -->
<mvc:annotation-driven></mvc:annotation-driven>
控制器传值给jsp
在上面的AuthorController.class控制器类加上
public String sayHello(String name, HttpServletRequest request) {
// 这个返回的字符串hi,其实是一个视图名
// 转发到指定目录下的,叫做hi.xx后缀名的文件
// 例如: /WEB-INF/jsp/ 的 hi.jsp
request.setAttribute("name", name);
request.setAttribute("key", "kkk");
// 这边的hello指的是文件名,也就是hello.jsp
return "hello";
}
hello.jsp加上代码,来接收值
<body>
<h1>来自jsp的hello.jsp</h1>
${name}=>${key};
</body>
结果
➤注:Struts2和SpringMVC区别
SpringMVC | Struts2 | |
---|---|---|
轻量级别 | 重量级别 | |
servlet | filter | |
基于方法的拦截,比较快 | 基于类的拦截 |
➤C控制器转发到V视图的方法
非ajax请求(页面跳转)
1)request.seAttribute() /session.setAtribute =>依然可以用.可以用优化后的方法,ModelAndView
控制器DataTransController.class
package com.mvc.controller;
import java.util.Arrays;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import com.mvc.entity.Users;
@Controller
public class DataTransController {
//浏览器需要输入这个t1
@GetMapping("t1")
public ModelAndView test1() {
ModelAndView mv = new ModelAndView("t1");
Users u1 = new Users(1, "Tom", 10, 100);
Users u2 = new Users(2, "Tem", 30, 300);
Users u3 = new Users(3, "Tcm", 20, 200);
mv.addObject("list", Arrays.asList(u1,u2,u3));
return mv;
}
}
jsp页面hello.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>测试页面</title>
</head>
<body>
<h1>来自jsp的hello.jsp</h1>
<c:forEach items="${list}" var="u">${u}<br />
</c:forEach>
</body>
</html>
结果
ajax请求
2)Ajax响应(servlet : response.getWriter().printIn) =>直接使用响应注解返回响应体
a)加入ajax支持的jar包jackson包。
b)在方法上加@ResponseBody;或者我们可以在类前@RestController
控制器AjaxController.class
package com.mvc.controller;
import java.util.Arrays;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.mvc.entity.Users;
@RestController
public class AjaxController {
// 浏览器需要输入这个
@GetMapping("ajax")
// @ResponseBody
public List ajax() {
Users u1 = new Users(1, "Tom", 10, 100);
Users u2 = new Users(2, "Tem", 30, 300);
Users u3 = new Users(3, "Tcm", 20, 200);
return Arrays.asList(u1, u2, u3);
}
}
ajax.jsp页面
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>测试页面</title>
</head>
<body>
<input type="submit" id="sumbit">
<div id="showdiv"></div>
<script src="js/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
$("#sumbit").click(function(){
$.ajax({
url:"ajax",
type:"get",
success:function(res){
console.log(res);
$.each(res,function(index,u){
$("#showdiv").append(u.name+"<br/>");
});
}
})
})
});
</script>
</body>
</html>
修改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-4.3.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">
<!-- 组件扫描的路径 -->
<context:component-scan
base-package="com.mvc.controller"></context:component-scan>
<!-- 关于"视图解析器"一些配置,暂时省略 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 注入 -->
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView"></property>
<!-- 视图的前缀和后缀 -->
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--静态资源的处理,我们的前端控制器不处理静态资源 ,注意,请求还是有走前端控制器,只不过不处理 -->
<mvc:default-servlet-handler />
<!-- 这句话要加,否则控制器失效 -->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
结果:
➤V视图传值给C控制器的方法
url传值
package com.mvc.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.mvc.entity.Users;
@RestController
public class ParameterCotroller {
@GetMapping("pc1")
public String pc1(String str,int n) {
System.out.println("str:"+str +"n:"+n);
return "success";
}
@GetMapping("pc2")
public String pc2(Users users) {
System.out.println("Users:"+users);
return "success";
}
}
表单提交
控制器ParameterCotroller
package com.mvc.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import com.mvc.entity.Users;
@RestController
public class ParameterCotroller {
@GetMapping("pc1")
public String pc1(String str, int n) {
System.out.println("str:" + str + "n:" + n);
return "success";
}
@GetMapping("pc2")
public String pc2(Users users) {
System.out.println("Users:" + users);
return "success";
}
@PostMapping("pc3")
public String pc3(Users users) {
System.out.println("Users:" + users);
return "success";
}
}
form.jsp页面
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>测试页面</title>
</head>
<body>
<h1>视图——form</h1>
<form action="pc3" method="post">
<input type="text" id="id" name="id"><br/>
<input type="text" id="name" name="name"><br/>
<input type="text" id="website_num" name="website_num"><br/>
<input type="text" id="website_access_num" name="website_access_num"><br/>
<input type="submit">
</form>
</body>
</html>
结果如下
注意:如果jsp上的method提交方式和控制器的不符合,会报如下错误,需要修改成一致的提交方式并重启服务
ajax请求
继续使用上面的控制器ParameterCotroller.class
package com.mvc.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.mvc.entity.Users;
@RestController
public class ParameterCotroller {
@GetMapping("pc1")
public String pc1(String str, int n) {
System.out.println("str:" + str + "n:" + n);
return "success";
}
@GetMapping("pc2")
public String pc2(Users users) {
System.out.println("Users:" + users);
return "success";
}
@GetMapping("pc3")
public String pc3(Users users) {
System.out.println("Users:" + users);
return "success";
}
}
ajaxform.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>测试页面</title>
</head>
<body>
<h1>ajaxform</h1>
<script src="js/jquery.min.js"></script>
<form action="pc3" method="get">
<input type="text" id="id" name="id"><br /> <input
type="text" id="name" name="name"><br /> <input type="text"
id="website_num" name="website_num"><br /> <input
type="text" id="website_access_num" name="website_access_num"><br />
<input type="button" id="sumbit" value="提交">
</form>
<script type="text/javascript">
$(function() {
$("#sumbit").click(function() {
console.log($("form").serialize());//表单的序列化方式
$.ajax({
url : "pc3",
type : "get",
data: {"id":$("#id").val(),
"name":$("#name").val(),
"website_num":$("#website_num").val(),
"website_access_num":$("#website_access_num").val()
},
//data : $("form").serialize(),
success : function(res) {
console.log(res);
}
})
})
});
</script>
</body>
</html>
结果:
➤SpringMVC+JDBC实现数据库显示到页面
➤ 网站管理案例
web项目加载applicationContext.xml
➤中文乱码处理
characterEncodingFilter顶层是filter
get提交方式不会出现中文乱码,post会
postEncoding修改适用于普通的表单提交
针对post请求
EncodingController 控制器
package com.mvc.controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import com.mvc.entity.Users;
@RestController
public class EncodingController {
@PostMapping(value = "pc4", produces = "application/json;charset=utf-8")
public String pc4(Users users) {
System.out.println("Users:" + users);
return "success";
}
}
form.jsp页面
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>测试页面</title>
</head>
<body>
<h1>post——解决普通表单提交乱码</h1>
<form action="pc4" method="post">
<input type="text" id="id" name="id"><br/>
<input type="text" id="name" name="name"><br/>
<input type="text" id="website_num" name="website_num"><br/>
<input type="text" id="website_access_num" name="website_access_num"><br/>
<input type="submit">
</form>
</body>
</html>
配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>springMVC03</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- The front controller of this Spring Web application, responsible for
handling all application requests -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- 启动参数设置,springmvc中,需要有一个配置文件 springmvc.xml -->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 新增以下内容 -->
<!-- Map all requests to the DispatcherServlet for handling -->
<!-- DispatcherServlet映射配置 -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- spring提供的characterEncodingFilter配置 -->
<filter>
<filter-name>encodingFilter</filter-name>
<!-- 观察发现这个类 CharacterEncodingFilter 有一个属性 encoding 所以提供一个initparm以及 value -->
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 给 CharacterEncodingFilter类的对象进行初始化的赋值 request.setCharacterEncoding -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!-- 响应编码的设置 true 设置response -->
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<!-- 过滤器对哪些资源进行过滤 -->
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
➤重点注解
RestFul风格
请求方式 | 请求路径 | 功能 |
---|---|---|
GET | api/user/ | 返回用户列表 |
GET | api/user/1 | 返回用户编号为1的用户对象 |
POST | api/user | 增加一个用户,参数为user对象json格式 |
PUT | api/user/1 | 更新用户,参数id编号为1,json格式的用户 |
DELETE | api/user/1 | 删除编号为1的用户 |
DELETE | api/user/ | 删除所有用户 |
细节案例比较(以增加为例子)
PathVariableController.class控制器
package com.etc.controller;
import java.util.Arrays;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.etc.entity.Users;
@RestController
public class PathVariableController {
//Description:增加记录 支持:postman的urlencoded方法,相当于原始表单post提交;
@PostMapping(value = "api/user")
public Users addUsers(Users user) {
System.out.println("user: " + user);
return user;
}
//Description:增加记录 ; @RequestBody Users user 支持:postman的text->json格式提交;
@PostMapping(value = "apis/user")
public Users addUsers2(@RequestBody Users user) {
System.out.println("user: " + user);
return user;
}
}
原始表单post提交
RestAdd.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
form 普通表单提交
<form action="api/user" method="post">
<input type="text" id="id" name="id" value="1"><br /> <input
type="text" id="name" name="name" value="baidu"><br /> <input
type="text" id="website_num" name="website_num" value="0"><br />
<input type="text" id="website_access_num" name="website_access_num"
value="0"><br /> <input type="submit" value="提交">
</form>
<hr>
Ajax表单部分
<form id="form1">
<input type="text" id="id" name="id" value="1"><br /> <input
type="text" id="name" name="name" value="前度"><br /> <input
type="text" id="website_num" name="website_num" value="0"><br />
<input type="text" id="website_access_num" name="website_access_num"
value="0"><br /> <input type="button" id="btn" value="提交">
</form>
<script src="js/jquery.min.js">
</script>
<script>
$(function() {
$("#btn").click(function() {
console.log($("#form1").serialize()); //表单的序列化的方式
$.ajax({
url : 'api/user',
type : 'post',
//data: layui data.filed (layui封装) / data: 自己拼接
//data: {"id":$("#id").val(),"name":$("#name").val(),"website_num":$("#website_num").val(),"website_access_num":$("#website_access_num").val()},
data : $("#form1").serialize(),
success : function(res) {
console.log(res);
}
});
});
});
</script>
</body>
</html>
结果——form 普通表单提交
结果——Ajax表单
Ajax表单部分
RestAddAjax.jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<hr>
Ajax表单部分(@RequestBody)
<form id="form1">
<input type="text" id="id" name="id" value="1"><br/>
<input type="text" id="name" name="name" value="前度"><br/>
<input type="text" id="website_num" name="website_num" value="0"><br/>
<input type="text" id="website_access_num" name="website_access_num" value="0"><br/>
<input type="button" id="btn" value="提交">
</form>
<script src="js/jquery.min.js">
</script>
<script>
$(function(){
$("#btn").click(function(){
console.log({"id":$("#id").val(),"name":$("#name").val(),"website_num":$("#website_num").val(),"website_access_num":$("#website_access_num").val()});
console.log(JSON.stringify({"id":$("#id").val(),"name":$("#name").val(),"website_num":$("#website_num").val(),"website_access_num":$("#website_access_num").val()}));
$.ajax({
url: 'apis/user',
type: 'post',
contentType: "application/json",//提交到控制器的数据格式,不设置会报格式错误
//data: layui data.filed (layui封装) / data: 自己拼接
//JSON.stringify转换成标准的JSON格式
data:JSON.stringify({"id":$("#id").val(),"name":$("#name").val(),"website_num":$("#website_num").val(),"website_access_num":$("#website_access_num").val()}),
//data: $("#form1").serialize(),
success: function(res){
console.log(res);
}
});
});
});
</script>
</body>
</html>
结果:
@PathVariable
用于将请求URL中的模块变量映射到功能处理方法的参数上
package com.mvc.controller;
import java.util.Arrays;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.mvc.entity.Users;
@RestController
public class PathVariableController {
//请求方式:Get——返回用户列表
@GetMapping(value = "api/user", produces = "application/json;charset=utf-8")
public List<Users> queryUser() {
Users u = new Users(1, "哈哈", 23, 42);
return Arrays.asList(u);
}
//请求方式:Get——返回特定用户
@GetMapping(value = "api/user/{id}/{name}/{website_num}/{website_access_num}",produces = "application/json;charset=utf-8")
public Users queryuser(@PathVariable(name = "id") int id,
@PathVariable(name = "name") String name,
@PathVariable(name = "website_num") int website_num,
@PathVariable(name = "id") int website_access_num) {
Users u = new Users(id, name, website_num, website_access_num);
return u;
}
// 请求方式:Post——增加一个用户
@PostMapping(value = "api/user/add")
public Users addUsers(Users users) {
System.out.println("users" + users);
return users;
}
// 请求方式:Post——增加一个用户,参数为user对象json格式
// 支持postman urlencoded方法,相当于原始表单post提交
@PostMapping(value = "api/user/add1")
public Users addUsers2(@RequestBody Users users) {
System.out.println("users" + users);
return users;
}
}
结果——用户列表
结果——特定用户
结果——增加用户(这里使用postman测试)
结果——增加用户(这里严格要求参数必须是json格式的)
@RequestMapping
有RequestMapping、GetMapping、HeadMapping、PutMapping、OptionsMapping、PatchMapping、DeleteMapping、TraceMapping
RequestMappingController控制器
package com.mvc.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RequestMappingController {
// 设置提交的方式
@RequestMapping(method = RequestMethod.GET, value = "ac1")
public String ac1(String name) {
System.out.println("name:" + name);
return "ac1" + name;
}
}
@CookieValue
@RequestHeader
Model、Map或ModelMap
传递一个user对象给jsp页面的方法
- request.getArribute
- model、ModelMap、Map——方法参数位置
- modelAndview——方法内部,作为放回值
- ModelAttrbute——写在方法前,返回是一个对象
类似于request对象的setAttribute方法的作用,用来在一个请求过程中传递处理的数据。通过以下方法向页面传递参数:
addAttribute(String key,Object value)
package com.mvc.controller;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("m")
public class ModelTestController {
@GetMapping("mtc1")
public String mtc1(HttpServletRequest request) {
//servlet的请求方式给页面传递数据
request.setAttribute("msg", "hello");
return "mtc1";
}
@GetMapping("mtc2")
public String mtc2(Model model) {
model.addAttribute("msg", "kitty");
model.addAttribute("msg1", "tom");
return "mtc1";
}
@GetMapping("mtc3")
public String mtc3(ModelMap modelMap, Model model) {
// BindingAwareModelMap
System.out.println(model == modelMap);// true 其实这里用到的是同一个实例
model.addAttribute("msg", "kitty");
modelMap.addAttribute("msg1", "kitty1");
return "mtc1";
}
@GetMapping("mtc4")
public String mtc3(ModelMap modelMap, Model model, Map map) {
// BindingAwareModelMap
System.out.println(model == modelMap);// true 其实这里用到的是同一个实例
System.out.println(model == map);// true 其实这里用到的是同一个实例
model.addAttribute("msg", "kitty");
modelMap.addAttribute("msg1", "kitty1");
map.put("msg2", "map");
return "mtc1";
}
}
@ModelAtrribute
ModelAttributeController 控制器
package com.mvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import com.mvc.entity.Users;
@Controller
public class ModelAttributeController {
// 目的—— c->v
@GetMapping(value = "model1")
public String model1(@ModelAttribute(value = "users") Users users) {
users = new Users(1, "读者", 2, 3);//这个对象不会存放到request中
return "users";
}
//user对象—— c->v
//当方法的返回值不是一个字符串时,且非ajax请求,这将会跳转到一个和映射名相同的视图资源去
//例如:这里会跳转到/WEB-INF/jsp/model2.jsp
@GetMapping(value = "model2")
@ModelAttribute(value = "users")
public Users model2() {
Users users = new Users(1, "读者", 2, 3);
return users;
}
}
结果:
注:结果在RequestScope
@SessionAtrributes
因为有ModelAttribute
➤转发和重定向
转发forward
情况 | 转发forward位置 |
---|---|
return 字符串 | 跳转到/WEB-INF/jsp/字符串.jsp |
return “forward:X”; | 跳转到X的映射地址 |
return “forward:X.jsp”; | 跳转到webContent根目录下X.jsp |
return “forward:X/Y/Z/X.jsp”; | 跳转到X/Y/Z/X.jsp |
package com.mvc.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ForwardAndRedirectController {
@GetMapping(value = "frc1", produces = "application/json;charset=utf-8")
public String frc1() {
// 跳转到/WEB-INF/jsp/frc1.jsp
return "frc1";
}
@GetMapping(value = "frc2", produces = "application/json;charset=utf-8")
public String frc2() {
// 加入了forward关键字,配置视图解析器的前缀和后缀失效了,此时跳转到frc2的一个映射地址去
return "forward:frc2";
}
@GetMapping(value = "frc3", produces = "application/json;charset=utf-8")
public String frc3() {
// 此时跳转到frc1的一个映射地址去
return "forward:frc1";
}
@GetMapping(value = "frc4", produces = "application/json;charset=utf-8")
public String frc4() {
// 跳转到webContent目录下的frc1.jsp
return "forward:frc1.jsp";
}
@GetMapping(value = "frc5", produces = "application/json;charset=utf-8")
public String frc5() {
// 可以加路径,多级目录,跳转到webContent目录下的frc1.jsp
return "forward:h/e/l/l/o/frc1";
}
}
重定向redirect
WEB/INF中的内容URL地址是进不去的,可以通过转发进去
情况 | 重定向redirect位置 |
---|---|
return “redirect:/WEB-INF/jsp/X.jsp”; | WEB-INF/目录下的文件不能直接重定向来访问 |
return “redirect:/X.jsp”; | 重定向WebContent/X.jsp |
package com.mvc.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ForwardAndRedirectController {
@GetMapping(value = "frc6", produces = "application/json;charset=utf-8")
public String frc6() {
// WEB-INF/目录下的文件不能直接重定向来访问
return "redirect:/WEB-INF/jsp/hello.jsp";
}
@GetMapping(value = "frc7", produces = "application/json;charset=utf-8")
public String frc7() {
// 重定向WebContent/frc1.jsp
return "redirect:/frc1.jsp";
}
}
特殊情况及响应
➤SpringMVC实现文件上传
了解一下3种文件上传组件:
cos ,commons-fileupload ,Servlet3.0自带后的Part
cos组件
官方文档
我们看到官方的user guide中已经有详细的案例了,但是还是比较麻烦,而springmvc中对这个文件上传又做了封装;
封装的依然是commons-fileupload.jar / commons-io.jar,场景是前台表单中,一个文本框和一个文件域;控制器中接收实现文件上传;
具体操作步骤
- 加入两个jar包commons-fileupload-1.4.jar,分别是commons-io-2.6.jar和commons-fileupload-1.4.jar
- 在SpringMVC配置文件中定义一个bean 类型为CommonsMultipartResolver。在SpringMVC中专门封装了一个类CommonsMultipartResolver来处理文件上传,所以需要在 SpringMVC的配置件中加入一个bean;配置bean的代码如下:
<!-- 文件上传的Bean -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
- 编写前台html页面:特别要注意这个action的值,确保是正确的url路径;
<body>
<h1>SpringMVC的文件上传</h1>
<form action="myupload.do" method="post" enctype="multipart/form-data">
<input type="text" id="id" name="id" value = "1"><br/>
<input type="file" name ="myfile" placeholder = "选择图片">
<input type = "submit" value = "提交">
</form>
</body>
- 编写文件上传的控制器代码
@Controller
public class UpLoadController {
@RequestMapping(value = "myupload.do", method = RequestMethod.POST)
public String upload(HttpServletRequest request) {
// 将request 转换为 MultipartHttpServletRequest
MultipartHttpServletRequest req = (MultipartHttpServletRequest) request;
// 得到上传的文件
MultipartFile file = req.getFile("myfile");
// 得到文件域的名字file.getName() 得到文件名file.getOriginalFilename()
System.out.println(file.getName() + "," + file.getOriginalFilename());
// 上传的目标路径
String path = request.getRealPath("/imgs") + "/" + file.getOriginalFilename();
System.out.println("path :" + path);
// 创建目标文件
File destFile = new File(path);
try {
// 直接使用封装好的 copyInputStreamToFile 实现文件的上传功能
FileUtils.copyInputStreamToFile(file.getInputStream(), destFile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "failed";
}
return "success";
}
}
- 访问路径: http://localhost:8080/springMVC05/index.jsp
这里是能成功进入成功页面,只是笔者没有写这个页面,所以404
- 文件最后上传到的位置是Tomcat的目录下
这里是访问 D:\ccp\softWare\apache-tomcat-9.0.41\webapps\springMVC05\imgs
多文件上传
控制器代码
/**
* 多文件上传
* 1.参数加上集合
* @param request
* @param id
* @param myfiles
* @return
*/
@PostMapping("uploadfiles.do")
public String uploadFiles(HttpServletRequest request, @RequestParam(value = "id", required = false) int id,
String name, @RequestParam("myfile") MultipartFile[] myfiles) {
for (MultipartFile myfile : myfiles) {
if (!myfile.getOriginalFilename().isEmpty()) {
String path = request.getRealPath("/");
File file = new File(path + myfile.getOriginalFilename());//
try {
// 文件上传操作
myfile.transferTo(file);
} catch (IllegalStateException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return "success";
}
页面代码
<body>
<h1>SpringMVC的多文件上传</h1>
<form action="uploadfiles.do" method="post" enctype="multipart/form-data">
<input type="text" id="id" name="id" value = "1"><br/>
<input type="file" name ="myfile" placeholder = "选择图片1">
<input type="file" name ="myfile" placeholder = "选择图片2">
<input type="file" name ="myfile" placeholder = "选择图片3">
<input type = "submit" value = "提交">
</form>
</body>
对文件上传大小限制
更改springmvc配置
<!-- 文件上传解析器的Bean,id必须是multipartResolver,否则报错 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 指定所上传文件的总大小不能超过200KB。注意maxUploadSize属性的限制不是针对单个文件,而是所有文件的容量之和 -->
<!-- 如果超过200kb会报一个异常 -->
<property name="maxUploadSize" value="200000"></property>
</bean>
这时候,如果用户上传文件的大小超过用户提供的限制大小,Tomcat将要优先拦截请求并且重定向
理论要显示文件过大,而不是显示错误页面给用户——解决方法
- 修改server.xml
如果是开发环境的话,可以设置成-1,使Tomcat将不会进行拦截任何请求(如果是实际环境,应该设置成合适的文件大小,如11MB=11x1024x1024=bytes -> maxSwallowSize=“11534336”)
<Connector connectionTimeout="20000" port="8080"
protocol="HTTP/1.1" redirectPort="8443" maxSwallowSize="-1" />
➤SpringMVC实现文件下载
控制器代码
@Controller
public class DownloadController {
@GetMapping("filedowload.do")
public void uploadFile(HttpServletRequest request, HttpServletResponse response, String fileName)
throws IOException {
// 得到文件的实际路径
String realpath = request.getServletContext().getRealPath("/img/");
File file = new File(realpath, fileName);
// 设置响应头,支持文件的下载
response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
// 先从服务器上读文件
FileInputStream fis = new FileInputStream(file);
// 然后将这个文件写入目标位置
OutputStream fos = response.getOutputStream();
// 比较小的文件读取和写入
byte[] b = new byte[fis.available()];
fis.read(b);
fos.write(b);
fos.close();
fis.close();
}
}
页面
<body>
<h1>SpringMVC的文件下载</h1>
<!-- .jpg 使用js来灵活获取并提交 -->
<input type="text" id="fileName">
<button id="download">下载图片</button>
<script src="js/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
//filedowload.do?fileName=1.jpg"
$("#download").click(function(){
//需要加入为空的判断... =>先查询,列表->点击下载
location.href="filedowload.do?fileName="+$("#fileName").val() //fileName
});
})
</script>
</body>
➤SpringMVC拦截器
SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理,可以用来做日志记录,通过它来进行权限验证,或者是来判断用户是否登陆。
SpringMVC 中的Interceptor 拦截请求是通过HandlerInterceptor 来实现的。在SpringMVC 中定义一个Interceptor 非常简单
主要有两种方式:
- 第一种方式是要定义的Interceptor类要实现了Spring 的HandlerInterceptor 接口,或者是这个类继承实现了HandlerInterceptor 接口的类,比如Spring 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter
- 第二种方式是实现Spring的WebRequestInterceptor接口,或者是继承实现了WebRequestInterceptor的类。
springMVC拦截器使用步骤:
/** 根目录和子目录的url请求:controller映射,静态资源html,不会拦截jsp
先走构造->preHandle->postHandle->afterComletion
日志记录
新建MyInterceptor 控制器,写上日志记录相关代码
package com.etc.interceptors;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class MyInterceptor implements HandlerInterceptor {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
final Log logger = LogFactory.getLog(getClass());
System.out.println("postHandle");
// 日志记录
logger.info("request.getRemoteAddr():" + request.getRemoteAddr());
FileWriter fw = new FileWriter("log.txt", true);
BufferedWriter bw = new BufferedWriter(fw);
bw.write(request.getRemoteAddr() + ":" + new Date().toLocaleString() + "\r\n");
bw.flush();
bw.close();
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
System.out.println("afterCompletion");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
/**
*
*/
public MyInterceptor() {
// TODO Auto-generated constructor stub
System.out.println("MyInterceptor");
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// TODO Auto-generated method stub
System.out.println("preHandle");
// if(request.getSession().getAttribute("user")==null) {
// response.sendRedirect("login.jsp");
// return false;
// }
return true;
}
}
新建一个控制器InterceptorController.class来测试
package com.etc.controller;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class InterceptorController {
protected final Log logger = LogFactory.getLog(getClass());
@GetMapping("ic01")
public String ic01() {
logger.info("ic01");
return "ic01";
}
}
因为写的是log.txt,所以最后的日志文件log.txt会放在eclipse安装根目录下,如果需要其他位置,可以使用realPath
➤注意点:
WEBINFO下的文件只能转发,不能重定向
Handler指的是控制器controller
SpringMVC默认加载/WEB-INF/[servlet-name]-servlet.xml
@RequestMapping注解没有指明请求方法,默认post和get请求都能访问,如果底下方法没有加requestMapping没有加,则加载不到方法
@RestController = @Controller + 方法前的@ResponseBody————since 4.0
public class DataTransController{
public ModelAndView test1(){
ModelAndView mv = new ModelAndView("t1");
return mv;
}
//上者等价于下者
public String test1(){
return "t1";
}
}
SpringMVC映射到方法级别
@RestController可以放在类前
@RequestBody可以放在方法和类前
- spring + spring mvc
web项目中:
spring: applicationContext.xml (IOC配置 AOP配置 数据源配置 事务管理器配置 等等)=>src目录
web项目怎么加载 applicationContext.xml:
web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
spring mvc:
springmvc.xml: (springmvc-servlet.xml)
配置了视图解析器,控制器的扫描包,静态资源过滤处理,文件上传下载配置
web项目怎么加载 springmvc.xml:
web.xml
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
测试:
dao/service/: junit测试 spring+junit
controller: 可以用postman(建议升级到最新版,否则可能有bug),后续还有介绍
@GetMapping @RequestMapping
@GetMapping == @RequestMapping(method = RequestMethod.GET)
@RestController ==@Controller + @ResponseBody
什么时候用到拦截器
SpringMVC拦截器和Servelt中Filter区别
Interceptor | Filter | 拦截器 |
---|---|---|
org.springframework.web.servlet.HandlerInterceptor接口 | Javax.servlet.Filter接口 | 接口定义位置 |
基于java的反射机制,是AOP思想的实现 | 属于javaEE标准,基于函数回调,也是AOP思想的实现 | 接口定义位置 |
Spring配置文件 | Web.xml中或者@WebFilter | 接口定义位置 |
拦截器拦截到具体方法的前后,整个请求的生命周期 | Filter只能在servlet前后起作用,依赖于servlet | 接口定义位置 |
Spring的aop实现 和支持 | Servlet规范和支持 | 接口定义位置 |
可以使用spring的任何资源,业务对象,数据源访问等,只需要给拦截器注入对象就可以 | Filter不能直接使用 spring资源 | 接口定义位置 |
被spring容器调用,容器实例化 | 被tomcat调用 | Spring拦截器执行晚于过滤器 |