什么是SpringMVC?
SpringMVC是基于MVC开发模式的框架,用来优化控制器,属于Spring,在Spring3.0后发布的,也具备IoC跟AOP
SpringMVC负责优化控制器Controller
MyBatis负责优化数据访问层DAO
那Spring负责干什么呢?
- Spring负责整合框架SpringMVC和MyBatis,何以见得?
- 我们不用手动创建动态代理
- Mybatis中的mybatis-config.xml都被整没了
SpringMVC有什么优点?
- 轻量级,基于MVC的框架
- 易于上手,容易理解,功能强大
- 具备AOP跟IoC
- 完全基于注解开发
SpringMVC的优化方向
SpringMVC执行流程
之前学的请求过程是:
-
index.jsp<------>servlet
-
通过index.jsp发送请求到servlet
但是SpringMVC的处理器是普通类,并不是servlet,怎么办?
- index.jsp<--------->DispatcherServlet<------------->SpringMVC
- 通过一个servlet(核心处理器)转发请求,还是web请求到servlet的模式,只是这个servlet不负责具体的业务,只是它将请求转发出去了
具体实现过程
假设现在有个请求http://localhost:8080/login
-
进入DispatcherServlet进行请求转发
-
转发到HandleMapping映射器,判断请求路径
-
路径正确,进入HandleAdapter适配器,把请求/login,分发到具体的/login的方法上,比如:
-
@RequestMapping("/login") public String test(){xxxxxx; return "login";}
-
返回视图
-
-
返回 "login"字符串,让InternalResourceViewResolver视图解析器解析(拼串):
-
<property name="prefix" value="/admin/"/> <property name="suffix" value=".jsp"/>
-
-
将/admin/login.jsp的视图页面返回给用户
注意!
DispatcherServlet必须要在web.xml中注册才能用
DispatcherServlet不能使用注解*式配置,*因为这是spring的源码,我们无法修改
Controller方法的返回值
- String:客户端资源的地址,自动拼接前缀和后缀,还可以屏蔽自动拼接字符串,可以指定返回的路径
- Object:返回json格式的对象,自动将对象或集合转为json,使用的jackson工具进行转换,必须要添加jackson依赖,一般用于ajax请求
- void:无返回值,一般用于ajax请求
- 基本数据类型:用于ajax请求
- ModelAndView:返回数据和视图对象,用的很少
注解开发
@RequestMapping
@RequestMapping
可以用在类和方法上,映射服务器访问的路径
这个注解可以区分get跟post请求
- 用在类上,表示创建一个虚拟目录,用在什么情况下?
- 一样的servlet名字,但是路径不同,用于区分servlet
- 用在方法上,指定servlet的名字
// action方法的规范
// 1.访问权限是public
// 2.方法的返回值任意
// 3.方法名称任意
// 4.方法可以没有参数,如果有可以是任意类型
// 5.要使用@RequestMappering注解来声明一个访问的路径(名称)
在@RequestMapping
下有几个子注解可以直接使用
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
可以代替@RequestMapping(name=“xxx” ,method=xxx)
案例
在web.xml中:
- 注册springmvc框架
- 注册的时候同时将springmvc.xml加入
在springmvc.xml中:
- 包扫描
- 添加视图解析器InternalResourceViewResolver
d路径下的demo Servlet方法
@Controller
@RequestMapping("d")
public class DemoAction {
// action方法的规范
// 1.访问权限是public
// 2.方法的返回值任意
// 3.方法名称任意
// 4.方法可以没有参数,如果有可以是任意类型
// 5.要使用@RequestMappering注解来声明一个访问的路径(名称)
@RequestMapping("demo")
public String demo(){
System.out.println("d服务器被访问到了");
return "main";//可以跳到/admin/main.sp页面上
}
}
d1路径下的demo Servlet方法
@Controller
@RequestMapping("d1")
public class DemoAction1 {
@RequestMapping("/demo")
public String demo(){
return "main";
}
@RequestMapping(value = "/demo",method = RequestMethod.POST)
public String demo2(){
return "main";
}
}
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 添加包扫描-->
<context:component-scan base-package="com.wzw.controller"/>
<!-- 添加视图解析器-->
<!-- 这个InternalResourceViewResolver视图解析器有啥用?
这个视图解析器的作用就是保证我传值的时候只写main,即可到达这个admin/main.jsp
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 配置前缀-->
<property name="prefix" value="/admin/"/>
<!-- 配置后缀-->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 让springmvc不处理静态资源:.js .css .html .mp3 .mp4-->
<!-- 在springMVC-servlet.xml中配置<mvc:default-servlet-handler />后,会在Spring MVC上下文中定义一个org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。
一般Web应用服务器默认的Servlet名称是"default",因此DefaultServletHttpRequestHandler可以找到它。如果你所有的Web应用服务器的默认Servlet名称不是"default",则需要通过default-servlet-name属性显示指定:
<mvc:default-servlet-handler default-servlet-name="所使用的Web服务器默认使用的Servlet名称" />-->
<mvc:default-servlet-handler/>
</beans>
html
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/d/demo.action">访问服务器</a>
<a href="${pageContext.request.contextPath}/d1/demo.action">访问服务器</a>
<form method="post" action="${pageContext.request.contextPath}/demo.action">
<input type="submit" value="post提交">
</form>
</body>
</html>
web.xml,注册SpringMVC框架,并且设置拦截器路径
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- 注册springMVC框架-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 注册的同时需要告诉DispatcherServletspringmvc做了什么-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 指定拦截什么样的请求-->
<!-- 不拦截:http://localhost:8080/one.jsp-->
<!-- 拦截:http://localhost:8080/one.action-->
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>
测试:
d服务器被访问到了
d1服务器被访问到了
注意
在springMVC的依赖中:如果版本大于5.2.5,使用@RequestMapping的时候需要写全名,小于等于5.2.5,可以只写名称
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<a href="${pageContext.request.contextPath}/demo.action">访问服务器</a>
@RequestMapping("demo")
public String demo(){
System.out.println("服务器被访问到了");
return "main";//可以跳到/admin/main.sp页面上
}
数据提交的方式
散提交
散提交的意思就是单个数据注入
不用再getParameter获取参数了,只要参数名称一致,就可以直接读取到
@RequestMapping("/submit")
public String submit(String myname,int age){
System.out.println("访问get请求的submit");
System.out.println(myname);
System.out.println(age);
return "main";
}
<form action="${pageContext.request.contextPath}/submit.action">
<input type="text" name="myname"><br>
<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
对象注入
通过封装对象注入
不用再getParameter获取参数了,只要表单与bean名称一致,就可以直接读取到
@RequestMapping("/fzdx")
public String fzdx(User user){
System.out.println(user);
return "main";
}
//User{name='张三', age=12}
<h1>封装对象提交</h1>
<form method="get" action="${pageContext.request.contextPath}/fzdx.action">
<input type="text" name="name"><br>
<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
动态占位符提交
RestFul风格
适用于超链接提交数据,请求路径后面加上变量,且在controller方法里接收,动态接收,而且也可以自动转化类型,比如提交的是一个int类型的数据,接收的时候可以直接接收int类型的。
@PathVariable
表示在这个动态路径里面,将哪个值赋值给参数,下面的这个例子就是表达:将dynamic后的第一个值传给name1
@RequestMapping("dynamic/{value1}/{value2}")
public String dynamic(@PathVariable("value1") String name1, @PathVariable("value2") String name2){
System.out.println(name1);
System.out.println(name2);
return "main";
}
<h1>动态占位符提交</h1>
<a href="${pageContext.request.contextPath}/dynamic/张三/李四.action">动态占位符提交</a>
可以使用同一个URL,但是请求方式不同,进入不同的处理:
http://localhost:8080/add/1/2 get
http://localhost:8080/add/1/2 post
表面上是同一个url,但是根据请求方式的不同,可以请求到不同的方法
@GETMapping("/add")
public String test1(@PathVariable("a") int a,@PathVariable("b") int b){
return "login"
}
@POSTMapping("/add")
public String test1(@PathVariable("a") int a,@PathVariable("b") int b){
return "login"
}
当提交请求参数与action方法的形参的名称不一样,使用注解@RequestParam
@RequestMapping("param")
public String param(@RequestParam("name") String name1,@RequestParam("age") int age1){
System.out.println(name1);
System.out.println(age1);
return "main";
}
<h1>解决请求参数跟接收参数不一样</h1>
<form method="get" action="${pageContext.request.contextPath}/param.action">
<input type="text" name="name"><br>
<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
中文乱码问题
以上两种数据提交的方式,post请求都会有中文乱码的问题,那么为什么get请求没有?谁处理的?
tomcat8以后的版本tomcat自己解决了get请求中文乱码的问题,tomcat10已经解决了post请求中文乱码
解决办法
<!-- 中文编码过滤器,拦截全部请求,强制转码-->
<filter>
<filter-name>encode</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encode</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
/ /* /**
<url-pattern>/</url-pattern>
跟 <url-pattern>/*</url-pattern>
的区别
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
要去掉*
spring用到forward("/WEB-INF/jsp/*.jsp")
而forward当然是又要经过web.xml的映射的,
然后,在URL匹配时,
<url-pattern>/</url-pattern> 不会匹配到*.jsp,不会进入spring的DispatcherServlet类
<url-pattern>/*</url-pattern> 会匹配*.jsp,导致进入spring的DispatcherServlet类,然后去寻找controller,接着找不到对应的controller所以报错。
2.访问的路径若是有错误,也会提示这个.
3.dispatcher-servlet.xml配置错误
/不会拦截页面,只会拦截路径
- 当请求为/user/login的时候可以被拦截,当为/page/login.html页面的时候不拦截
/* 和 /*
-
/* 是拦截所有的文件夹,不包含子文件夹
-
/** 是拦截所有的文件夹及里面的子文件夹
- 例如请求/usr/add/a,/*不会进行拦截,/*会拦截,
- 而请求/usr/add,/*会进行拦截,/**也会拦截
-
相当于/*只有后面一级
-
/** 可以包含多级
/**可以拦截,其他的拦截不了
<a href="${pageContext.request.contextPath}/jsp2/jsp3">demo</a>
@RequestMapping("/jsp2/jsp3")
public String gojsp(){
return "/jsp2/jsp3/a";
}
/**跟/*可以拦截,其他的拦截不了
<a href="${pageContext.request.contextPath}/jsp2">demo</a>
@RequestMapping("/jsp2")
public String gojsp(){
return "/jsp2/jsp3/a";
}
v
json
服务器返回json格式的数据
@RequestMapping("/demo2")
@ResponseBody
public String demo2() throws ParseException, JsonProcessingException {
String s = "zs"
ObjectMapper objectMapper = new ObjectMapper();
String str = objectMapper.writeValueAsString(s);
return str;
}
如果含有日期:
@RequestMapping("/demo2")
@ResponseBody
public String demo2() throws ParseException, JsonProcessingException {
String date = "2000-10-2";
Date parse = new SimpleDateFormat("yyyy-MM-dd").parse(date);
ObjectMapper objectMapper = new ObjectMapper();
String str = objectMapper.writeValueAsString(parse);
return str;
}
使用ajax发送请求
index.jsp使用ajax发送请求,需要jquery库
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="js/jquery-3.6.0.min.js"></script>
</head>
<body>
<a href="javascript:showStu()">访问服务器返回学生集合</a>
<div id="mydiv">
等待服务器返回数据
</div>
<script>
function showStu(){
$.ajax({
url:"${pageContext.request.contextPath}/list.action",
type:"get",
dataType:"json",
success:function (stuList){
var s = "";
$.each(stuList,function (i,stu){
s+=stu.name + "----" + stu.age +"<br>";
});
$("#mydiv").html(s);
}
});
}
</script>
</body>
</html>
配置web.xml:中午过滤器Filter,请求转发DispatcherServlet,导入springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<filter>
<filter-name>encode</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encode</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>
配置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 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.wzw.controller"/>
<!-- 添加注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
bean对象
public class Student {
private String name;
private int age;
//此处省略set,get,tostring,构造器方法不写
}
action方法
@ResponseBody
返回字符串,不走视图解析器
@Controller
public class StudentListAction {
@RequestMapping("/list")
@ResponseBody//必须要在springmvc.xml钟添加注解驱动
public List<Student> list(){
List<Student> list = new ArrayList<>();
Student student = new Student("张三", 12);
Student student1 = new Student("张三1", 12);
Student student2 = new Student("张三2", 12);
list.add(student);
list.add(student1);
list.add(student2);
return list;//springmvc框架负责讲集合转为json数组
}
}
配置pom.xml文件
引入jackson依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
请求转发和重定向
<a href="${pageContext.request.contextPath}/one.action">请求转发页面(默认)</a><br><br>
<a href="${pageContext.request.contextPath}/two.action">请求转发action</a><br><br>
<a href="${pageContext.request.contextPath}/three.action">重定向页面</a><br><br>
<a href="${pageContext.request.contextPath}/four.action">重定向action</a><br><br>
<a href="${pageContext.request.contextPath}/five.action">重定向去login</a><br><br>
<a href="${pageContext.request.contextPath}/six.action">请求转发去login</a><br><br>
@Controller
public class JumpAction {
@RequestMapping("/one")
public String one(){
System.out.println("one.action");
return "main";//默认使用视图解析器拼接前缀后缀进行页面跳转
}
@RequestMapping("/two")
public String two(){
System.out.println("two.action");
//forward: 这组字符串可以屏蔽前缀跟后缀的拼接
return "forward:/other.action";//默认使用视图解析器拼接前缀后缀进行页面跳转
}
@RequestMapping("/other")
public String other(){
System.out.println("other.action");
return "main";//默认使用视图解析器拼接前缀后缀进行页面跳转
}
@RequestMapping("three")
public String three(){
System.out.println("three.action");
//redirect: 这组字符串可以屏蔽前缀跟后缀的拼接,实现重定向跳转
return "redirect:/admin/main.jsp";
}
@RequestMapping("four")
public String four(){
System.out.println("three.action");
//redirect: 这组字符串可以屏蔽前缀跟后缀的拼接,实现重定向跳转
return "redirect:/other.action";
}
@RequestMapping("/five")
public String five(){
return "redirect:/fore/login.jsp";
}
@RequestMapping("/six")
public String six(){
return "forward:/fore/login.jsp";
}
}
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/admin/"/>
<property name="suffix" value=".jsp"/>
</bean>
视图解析器:
-
使用视图解析器进行拼串,如果返回的是String,例如:
-
@RequestMapping("/one") public String one(){ System.out.println("one.action"); return "main";//默认使用视图解析器拼接前缀后缀进行页面跳转 }
-
没有使用
forward
跟Redirect
,就自动拼串:/admin/main.jsp -
适用于很多访问同一个页面的时候,可以这么做
-
-
返回结果带有
forward
跟Redirect
-
就不会自动拼串,就根据提供的字符串判断是转发还是重定向
-
@RequestMapping("three") public String three(){ System.out.println("three.action"); //redirect: 这组字符串可以屏蔽前缀跟后缀的拼接,实现重定向跳转 return "redirect:/admin/main.jsp"; } @RequestMapping("four") public String four(){ System.out.println("three.action"); //redirect: 这组字符串可以屏蔽前缀跟后缀的拼接,实现重定向跳转 return "redirect:/other.action"; } @RequestMapping("/five") public String five(){ return "redirect:/fore/login.jsp"; } @RequestMapping("/six") public String six(){ return "forward:/fore/login.jsp"; }
-
为什么带有forward跟redirect就不会自动拼串?
InternalResourceViewResolver
继承了 UrlBasedViewResolver
public class UrlBasedViewResolver extends AbstractCachingViewResolver implements Ordered {
public static final String REDIRECT_URL_PREFIX = "redirect:";
public static final String FORWARD_URL_PREFIX = "forward:";
public class InternalResourceViewResolver extends UrlBasedViewResolver {
数据传输
SpringMVC默认支持的内置对象:
-
HttpServletRequest 对象
-
HttpServletResponse 对象
-
HttpSession 对象
-
Model/ModelMap 对象
-
Map<String,Object> 对象
注意
Model,Map,ModelMap都使用的是request请求作用域,意味着只能是请求转发后,页面才可以得到值。
<a href="${pageContext.request.contextPath}/data.action?name=李四">携带数据请求</a>
<a href="${pageContext.request.contextPath}/data2.action?name=李四">携带数据请求,重定向</a>
@RequestMapping("/data")
public String data(HttpServletRequest request,
HttpServletResponse response,
HttpSession session,
Map map,
Model model,
ModelMap modelMap){
String name = "张三";
request.setAttribute("requestName",name);
session.setAttribute("sessionName",name);
map.put("mapName",name);
model.addAttribute("modelName",name);
modelMap.addAttribute("modelMapName",name);
return "forward:/fore/login.jsp";
}
@RequestMapping("/data2")
public String data2(HttpServletRequest request,
HttpServletResponse response,
HttpSession session,
Map map,
Model model,
ModelMap modelMap){
String name = "张三";
request.setAttribute("requestName",name);
session.setAttribute("sessionName",name);
map.put("mapName",name);
model.addAttribute("modelName",name);
modelMap.addAttribute("modelMapName",name);
return "redirect:/fore/login.jsp";
}
<h1>requestName:${requestName}</h1>
<h1>sessionName:${sessionName}</h1>
<h1>mapName:${mapName}</h1>
<h1>modelName:${modelName}</h1>
<h1>modelMapName:${modelMapName}</h1>
<h1>携带数据:${param.name}</h1>
日期处理
日期类型不能自动注入到方法的参数中。需要单独做转换处理。使用@DateTimeFormat注解,需要在springmvc.xml文件中添加<mvc:annotation-driven/>
标签。
因为form表单提交过来的日期是String类型的,需要将String转化成Date
单个类中处理
<form method="get" action="${pageContext.request.contextPath}/mydate.action">
日期:<input type="date" name="mydate"><br><br>
<input value="提交" type="submit">
</form>
@RequestMapping("/mydate")
public String mydate(@DateTimeFormat(pattern = "yyyy-MM-dd")@RequestParam("mydate") Date date, HttpServletRequest request){
request.setAttribute("date",date);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String s = simpleDateFormat.format(date);
request.setAttribute("date2",s);
System.out.println(s);
System.out.println(date);
return "forward:/admin/show.jsp";
}
//2022-11-10
//Thu Nov 10 00:00:00 CST 2022
在这个例子中,参数的选择有两个
- 因为form表单中的name是mydate
- 所以可以采用
@RequestParam("mydate")
来接收数据,且定义自己的参数名 - 也可以采用
(@DateTimeFormat(pattern = "yyyy-MM-dd") Date mydate)
自动接收
- 所以可以采用
@DateTimeFormat(pattern = "yyyy-MM-dd")
表示:
-
String类型的date数据提交过来转化成"yyyy-MM-dd"类型的date,但是好像没啥用,最后还是要通过
SimpleDateFormat
来进行转换 -
@DateTimeFormat(pattern = "yyyy-MM-dd")
单单用来接收date类型的数据,页面提交的是date数据,比如:<input type="date">
类中全局处理
<form method="get" action="${pageContext.request.contextPath}/mydate.action">
日期:<input type="date" name="mydate"><br><br>
<input value="提交" type="submit">
</form>
//注册一个全局的日期处理注解,
// registerCustomEditor 注册自定义编辑器
// CustomDateEditor 自定义日期编辑器
// 将Date类转化成SimpleDateFormat("yyyy-MM-dd")类型,可以为空,等于说代替了@DateTimeFormat(pattern = "yyyy-MM-dd")这个注解
@InitBinder
public void initBinder(WebDataBinder dateBinder){
dateBinder.registerCustomEditor(Date.class,new CustomDateEditor(sf,true));
}
@RequestMapping("/mydate")
public String mydate(@RequestParam("mydate") Date date, HttpServletRequest request){
request.setAttribute("date",date);
String s = sf.format(date);
request.setAttribute("date2",s);
System.out.println(s);
System.out.println(date);
return "forward:/admin/show.jsp";
//http://localhost:8080/springmvc_04/mydate.action?mydate=2022-11-03
}
注册一个全局的日期处理注解 @InitBinder
registerCustomEditor
注册自定义编辑器
CustomDateEditor
自定义日期编辑器
将Date类转化成SimpleDateFormat("yyyy-MM-dd")
类型,可以为空,等于说代替了@DateTimeFormat(pattern = "yyyy-MM-dd")
这个注解,且这个是全局的整个类都可以用到
数据提交过来的时候直接就是已经转化好了的:http://localhost:8080/springmvc_04/mydate.action?mydate=2022-11-03
,虽然单个的也是
案例
<form action="${pageContext.request.contextPath}/list.action">
姓名:<input type="text" name="name"><br>
生日:<input type="date" name="birthday"><br>
<input type="submit" value="提交">
</form>
@Controller
public class StudentAction {
@RequestMapping("/list")
public String student(String name,String birthday,HttpServletRequest request) throws ParseException {
Student student = new Student(name, new SimpleDateFormat("yyyy-MM-dd").parse(birthday));
List<Student> list = new ArrayList<>();
list.add(student);
request.setAttribute("list",list);
return "show";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--jstl格式化标签库--%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>单个日期显示</h1>
<h1>date:${date}</h1>
<h1>date2:${date2}</h1>
<hr>
<c:forEach items="${list}" var="item">
${item.name}
<br>
${item.birthday}---<fmt:formatDate value="${item.birthday}" pattern="yyyy-MM-dd"/>
</c:forEach>
</body>
</html>
mvc:annotation-driven/
mvc:annotation-driven/会自动注册两个bean,分别为DefaultAnnotationHandlerMapping
和AnnotationMethodHandlerAdapter
。
是springmvc为@controller分发请求所必须的。除了注册了这两个bean,还提供了很多支持。
1)支持使用ConversionService 实例对表单参数进行类型转换;
2)支持使用 @NumberFormat 、@DateTimeFormat;
3)注解完成数据类型的格式化;
4)支持使用 @RequestBody 和 @ResponseBody 注解;
5)静态资源的分流也使用这个标签;
拦截器
拦截器是AOP思想的具体实现
SpringMVC 中的 Interceptor 拦截器,它的主要作用是拦截指定的用户请求,并进行相应的预处理与后处理。
其拦截的时间点在“处理器映射器根据用户提交的请求映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器,在处理器适配器执行处理器之前”。
当然,在处理器映射器映射出所要执行的处理器类时,已经将拦截器与处理器组合为了一个处理器执行链,并返回给了中央调度器。
拦截器执行的时机
-
preHandle()
:在请求被处理之前进行操作,预处理 -
postHandle()
: 在请求被处理之后,但结果还没有渲染前进行 -
afterCompletion()
: 所有的请求相应结束后执行善后工作,清理对象,关闭资源,最终处理
拦截器实现的两种方式
- 继承
HandlerInterceptorAdapter
的父类 - 实现
HandlerInterceptor
接口
public class myInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("=======");
HttpSession session = request.getSession(false);
User user = (User)session.getAttribute("user");
System.out.println(user);
if (user !=null){
return true;
}
if (request.getRequestURI().contains("login")){
return true;
}
request.getRequestDispatcher("/index.jsp").forward(request,response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
拦截器配置
<!-- 注册拦截器驱动-->
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截路径
拦截这个请求下的所有请求
-->
<mvc:mapping path="/**"/>
<!-- 放行请求-->
<mvc:exclude-mapping path="/login"/>
<!-- <mvc:exclude-mapping path="/success"/>-->
<mvc:exclude-mapping path="/judgeLogin"/>
<!-- 配置具体的拦截器实现功能的类-->
<bean class="com.wzw.login.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
案例
这个案例写的不太好
bean
public class User {
private String name;
private String password;
controller
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public String login(String name, String password, HttpSession session){
System.out.println(name +","+password);
if (!("张三".equals(name) && "123".equals(password))){
return "redirect:/index.jsp";
}
User user = new User("张三", "123");
session.setAttribute("user",user);
return "main";
}
@RequestMapping("/goLogin")
public String goLogin(){
return "login";
}
@RequestMapping("/goMain")
public String goMain(){
return "main";
}
}
HandlerInterceptor
public class myInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("=======");
HttpSession session = request.getSession(false);
User user = (User)session.getAttribute("user");
System.out.println(user);
if (user !=null){
return true;
}
if (request.getRequestURI().contains("login")){
return true;
}
request.getRequestDispatcher("/index.jsp").forward(request,response);
return false;
}
springmvc.xml
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<!-- <mvc:exclude-mapping path="/user/login"/>-->
<mvc:exclude-mapping path="/user/goLogin"/>
<bean class="com.wzw.Interceptor.myInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/user/goLogin">登录页面</a>
<a href="${pageContext.request.contextPath}/user/goMain">首页</a>
</body>
</html>
jsp/login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form method="post" action="${pageContext.request.contextPath}/user/login">
用户名:<input type="text" name="name"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
文件上传下载
文件上传是项目开发中最常见的功能之一 ,springMVC 可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。
如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver。
前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。
只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器;
对表单中的 enctype 属性做个详细的说明:
- application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
- multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
- text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。
<form action="" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit">
</form>
一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。
文件上传
<!--文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<!--servlet-api导入高版本的-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
配置bean:multipartResolver
<!--文件上传配置-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 上传文件大小上限,单位为字节(10485760=10M) -->
<property name="maxUploadSize" value="10485760"/>
<property name="maxInMemorySize" value="40960"/>
</bean>
CommonsMultipartFile 的 常用方法:
- String getOriginalFilename():获取上传文件的原名
- InputStream getInputStream():获取文件流
- void transferTo(File dest):将上传文件保存到一个目录文件中
案例
html
<form action="/upload" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit" value="upload">
</form>
controller
方法1
@Controller
public class FileController {
//@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";
}
}
方法2
采用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";
}
文件下载
- 设置 response 响应头
- 读取文件 — InputStream
- 写出文件 — OutputStream
- 执行操作
- 关闭流 (先开后关)
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response ,HttpServletRequest request) throws Exception{
//要下载的图片地址
String path = request.getServletContext().getRealPath("/upload");
String fileName = "基础语法.jpg";
//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;
}
html
<a href="/download">点击下载</a>
注解总结
@RequestMapping 请求注解,用于指定请求路径
-
@RequestMapping(value = "/demo",method = RequestMethod.POST)
-
子注解
-
@GetMapping
-
@PostMapping
-
@PutMapping
-
@DeleteMapping
-
@PatchMapping
@RequestParam 参数注解,用于接收请求参数,参数名称不同的时候可以指定参数名称
-
public String submit(@RequestParam("myname") String name,int age)
@ResponseBody 返回字符串,不走视图解析器,用于返回json数据
-
@ResponseBody
@InitBinder 全局注解,可以用到处理日期
-
@InitBinder public void initBinder(WebDataBinder dateBinder){ dateBinder.registerCustomEditor(Date.class,new CustomDateEditor(sf,true)); }
@ResponseBody 用在类上,意思是该类中所有的方法返回值都是以json格式返回给客户端
ervletRequest request) throws Exception{
//要下载的图片地址
String path = request.getServletContext().getRealPath(“/upload”);
String fileName = “基础语法.jpg”;
//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;
}
html
```html
<a href="/download">点击下载</a>
注解总结
@RequestMapping 请求注解,用于指定请求路径
-
@RequestMapping(value = "/demo",method = RequestMethod.POST)
-
子注解
-
@GetMapping
-
@PostMapping
-
@PutMapping
-
@DeleteMapping
-
@PatchMapping
@RequestParam 参数注解,用于接收请求参数,参数名称不同的时候可以指定参数名称
-
public String submit(@RequestParam("myname") String name,int age)
@ResponseBody 返回字符串,不走视图解析器,用于返回json数据
-
@ResponseBody
@InitBinder 全局注解,可以用到处理日期
-
@InitBinder public void initBinder(WebDataBinder dateBinder){ dateBinder.registerCustomEditor(Date.class,new CustomDateEditor(sf,true)); }
@ResponseBody 用在类上,意思是该类中所有的方法返回值都是以json格式返回给客户端