SpringMVC
一.简介
1.框架特征
MVC框架mvc是一种设计模式
- 天生就与Spring整合
- 提供了大量的注解来代替原有的配置文件
- 提供了非常简单的web支持
- 提供了大量的扩展点
m:model:模型 vo对象 java代码
v:view :视图 html,jsp
c:controller 控制器 controller servlet
控制器根据用户请求决定调用哪个模型去处理请求,然后决定调用哪个视图来显示模型处理的返回数据
2.运行机制
客户端发送请求到达*控制器DispatcherServlet由*控制器负责解析整个SpringMVC的运行流程
当*控制器接收到请求之后,会将请求先交给HandlerMapping进行处理
HandlerMapping负责解析整个SpringMVC的业务流程,将请求分发给合适的处理器
处理完成之后将处理的结果返回给*控制器
*控制器将处理结果交给HandlerAdapter
由HandlerAdapter负责适配不同类型的处理器,找到对应的Handler进行处理
Handler负责处理核心业务逻辑并返回响应的视图与模型
将响应结果封装成一个对象,ModelAndView
ModelAndView负责处理模型与视图
最终会将ModelAndView交给*控制器
*控制器将结果交给ViewResolver
ViewResolver负责解析响应结果,将其解析为具体的视图技术
根据解析结果找到View,由View使用具体的视图技术来进行具体的实现
最终将处理完成的结果返回给*控制器
由*控制器将结果返回给客户端
最终在客户端生成对应的效果
二. 第一个SpringMVC程序
1.环境搭建
1-1 POM配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itany.springmvc</groupId>
<artifactId>springmvc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<spring.version>5.2.5.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- J2EE 环境依赖 begin -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- J2EE 环境依赖 end -->
</dependencies>
</project>
1-2 web.xml配置
在web.xml中对*控制器进行配置*控制器DispatcherServlet是一个Servlet
<!-- 配置*控制器 -->
<servlet>
<!--
此处的servlet-name理论上可以随便写
但是此处的值是用于控制SpringMVC默认的配置文件的名字的
假设此处的值是xxx,则其配置文件的文件名就叫做:xxx-servlet.xml
默认情况下,读取的配置文件是存放在WEB-INF根目录下的
-->
<servlet-name>controller</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</init-param>
<!--希望在容器启动的时候创建servlet,并且创建spring容器-->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>controller</servlet-name>
<url-pattern>*.do
</url-pattern>
</servlet-mapping>
2.xml配置实现
1.导入依赖
2.创建controller 新建一个普通类,实现ctroller接口,重写方法
3.spring配置文件中配置一个handlerMapping,HandlerAdapter
BeanNameUrlHandlerMapping SimpleControllerHandlerAdapter
4.配置handler
5.配置视图解析器
<!--1.处理器映射器,作用就是根据url找到对应的处理器 /hello -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
<!--2.处理器适配器 作用就是调用处理器的方法 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- 3.视图解析器
获取modelAndView的数据
获取视图名 比如说success
根据视图名去解析出视图 /WEB-INF/pages/success.htm
把数据渲染到视图
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".html"/>
</bean>
<!-- 配置控制器 -->
<bean id="/hello" class="com.controller.HelloController"></bean>
3.注解实现
3-1 配置文件
<!-- 开启注解版的springmvc,帮我们配置了SpringMVC的基本固定配置项,包含HandlerMapping与HandlerAdapter -->
<mvc:annotation-driven/>
<!-- 扫包,扫描指定包下的注解 -->
<context:component-scan base-package="controller"/>
<!-- 配置viewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 配置前缀 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 配置后缀 -->
<property name="suffix" value=".html"/>
</bean>
3-2 常用注解
-
@Controller
- 标注在类上
- 表示当前类是一个由Spring所管理的bean
- 该注解表示当前bean是一个Controller
- 其用法类似于普通bean
@Component
-
@RequestMapping
- 该注解可以标注在方法上,也可以标注在类上,也可以都标注
- 表示访问对应的资源所需要的命令,其value属性用法类似于Servlet的url-pattern
-
如果标注在类上,表示访问该类所需要的的命令
- 如果只在类上进行标注,此时无法直接访问
- 但是可以对类中的方法的请求进行限定
- 每一个请求只能对应一个方法,如果超过一个则报错
-
如果标注在方法上,表示访问该方法所需要的命令
- 如果只在方法上进行标注,则可以直接访问
- 如果类上与方法上同时存在,必须先访问类,后访问方法
-
value属性
- 配置访问资源所需要的命令
- 其值可以是字符串,也可以是字符串数组
- 如果是一个字符串数组,数组中每一个元素都是该资源的访问命令
-
如果值是一个字符串,且当前注解中有且仅有value属性
- 此时关键字
value=
可以省略
- 此时关键字
@Controller
//@RequestMapping(value="/hello")
@RequestMapping("/hello")
public class HelloAnnotation {
@RequestMapping(value={"/f1"})
public ModelAndView f1(){
ModelAndView mav = new ModelAndView();
mav.setViewName("hello");
mav.addObject("msg","Hello Annotation");
return mav;
}
}
三.响应配置
1.直接访问视图
<!--
配置直接访问视图
path属性:访问命令
view-name属性:访问的视图名
该视图名同样经过viewResolver处理
最终访问的视图为:prefix+view-name+suffix
-->
<mvc:view-controller path="/showLogin" view-name="login"/>
2.Handler方法返回值类型
-
ModelAndView
- 返回模型与视图
- 可以两者均返回,也可以只返回其中任意一种
-
String
- 使用字符串作为返回值的时候,其返回值的格式有三种情况
-
直接写字符串
- 返回的是视图名
- 将当前的方法的返回值作为视图名来处理
- 会经过viewResolver处理
-
redirect:命令
- 使用重定向的方式访问指定的命令
- 不和视图解析器一起工作。
- 框架对重定向的操作:框架会把modelAndView中的简单类型的数据转为字符串,作为目标页面的get请求参数使用。目的是在两次请求中传递参数。重定向不能访问web-inf资源。
-
forward:命令
- 使用转发的方式访问指定的命令
- 不和视图解析器一起工作。
@RequestMapping(value = "/forwardTest")
public ModelAndView handle4(@RequestParam("name") String name){
ModelAndView mv = new ModelAndView();
mv.addObject("msg",name);
mv.setViewName("forward:/WEB-INF/pages/success.html");
return mv;}
- void(少见)
如果方法中有响应对象参数,返回响应可以用这个参数处理
如果方法中没有相应参数,则会根据请求url作为viewName通过视图解析器处理响应。
-
@ResponseBody Object
- 当方法或者方法的返回值类型被
@ResponseBody
进行标注的时候 - 表示当前方法的返回值是作为数据模型返回
- 不会返回视图,只返回数据模型
- 一般用于Ajax操作
-
如果当前Controller中所有的方法都是返回数据模型的时候
- 可以在当前类上使用
@RestController
代替原有的@Controller
- 当类使用
@RestController
进行标注之后 - 其效果相当于为当前类中的每一个方法都自动添加
@ResponseBody
注解
- 可以在当前类上使用
- 当方法或者方法的返回值类型被
//@Controller
@RestController
@RequestMapping("/resp")
public class ResponseController {
@RequestMapping("/f1")
public String f1(){
return "login";
}
@RequestMapping("/f2")
public ModelAndView f2(){
ModelAndView mav = new ModelAndView();
mav.setViewName("login");
return mav;
}
@RequestMapping("/f3")
public void f3(){
System.out.println("ResponseController.f3");
String s = "/WEB-INF/pages/"+"resp/f3"+".html";
}
@RequestMapping("/f4")
public void f4(HttpServletResponse response) throws IOException {
System.out.println("ResponseController.f4");
PrintWriter out = response.getWriter();
out.print("<h1 style='color:green;'>Hello World</h1>");
}
@ResponseBody
@RequestMapping("/f5")
public String f5(){
return "<h1>hello</h1>";
}
}
3.POST请求字符编码过滤器
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4.访问静态资源
默认情况下,如果*处理器设置拦截的url-pattern是/;所有的SpringMVC工程均无法直接访问静态资源静态资源:HTML、CSS、JavaScript、image...
必须通过配置实现访问静态资源的目的
有两种方式进行配置
-
方式
- 配置访问非WEB-INF下的资源
<mvc:default-servlet-handler/>
练习:pages目录下添加login.html controller中添加控制器类,处理登陆请求
四.请求配置
1.路径的配置
1.1.基本配置
使用@RequestMapping(value="url")
进行直接配置该注解表示配置访问命令,可以配置在类上,也可以配置在方法上
1.2.ANT风格配置(使用通配符)
通配符配置
-
*
- 匹配一层路径
-
**
- 匹配0或多层路径
-
?
- 只匹配一个字符
- 可以与其他字符联合使用
@RequestMapping("/f1/*")
public void f1(){
System.out.println("RequestController.f1");
}
@RequestMapping("/f2/**")
public void f2(){
System.out.println("RequestController.f2");
}
@RequestMapping("/f3/a?")
public void f3(){
System.out.println("RequestController.f3");
}
1.3.REST风格配置
查询: /user GET
删除: /user DELETE
新建: /user POST
更新: /user PUT
-
{xxx}
- 表示一层路径
- 该方式必须与
@PathVariable("xxx")
联合使用 - 该注解标注在方法的参数前,表示将路径中的
xxx
与方法的参数进行绑定 - 当用户输入访问命令的时候,
xxx
部分的值即为当前参数的值
- 当注解中
xxx
的值与变量名一致的时候,注解的参数可以省略 - REST配置在一个请求中可以配置无数个
@RequestMapping("/f4/{name}")
public void f4(@PathVariable String name){
System.out.println("RequestController.f4,name:"+name);
}
@RequestMapping("/f5/{aaa}/{password}")
public void f5(@PathVariable("aaa") String username,@PathVariable String password){
System.out.println("username:"+username+",password:"+password);
}
@RequestMapping("/f6/{id}")
public void f6(@PathVariable Integer id){
System.out.println("id:"+id);
}
2.其他配置
2.2 可以限制请求提交的方式
- 可以通过两种方式进行设置
-
方式一
- 通过
@RequestMapping
的method属性指定当前请求的类型 - 其值是一个枚举类型,通过
RequestMethod
进行获取 -
请求方式一共八种
- GET
- POST
- PUT
- DELETE
- HEAD
- PTACH
- OPTIONS
- TRACE
- 通过
-
方式二
- 直接通过使用
@XXXMapping
代替原有的@RequestMapping
注解 -
XXX
的值即为请求方式 - 例如:
@PostMapping
表示使用post请求进行访问
- 直接通过使用
@RequestMapping(value = "/f9",method = RequestMethod.POST)
public void f9(){
System.out.println("RequestController.f9");
}
@PostMapping("/f10")
public void f10(){
System.out.println("RequestController.f10");
}
五.参数配置
1.参数种类
1.1原生servlet对象
request/response/session
- 这些都可以直接作为方法的参数使用
application
不能直接作为方法的参数
InputStream/OutputStream
Reader/Writer/PrintWriter
1.2简单类型
@RequestParam(key)
- 该注解标注在方法的参数前
- 表示自动将请求中的参数的数据装配到对应的方法的参数中
- 通过注解的key与请求中数据的key进行匹配
- 当两者一致时,进行装配
- 当方法的参数名与注解的key相同的时候,key可以省略
-
当方法的参数不是一个对象的时候,且方法的参数名与注解的key相同
- 此时注解可以省略
- 当注解被省略之后,如果请求中没有传递指定的参数,则参数的值为null
- 如果注解没有被省略,请求中没有传递指定的参数,则报错,400
-
默认情况下
- 当Handler方法的参数没有被任何注解进行标注
- 且方法的参数是一个普通类型(非对象)
- 且方法的参数有值的时候
- 默认该参数自动被
@RequestParam("参数名")
进行标注
1.3pojo对象类型 Plain Ordinary java object
参数可以是对象类型
- 在获取请求的时候
- 会自动解析请求中所有的数据以及方法对象中所有的属性
- 将请求中数据的key与对象中属性的名字进行匹配
- 如果请求中数据的key与对象属性的名字相同,则进行数据的装配
- 将请求中的value注入给对象的属性值中
1.5Model/Map/ModelMap
都会被封装成BindingAwareModelMap类型
- 这三个参数用法一致,其中ModelMap就是ModelAndView中的模型部分
- 这三个参数的用法类似于
request
作用域 - 用于存储数据模型
@PathVariable("xxx")
- 详情请参考REST风格的请求配置
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/f1")
public void f1(HttpServletRequest request, HttpServletResponse response, HttpSession session){
System.out.println("ParamController.f1");
ServletContext application = session.getServletContext();
System.out.println("application:"+application);
}
@RequestMapping("/f2")
public void f2(ServletContext application){
System.out.println("ParamController.f2");
}
@RequestMapping("/f3")
public void f3(InputStream in, OutputStream out){
System.out.println("ParamController.f3");
}
@RequestMapping("/f4")
public void f4(Reader reader, Writer writer, PrintWriter out){
System.out.println("ParamController.f4");
}
@RequestMapping("/f5")
public String f5(Model model, Map map, ModelMap modelMap){
model.addAttribute("model","Hello Model");
map.put("map","Hello Map");
// ModelMap即可以使用model的方法,也可以使用map的方法
// 相当于两者的结合
modelMap.addAttribute("modelMap1","Hello ModelMap-Model");
modelMap.put("modelMap2","Hello ModelMap-Map");
// return "model";
return "redirect:/showModel";
}
@RequestMapping("/f6")
public String f6(Model model){
model.addAttribute("msg","Hello");
// return "model";
return "redirect:/showModel";
}
@RequestMapping("/f7")
public void f7(String username){
System.out.println("username:"+username);
}
@RequestMapping("/f8")
public void f8(@RequestParam("username") String username){
System.out.println("username:"+username);
}
@RequestMapping("/f9")
public void f9(@Value("admin") String name){
System.out.println("name:"+name);
}
}
七.异常处理
1.全局异常处理
2-1 通过@ControllerAdvice
来处理
定义一个Java类,该类用于处理全局异常该类使用
@ControllerAdvice
进行标注表示当前类是一个全局异常处理类
<context:component-scan base-package="global"/>
@ControllerAdvice
public class GlobalException {
@ExceptionHandler
public ModelAndView exceptionHandler(Exception e){
return new ModelAndView("exception","msg","服务器内部异常");
}
}
八.拦截器
在处理Handler业务方法之前或者之后以及响应到达之前做一些额外的处理可以将一些相关的操作封装成拦截器,进行额外的处理
1.开发步骤
1-1 创建一个Java类
该类实现HandlerInterceptor接口不同Spring版本需求也不一样
-
在Spring5中已经对这些方法做了默认的实现
- 因此,可以根据需求选择性的重写部分的方法
-
在Spring4中并没有对这些方法做任何的实现
- 因此,必须重写所有的方法
public class LogHandlerInterceptor implements HandlerInterceptor {
/**
* 在处理核心业务逻辑之前执行
* @param request 请求对象
* @param response 响应对象
* @param handler Handler对象
* @return 是否放行,true-放行,false-不放行
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("LogHandlerInterceptor.preHandle");
return false;
}
/**
* 在处理核心业务逻辑之后执行
* @param request
* @param response
* @param handler
* @param modelAndView 目标Handler的视图与模型
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("LogHandlerInterceptor.postHandle");
}
/**
* 在响应到达之前执行
* @param request
* @param response
* @param handler
* @param e 遇到的异常
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) throws Exception {
System.out.println("LogHandlerInterceptor.afterCompletion");
}
}
1-2 配置拦截器
在SpringMVC配置文件中进行配置
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截谁,哪些请求会进入拦截器进行处理,支持通配符 -->
<mvc:mapping path="/log/**"/>
<!--
不拦截谁,哪些请求不仅如此拦截器进行处理
此处配置的请求一般是属于存在mapping配置的请求中的
可以配置多个
-->
<mvc:exclude-mapping path="/log/add"/>
<mvc:exclude-mapping path="/log/find"/>
<!-- 拦截器是谁 -->
<bean class="interceptor.LogHandlerInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
1-3 过滤器和拦截器的区别
过滤器是j2EE的规范,需要在web.xml中配置(或者注解实现),依赖于servlet-api,在进行servlet前后进行过滤。
拦截器需要在spring配置文件中中配置,依赖于spring框架,针对handler的拦截。
2.练习
实现登录检查拦截器当用户没有登录的时候,无法访问当前工程中的除登录相关之外的任意资源
此时不管访问任意资源,页面均会跳转到登录页面,提示:请先登录
只有用户登录之后,才能正常访问
public class CheckLoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
User user = (User) request.getSession().getAttribute("user");
if(user == null){
request.setAttribute("loginMsg","请先登录");
request.getRequestDispatcher("/showLogin").forward(request,response);
return false;
}
return true;
}
}
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/showLogin"/>
<mvc:exclude-mapping path="/resp/f8"/>
<bean class="interceptor.CheckLoginInterceptor"/>
</mvc:interceptor>
九.Ajax处理
1.返回普通字符串
在处理Ajax返回普通字符串的时候如果字符串的值是一个中文,则可能会出现乱码问题
此时可以通过
@RequestMapping
注解提供的produces
属性设置当前响应数据的格式其效果相当于
response.setContentType("属性值")
$(function(){
$("#username").blur(function(){
$.ajax({
type:"post",
url:"${pageContext.request.contextPath}/ajax/checkUsername",
data:{"username":$(this).val()},
success:function(result){
$("#s1").html(result);
}
});
});
});
@ResponseBody
@RequestMapping(value = "/checkUsername",produces = "text/html;charset=utf-8")
public String checkUsername(String username){
if("admin".equals(username)){
return "该用户已经被注册";
}
return "用户名可用";
}
2.返回JSON
2-1 POM依赖
<jackson.version>2.9.8</jackson.version>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
//导入这一个jar包
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
2-2 Controller
@RequestMapping("/findAll")
public ModelAndView findAll(){
ModelAndView mav = new ModelAndView();
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
String url = "jdbc:mysql://127.0.0.1:3306/ums?useUnicode=true&characterEncoding=utf-8";
List<User> users = new ArrayList<>();
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url,"root","");
String sql = new StringBuffer()
.append(" select id,username,password,phone,address ")
.append(" from t_user ")
.toString();
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while(rs.next()){
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
user.setPhone(rs.getString("phone"));
user.setAddress(rs.getString("address"));
users.add(user);
}
mav.setViewName("list");
mav.addObject("users",users);
} catch (Exception e) {
e.printStackTrace();
}
return mav;
}
@ResponseBody
@RequestMapping("/findById")
public User findById(Integer id){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
String url = "jdbc:mysql://127.0.0.1:3306/ums?useUnicode=true&characterEncoding=utf-8";
User user = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url,"root","");
String sql = new StringBuffer()
.append(" select id,username,password,phone,address ")
.append(" from t_user ")
.append(" where id = ? ")
.toString();
ps = conn.prepareStatement(sql);
ps.setInt(1,id);
rs = ps.executeQuery();
while(rs.next()){
user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
user.setPhone(rs.getString("phone"));
user.setAddress(rs.getString("address"));
}
} catch (Exception e) {
e.printStackTrace();
}
return user;
}
@ResponseBody
@RequestMapping("/findUser")
public User findUser(){
User user = new User();
user.setId(1);
user.setUsername("admin");
user.setPassword("123456");
user.setPhone("13812345678");
user.setAddress("江苏-南京");
user.setDate(new Date());
return user;
}
2-3 html
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>用户列表</title>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-1.9.1.min.js"></script>
<script>
$(function(){
$("li").mouseover(function(){
$("#d").show();
$.ajax({
type:"post",
url:"findById",
data:{"id":$(this).attr("data-id")},
dataType:"json",
success:function(user){
$("#s1").html(user.id);
$("#s2").html(user.username);
$("#s3").html(user.password);
$("#s4").html(user.phone);
$("#s5").html(user.address);
}
});
});
$("li").mouseout(function(){
$("#d").hide();
});
});
</script>
</head>
<body>
<ul>
</ul>
<hr/>
<div id="d" style="background-color: #dddddd;width: 20%;padding: 10px;display: none;">
编号:<span id="s1"></span><br/>
用户名:<span id="s2"></span><br/>
密码:<span id="s3"></span><br/>
电话:<span id="s4"></span><br/>
地址:<span id="s5"></span><br/>
</div>
</body>
</html>
2-4 User
private Integer id;
private String username;
// 处理的时候忽略指定的属性
@JsonIgnore
private String password;
private String phone;
private String address;
// JsonFormat用于格式化属性
// 一般用于处理日期格式
// 日期的值是以标准时间为基准
// 我们属于东八区
// 比标准时间快了8个小时
//注意:如果接收数据是json,两个都配上
@JsonFormat(pattern = "yyyy年MM月dd日 HH:mm:ss",timezone = "GMT+8")
//接收前台输入的日期格式的字符串转为日期对象
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date date;
十.文件处理
1.文件上传
使用servlet3上传的新功能,不需要导入第三方jar包
2.1web.xml配置:
<servlet>
<servlet-name>dispatchServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<!--在servlet上使用multipart-config 支持上传-->
<multipart-config/>
</servlet>
2.2 spring配置
<!--
文件上传
SpringMVC帮我们使用fileupload进行了解析
在它解析的过程中肯定会用到配置文件中bean的id
在底层代码中,对该bean的id做了限制
其id的值必须是multipartResolver
-->
<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"></bean>
2.3html
<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file"/>
<input type="submit" value="上传"/>
</form>
<img src="${url}" style="width: 200px;height: 200px">
ajax的方式:
<script>
$(function () {
$("#btn").click(function () {
var file = $("#file")[0].files[0];
var fdata = new FormData();
fdata.append("file",file);
console.log(fdata);
$.ajax({
url:"${pageContext.request.contextPath}/upload",
data:fdata,
type:"post",
processData:false,
contentType:false,
success:function (data) {
console.log(data);
// location.reload();
img.src = URL.createObjectURL(file);
}
});
});
});
</script>
2.4 java代码
@Controller
public class UploadController {
@RequestMapping("/upload")
public String upload(@RequestParam("file")MultipartFile file, HttpSession session) throws IOException {
String dir = new SimpleDateFormat("yyyyMMdd").format(new Date());
String path = session.getServletContext().getRealPath("/file/"+dir);
System.out.println(path);
File file1 = new File(path);
file1.mkdirs();
String fileName = file.getOriginalFilename();
file.transferTo(new File(file1,fileName));
session.setAttribute("url",session.getServletContext().getContextPath()+"/file/"+dir+"/"+fileName);
return "upload";
}
}
2.断点续传
- 在开始上传时,都去后台查询该文件是否已经传递过,返回已经完成传递的字节数,如果没有传递过,就返回0,开始上传时,就从返回的数字开始上传。
- 浏览器js端,调用自定义方法upload,每次只上传文件的固定大小数据,如1M,结束后再递归调用upload方法传下一个1M数据。
- 在后台用FileOutputStream的append继续到要写数据的位置。
前端代码
<input id="file" type="file" onchange="init()"/><br />
<span id="fileSize">显示已上传的大小</span>
<div id="process_border" style="width: 200px;height: 20px;background: gray;display: none">
<div id="process" style="width: 0px;height: 20px;background: green"></div>
</div>
const fileSize = 1024*1024;
function upload(start) {
let fileObj = $("#file")[0].files[0];
console.log("=======")
console.log(fileObj);
//
if(start >= fileObj.size){
$("#fileSize").html("上传成功!");
$("#file").val(null);
return;
}
let end = (start+fileSize) > fileObj.size ? fileObj.size : (start+fileSize);
let fd = new FormData();
fd.append("start",start);
//将文件切块上传
fd.append("f",fileObj.slice(start,end));
fd.append("filename",fileObj.name);
$.ajax({
url:"/ssm/upload2",
data:fd,
type:"post",
processData:false,
contentType:false,
success:function(){
$("#fileSize").html(start+"/"+fileObj.size);
$("#process").css("width",start*200/fileObj.size+"px");
upload(end);
}
});
}
function init() {
var fileObj = $("#file")[0].files[0];
//显示进度条
$("#process_border").css("display","block");
//获取文件已经上传的大小
$.ajax({
url:"/ssm/getSize",
data:{filename:fileObj.name},
success:function (data) {
console.log(data);
$("#fileSize").text(data+"/"+fileObj.size).css("color","blue");
upload(data);
}
});
}
java代码
@RequestMapping("/getSize")
public Long getSize(String filename, HttpSession session){
String dir = new SimpleDateFormat("yyyyMMdd").format(new Date());
String path = session.getServletContext().getRealPath("/upload/"+dir+"/"+filename);
File file = new File(path);
return file.exists() ? file.length() : 0;
}
@RequestMapping(value = "/upload2",method = RequestMethod.POST)
public void upload(MultipartFile f, String filename,HttpSession session,Long start) throws Exception {
String dir = new SimpleDateFormat("yyyyMMdd").format(new Date());
String path = session.getServletContext().getRealPath("/upload/"+dir);
System.out.println("===="+path);
File file = new File(path);
//创建子目录
file.mkdirs();
System.out.println("文件名:"+filename+"起始位置:"+start);
file = new File(path,filename);
FileOutputStream os = new FileOutputStream(file,true);
os.write(f.getBytes());
os.flush();
os.close();
}
3.文件下载
使用ResponseEntity实现
页面: <a href="download?filename=jquery-3.5.1.min.js">下载jquery</a>
//ResponseEntity是一种泛型类型。因此,我们可以使用任何类型作为响应主体:
@RequestMapping("/download")
public ResponseEntity<byte[]> download(HttpSession session,String filename) throws IOException {
String path = session.getServletContext().getRealPath("jquery/"+filename);
InputStream in=new FileInputStream(new File(path));
byte[] body=null;
body=new byte[in.available()];// 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数
in.read(body);//读入到输入流里面
// 设置头信息
// 使用MultiValueMap接口的实现类HttpHeaders设置响应头信息
HttpHeaders headers = new HttpHeaders();
// 参数一:表示当前所使用的MIME协议方式,即下载的方式
// 其值有两种
// 1.attachment:以附件的方式进行下载
// 2.inline:在线打开
// 参数二:下载时所显示的文件名
headers.setContentDispositionFormData("attachment",filename);
return new ResponseEntity<byte[]>(body,headers, HttpStatus.OK);
}
十一.SSM整合
步骤:
第一步:基础环境搭建(maven工程)
第二步:引入依赖
- Spring
- SpringMVC
- MyBatis
- mybatis-spring mybatis和spring的整合包
- MyBatis-Generator
- 数据库连接池 数据库驱动
第三步:编写整合配置文件
web.xml: *控制器,过滤器(乱码)
spring配置文件 :跟业务相关的配置,数据源,事务,mybatis的整合配置
springMVC配置文件:注解驱动,视图解析器,扫包,处理静态资源
mybatis配置文件
1.开发环境
-
开发工具
IntelliJ IDEA 2018.2 x64
- MySQL Server
-
涉及框架
- Spring
- SpringMVC
- MyBatis
- Maven
- MyBatis-Generator
3.配置文件
3-1 spring的配置文件
application.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:tx="http://www.springframework.org/schema/tx"
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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- Spring配置文件 这里主要配置业务逻辑有关的 数据源 事务配置等等 -->
<!-- 扫描组件 不扫描controller注解 -->
<context:component-scan base-package="com.gxh">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 引入外部文件 -->
<context:property-placeholder location="classpath:dataSource.properties"/>
<!--数据源配置 -->
<bean id="ds" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</bean>
<!-- mybatis整合配置 -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- mybatis的全局配置文件位置 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="dataSource" ref="ds"/>
<!-- 指定mybatis的mapper文件位置 -->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!-- 配置扫描器,将mybatis接口的代理实现加入到ioc容器中 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.gxh.dao"/>
</bean>
<!-- 事务控制 -->
<!-- 事务管理器的配置 -->
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="ds"/>
</bean>
<!-- 开启注解式事务 -->
<tx:annotation-driven transaction-manager="tx"/>
</beans>
3-2 web.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">
<!-- 1.配置spring监听器 服务一启动即加载spring配置文件 -->
<!-- contextConfigLocation:spring文件位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--2.springMVC的前端控制器 拦截所有请求 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- springmvc配置文件位置 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--3.字符编码过滤器 如果有多个过滤器,字符编码过滤器配置在最前面 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>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>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
3-3 springMVC的配置文件
applicationContext.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">
<!-- 1.springMVC配置文件,包含网站跳转逻辑 -->
<!-- 扫描 -->
<context:component-scan base-package="com.gxh" use-default-filters="false">
<!-- 只扫描控制器-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--2.配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 3.两个标准配置 -->
<!-- 处理静态资源 -->
<mvc:default-servlet-handler/>
<!-- 注解驱动 -->
<mvc:annotation-driven/>
</beans>
使用mybatis的逆向工程生成部分代码
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--数据库驱动的位置 -->
<!-- <classPathEntry location="D:\tools\.m2\repository\mysql\mysql-connector-java\8.0.21\mysql-connector-java-8.0.21.jar" />-->
<!-- 配置数据库连接池 -->
<context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mysql?serverTimezone=UTC"
userId="gxh"
password="123">
</jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- 指定java bean的生成路径 -->
<javaModelGenerator targetPackage="com.gxh.bean" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--指定mapper文件的路径 -->
<sqlMapGenerator targetPackage="mapper" targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- 指定dao接口的路径 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.gxh.dao" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 指定每个表的生成策略 -->
<table tableName="t_emp" domainObjectName="Emp" >
</table>
<table tableName="t_dept" domainObjectName="Dept" >
</table>
</context>
</generatorConfiguration>
//生成代码
@Test
public void main1() throws Exception{
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("D:\\ideaWork\\springmvc-teach\\ssm\\mbg.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
//为当前请求决定使用哪一个处理器
//通过这个映射器去找到对应的处理器RequestMappingHandlerMapping mapping;
mappedHandler = getHandler(processedRequest);
//找适配器 RequestMappingHandlerAdapter adapter 为了去调用处理器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Actually invoke the handler.
//使用适配去去调用处理器的方法
ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//处理转发结果 根据视图名 能解析出转发地址 然后使用内部转发
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
//渲染页面
render(mv, request, response);
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
十二. thymeleaf+springMVC
1.依赖
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf-spring4 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
2.application.xml的配置
<bean id="viewResolver" class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine" ref="templateEngine"/>
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
</bean>
<bean id="templateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
3.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" >
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>成功!</h1>
<div class="col-xs-6">
<span th:text="${msg}"></span>
</div>
</body>
</html>