SpringMVC 学习笔记
1、Spring MVC
1、MVC 设计模式
MVC
的全名是 Model View Controller
,是模型(model)
-视图(view)
-控制器(controller)
的缩写,是一种软件设计模式。
职责分析:
Controller:控制器
- 取得表单数据
- 调用业务逻辑
- 转向指定的页面
Model:模型
- 业务逻辑
- 保存数据的状态
View:视图
步骤:
- 显示页面
- 用户发请求
- Servlet接收请求数据,并调用对应的业务逻辑方法
- 业务处理完毕,返回更新后的数据给servlet
- servlet转向到JSP,由JSP来渲染页面
- 响应给前端更新后的页面
2、简介
- Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。
- Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。
- 使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的Spring MVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts 2(一般老项目使用)等等。
Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架。
Spring MVC 官网:
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#spring-web
Spring MVC 的特点:
- 轻量级,简单易学
- 高效 , 基于请求响应的MVC框架
- 与Spring兼容性好,无缝结合
- 约定优于配置
- 功能强大:RESTful、数据验证、格式化、本地化、主题等
- 简洁灵活
Spring的web框架围绕 DispatcherServlet
[ 调度Servlet ] 设计。
DispatcherServlet
的作用是将请求分发到不同的处理器 不同的网页
。
3、DispatcherServlet 原理
Spring
的 web
框架围绕 DispatcherServlet
设计。DispatcherServlet
的作用是将请求分发到不同的处理器。
Spring MVC
框架像许多其他 MVC
框架一样, 以请求为驱动 , 围绕一个中心Servlet分派请求及提供其他功能,DispatcherServlet
是一个实际的Servlet (它继承自HttpServlet 基类)。
4、SpringMVC执行原理
运行流程分析:
-
DispatcherServlet
表示前端控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。我们假设请求的url为 : http://localhost:8080/hello
如上url拆分成三部分:
http://localhost:8080服务器域名
hello
表示控制器DispatcherServlet
在web.xml
注册的访问路径,表示经过这个路径的请求都会经过它来控制分发。一般都是用 / 来配置,表示全部访问都要经过它。 -
HandlerMapping
为处理器映射。DispatcherServlet
调用HandlerMapping
, 它会调用 HeadlerExecutionChain方法 根据请求url
查找控制实现Controller
接口的类,并返回类路径给前端控制器
,。 -
前端控制器
将获得的路径传给HandlerAdapter (处理器适配器)
-
处理器适配器
将得到的路径
进行解析,并调用路径下实现了Controller
接口的类,并返回一个ModelAndView
的对象。 -
HandlerAdapter(处理器适配器)
将视图逻辑名或模型传递给DispatcherServlet(前端控制器)
。 -
DispatcherServlet
调用视图解析器(ViewResolver)
来解析HandlerAdapter
传递的逻辑视图名。 -
视图解析器将解析的逻辑视图名传给
DispatcherServlet(前端控制器)
。 -
DispatcherServlet
根据视图解析器解析的视图并渲染上模型传递的数据
的结果,调用具体的视图。 -
最终视图呈现给用户。
2、第一个 SpringMVC 程序(配置版)
-
导入
pom.xml
依赖<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency> <!--spring-webmvc 依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.4</version> </dependency> <!--servlet 依赖 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency> <!--jsp 依赖--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> </dependency> <!--jstl表达式 依赖--> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
-
将
DispatcherServlet
注册到web.xml中
,这个类是官方写好的,直接配置全路径,注册到web.xml
中即可<!--1.注册DispatcherServlet--> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--关联一个springmvc的配置文件:【servlet-name】-servlet.xml--> <init-param> <!--关联 springmvc 的配置文件,这里文件名可以随便,下面是官方推荐的。--> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> <!--启动级别设置为 1级,在服务器启动的时候就启动,用来控制,所有的请求--> <load-on-startup>1</load-on-startup> </servlet> <!--/ 匹配所有的请求;(不包括.jsp)--> <!--/* 匹配所有的请求;(包括.jsp)--> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
-
创建上面
web.xml
配置的springmvc-servlet.xml
缺少的spring
配置文件在resources
目录下<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
-
将映射处理器
HandlerMapping
,适配处理器HandlerAdapter
,视图解析器ViewResolver
注册到bean
中(刚刚创建的 springmvc-servlet.xml)<!-- 添加 处理映射器--> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <!--添加 处理器适配器--> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <!--视图解析器:DispatcherServlet给他的ModelAndView--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver"> <!--这里可以配置 请求路径的地址,到后面需要走上面 jsp 的时候,会自动拼接--> <!--前缀:用来拼接请求的地址--> <property name="prefix" value="/WEB-INF/jsp/"/> <!--后缀:用来拼接请求的地址--> <property name="suffix" value=".jsp"/> </bean>
-
编写实现了
Controller
接口的类处理请求类似于以前实现servlet
接口的类,只是方法不同。package com.wyx.controller; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class HelloController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { //ModelAndView 模型和视图,这个对象需要返回这个类。 ModelAndView mv = new ModelAndView(); //封装对象,放在ModelAndView中。可以在 jsp 中引用 mv.addObject("msg","HelloSpringMVC!"); //封装要跳转的视图,放在ModelAndView中 mv.setViewName("hello"); // /WEB-INF/jsp/hello.jsp return mv; } }
-
将写好的类注册到
bean
中,(springmvc-servlet.xml)<!-- id:作为请求的路径,记得请求路径前配置加上/,不然会找不到资源,拼接上面的前缀和后缀,class 是需要映射的类 --> <bean id="/hello" class="com.wyx.controller.HelloController"/>
-
编写要跳转的 jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <%--刚刚封装的 msg 信息--%> ${msg} </body> </html>
一个简单的 Spring mvc
就在这样创建完成,配置 Tomcat 启动测试,如果找不到资源,需要将 jar
包导入 lib
目录
3、使用注解来编写 SpringMVC(常用)
- 使用注解版开发,前面 3 步还是一样,我们只需要在
bean
中加入开启自动扫描包及mvc
驱动支持,和注解
支持就可以使用了。
<?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">
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="com.wyx.controller"/>
<!-- 让Spring MVC不处理静态资源
添加这个,可以让 SpringMVC 过滤一些图片,音乐,视频,等静态资源 (html,css,js)
-->
<mvc:default-servlet-handler />
<!--添加这个 MVC驱动 可以免配,映射适配器,和适配处理器-->
<mvc:annotation-driven />
<!--视图解析器:DispatcherServlet给他的ModelAndView-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!--前缀:用来拼接请求的地址-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀:用来拼接请求的地址-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
- 编写对应的 Servlet类,不在需要实现
Controller
接口,只需要将类名加上注解映射到bean
中,具体的跳转视图,看类中的方法
package com.wyx.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
/*
* 开启,@Controller 在spring中所学,让包能够被 bean托管
* 也可以将 @RequestMapping("请求路径") 定义在类上,在访问的时候,
* 只需要加 localhost:8080/项目发布名/加上方法的请求路径
* */
@Controller
public class HelloController{
/*
* @RequestMapping("/hello"),用来查看保存请求路径,其中可以加参数。
* @Mapping :初始化所有mapping的注解
* @GetMapping :默认只能走get 方式的请求
* @PostMapping : 默认只能走 post 方式请求,剩下的意思一样,常用的就是上面 3 种
* @DeleteMapping
* @PutMapping
* @PatchMapping
* */
@RequestMapping("/hello") //参数就是请求的路径,默认走get方法
public String test(Model model){
// Model model 代表获取数据模型,他继承自 ModelAndView ,然而,通过注解开发,一般只需要调用 Model 就可以了
model.addAttribute("msg","SpringMVC,!Annotation"); //给浏览器传递一个名为 msg 的对象
// 返回值,就是用来拼接视图映射器的前缀和后缀的东西,表示跳转的另一个jsp。
return "hello";
}
}
总结:
- 实现接口
Controller
定义控制器是较老的办法 缺点是
:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦;
4、RestFul 风格
RestFul
就是一种资源定位及其资源操作风格,基于这个风格设计软件可以更简洁,更有层次,更易于实现缓存等机制。
使用传统方式操作资源
http://127.0.0.1/admin/queryuser.action?id=1 查询 Get
http://127.0.0.1/admin/saveuser.action 添加 Post
http://127.0.0.1/admin/updateuser.action 修改 Post
http://127.0.0.1/admin/deleteuser.action?id=1 删除 Post|Get
使用
RestFul
操作资源 http://127.0.0.1/admin/1 查询 Get
http://127.0.0.1/admin/ 新增 Post
http://127.0.0.1/admin/ 更新 Put
http://127.0.0.1/admin/1 删除 Delete
@RequestMapping("/hello") //参数就是请求的路径,默认走get方法
@RequestMapping(value = "/请求路径",method = RequestMethod.POST) //另外一种写法
/*
* @ResponseBody 使用此注解,返回值不会去解析视图,将返回值封装成为一个Json字符串
* @GetMapping :默认只能走get 方式的请求
* @PostMapping : 默认只能走 post 方式请求,剩下的意思一样,常用的就是上面 3 种
* @DeleteMapping
* @PutMapping
* @PatchMapping
* 多数情况使用注解来定义方法:
*/
//以下请求相当于 http://localhost:8080/getKhxxPage/1/2/
/*
将 路径中的变量传递给请求的方法,
*/
@RequestMapping("/getKhxxPage/{a}/{b}")
public String getKhxxPage( @PathVariable("a") String a,
@PathVariable("b") String b){
}
//以上就是 RestFul 风格实现的主要方法。
5、重定向和转发
通过设置ServletAPI , 不需要视图解析器 .
1、通过HttpServletResponse进行输出
2、通过HttpServletResponse实现重定向
3、通过HttpServletResponse实现转发
@Controller
public class ResultGo {
@RequestMapping("/result/t1")
public void test1(HttpServletRequest req, HttpServletResponse rsp) throwsIOException {
rsp.getWriter().println("Hello,Spring BY servlet API");
}
@RequestMapping("/result/t2")
public void test2(HttpServletRequest req, HttpServletResponse rsp) throwsIOException {
rsp.sendRedirect("/index.jsp");
}
@RequestMapping("/result/t3")
public void test3(HttpServletRequest req, HttpServletResponse rsp) throwsException {
//转发
req.setAttribute("msg","/result/t3");
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
}
}
SpringMVC
通过SpringMVC来实现转发和重定向 - 无需视图解析器;
测试前,需要将视图解析器注释掉
@Controller
public class ResultSpringMVC {
@RequestMapping("/rsm/t1")
public String test1(){
//转发
return "/index.jsp";
}
@RequestMapping("/rsm/t2")
public String test2(){
//转发二
return "forward:/index.jsp";
}
@RequestMapping("/rsm/t3")
public String test3(){
//重定向
return "redirect:/index.jsp";
}
}
通过SpringMVC来实现转发和重定向 - 有视图解析器;
重定向 , 不需要视图解析器 , 本质就是重新请求一个新地方嘛 , 所以注意路径问题.
可以重定向到另外一个请求实现 .
@Controller
public class ResultSpringMVC2 {
@RequestMapping("/rsm2/t1")
public String test1(){
//转发
return "test";
}
@RequestMapping("/rsm2/t2")
public String test2(){
//重定向
return "redirect:/index.jsp";
//return "redirect:hello.do"; //hello.do为另一个请求/
}
}
5、乱码问题
不得不说,乱码问题是在我们开发中十分常见的问题,也是让我们程序猿比较头大的问题!
以前乱码问题通过过滤器解决 , 而SpringMVC给我们提供了一个过滤器 , 可以在web.xml中配置 .
修改了xml文件需要重启服务器!
<filter>
<filter-name>encoding</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>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
但是我们发现 , 有些极端情况下.这个过滤器对get的支持不好 .
处理方法 :
1、修改tomcat配置文件 :设置编码!
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
2、自定义过滤器(网上大神代码,基本能解决所有乱码)
package com.kuang.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
* 解决get和post请求 全部乱码的过滤器
*/
public class GenericEncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChainchain) throws IOException, ServletException {
//处理response的字符编码
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");
// 转型为与协议相关对象
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 对request包装增强
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
//是否编码的标记
private boolean hasEncode;
//定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
public MyRequest(HttpServletRequest request) {
super(request);// super必须写
this.request = request;
}
// 对需要增强方法 进行覆盖
@Override
public Map getParameterMap() {
// 先获得请求方式
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
// post请求
try {
// 处理post乱码
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
// get请求
Map<String, String[]> parameterMap = request.getParameterMap();
if (!hasEncode) { // 确保get手动编码逻辑只运行一次
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
// 处理get乱码
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}
//取一个值
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
return values[0]; // 取回参数的第一个值
}
//取所有值
@Override
public String[] getParameterValues(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}
6、拦截器和文件上传下载
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。
过滤器与拦截器的区别:拦截器是AOP思想的具体应用。
过滤器
- servlet规范中的一部分,任何java web工程都可以使用
- 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截
拦截器
- 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
- 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的
拦截器的实现
创建 Maven程序并导入依赖,添加 Web框架支持
配置web.xml 和 springmvc-servlet.xml 文件
编写自定义拦截器
实现自定义拦截器需要实现 HandlerInterceptor
接口。(这里利用的是Spring AOP
思想)
package com.wyx.config;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
//在请求处理的方法之前执行
//如果返回true执行下一个拦截器
//如果返回false就不执行下一个拦截器
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("------------处理前------------");
return true;
}
//在请求处理方法执行之后执行
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("------------处理后------------");
}
//在dispatcherServlet处理后执行,做清理工作.
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("------------清理------------");
}
}
在springmvc的配置文件中配置拦截器
在 springmvc-servlet.xml
中配置
<!--关于拦截器的配置-->
<mvc:interceptors>
<mvc:interceptor>
<!--/** 包括路径及其子路径-->
<!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截-->
<!--/admin/** 拦截的是/admin/下的所有-->
<mvc:mapping path="/**"/>
<!--bean配置的就是拦截器-->
<bean class="com.wyx.config.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
验证用户是否登录 (认证用户)
1、登录页面登录,提交controller
请求,判断密码,如果正确,将用户信息放入Session
3、拦截 除了登录请求的所有请求
如果存在用户信息的 Session
。放行, 如果没有,跳转到登陆页面。
登录请求放入 Session
//登陆提交
@RequestMapping("/login")
public String login(HttpSession session, String username, String pwd) throws Exception {
// 向session记录用户身份信息 使用时,修改判断密码或用户是否正确
System.out.println("接收前端==="+username);
session.setAttribute("user", username);
return "success";
}
利用Session 实现拦截器
package com.wyx.config;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
// 判断是不是登录请求
if (request.getRequestURI().contains("login")) {
return true;
}
HttpSession session = request.getSession();
// 如果用户已登陆也放行
if(session.getAttribute("user") != null) {
return true;
}
// 用户没有登陆跳转到登陆页面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
将编写的 拦截器注册到 springmvc-servlet.xml
<!--关于拦截器的配置-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean id="loginInterceptor" class="com.wyx.config.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
文件上传下载 MultipartResolver
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响应。
- Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。
- Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:
- CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件。
文件上传
导入文件上传的jar包
,commons-fileupload , Maven会自动帮我们导入他的依赖包 commons-io包;
<!--文件上传-->
<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
【注意!!!这个bena的id必须为:multipartResolver , 否则上传文件会报400的错误!在这里栽过坑,教训!】
<!--文件上传配置-->
<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):将上传文件保存到一个目录文件中
编写前端页面
<form action="/upload" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit" value="upload">
</form>
Controller
package com.wyx.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
@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";
}
}
测试上传
采用file.Transto 来保存上传的文件
编写Controller
/*
* 采用file.Transto 来保存上传的文件
*/
@RequestMapping("/upload2")
public String fileUpload2(@RequestParam("file") CommonsMultipartFile file,HttpServletRequest request) throws IOException {
//上传路径保存设置
String path = request.getServletContext().getRealPath("/upload");
File realPath = new File(path);
if (!realPath.exists()){
realPath.mkdir();
}
//上传文件地址
System.out.println("上传文件保存地址:"+realPath);
//通过CommonsMultipartFile的方法直接写文件(注意这个时候)
file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));
return "redirect:/index.jsp";
}
测试访问
文件下载
文件下载步骤:
1、设置 response 响应头
2、读取文件 -- InputStream
3、写出文件 -- OutputStream
4、执行操作
5、关闭流 (先开后关)
代码实现:
@RequestMapping(value="/downloadurl")
@ResponseBody
public String downloads(HttpServletResponse response ,HttpServletRequest request)throws Exception{
//要下载的图片地址
String path = request.getServletContext().getRealPath("/upload");
String fileName = "1.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 "下载成功";
}
前端
<a href="/downloadurl">点击下载</a>
测试下载
7、SSM框架整合
1、创建数据库
2、创建Maven项目并添加Web框架支持
3、导入Pom.xml依赖
<!--以下依赖,由于技术更新迭代快,使用时可以选择版本-->
<dependencies>
<!--Junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--Servlet - JSP -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<!--Jsp api 这个和上面一个都是jsp 依赖,下面这个已经过时-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<!--jstl表达式依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--mybatis-spring-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!--Spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--Spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
</dependencies>
4、maven静态资源过滤
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
5、创建层级目录
- com.wyx.pojo
- com.wyx.dao
- com.wyx.service
- com.wyx.controller
6、编写核心配置文件
-
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--开启别名包--> <typeAliases> <package name="com.wyx.pojo"/> </typeAliases> <!--将编写的实体类mapper.xml添加进mapper--> <mappers> <!--每一个mapper.xml都要添加--> <mapper resource="com/wyx/dao/BookMapper.xml"/> </mappers> </configuration>
-
编写实体类
package com.wyx.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class Books { private int bookID; private String bookName; private int bookCounts; private String detail; }
-
编写 dao
package com.wyx.dao; import com.wyx.pojo.Books; import java.util.List; public interface BookMapper { //增加一个Book int addBook(Books book); //根据id删除一个Book int deleteBookById(int id); //更新Book int updateBook(Books books); //根据id查询,返回一个Book Books queryBookById(int id); //查询全部Book,返回list集合 List<Books> queryAllBook(); }
-
编写对应的 mapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.wyx.dao.BookMapper"> <!--增加一个Book--> <insert id="addBook" parameterType="Books"> insert into ssmbuild.books(bookName,bookCounts,detail) values (#{bookName}, #{bookCounts}, #{detail}) </insert> <!--根据id删除一个Book--> <delete id="deleteBookById" parameterType="int"> delete from ssmbuild.books where bookID=#{bookID} </delete> <!--更新Book--> <update id="updateBook" parameterType="Books"> update ssmbuild.books set bookName = #{bookName},bookCounts = #{bookCounts},detail = #{detail} where bookID = #{bookID} </update> <!--根据id查询,返回一个Book--> <select id="queryBookById" resultType="Books"> select * from ssmbuild.books where bookID = #{bookID} </select> <!--查询全部Book--> <select id="queryAllBook" resultType="Books"> SELECT * from ssmbuild.books </select> </mapper>
-
编写 Service
package com.wyx.service; import com.wyx.pojo.Books; import java.util.List; //BookService:底下需要去实现,调用dao层 public interface BookService { //增加一个Book int addBook(Books book); //根据id删除一个Book int deleteBookById(int id); //更新Book int updateBook(Books books); //根据id查询,返回一个Book Books queryBookById(int id); //查询全部Book,返回list集合 List<Books> queryAllBook(); }
-
编写对应的ServiceImpl
package com.wyx.service; import com.wyx.dao.BookMapper; import com.wyx.pojo.Books; import java.util.List; public class BookServiceImpl implements BookService { //调用dao层的操作,设置一个set接口,方便Spring管理 private BookMapper bookMapper; public void setBookMapper(BookMapper bookMapper) { this.bookMapper = bookMapper; } public int addBook(Books book) { return bookMapper.addBook(book); } public int deleteBookById(int id) { return bookMapper.deleteBookById(id); } public int updateBook(Books books) { return bookMapper.updateBook(books); } public Books queryBookById(int id) { return bookMapper.queryBookById(id); } public List<Books> queryAllBook() { return bookMapper.queryAllBook(); } }
-
database.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=true&characterEncoding=utf8 jdbc.username=root jdbc.password=123456
-
spring-dao.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"> <!-- 配置整合mybatis --> <!-- 1.关联数据库文件 --> <context:property-placeholder location="classpath:database.properties"/> <!-- 2.数据库连接池 --> <!--数据库连接池 dbcp 半自动化操作 不能自动连接 c3p0 自动化操作(自动的加载配置文件 并且设置到对象里面) --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 配置连接池属性 --> <property name="driverClass" value="${jdbc.driver}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <!-- c3p0连接池的私有属性 --> <property name="maxPoolSize" value="30"/> <property name="minPoolSize" value="10"/> <!-- 关闭连接后不自动commit --> <property name="autoCommitOnClose" value="false"/> <!-- 获取连接超时时间 --> <property name="checkoutTimeout" value="10000"/> <!-- 当获取连接失败重试次数 --> <property name="acquireRetryAttempts" value="2"/> </bean> <!-- 3.配置SqlSessionFactory对象 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入数据库连接池 --> <property name="dataSource" ref="dataSource"/> <!-- 配置MyBaties全局配置文件:mybatis-config.xml --> <property name="configLocation" value="classpath:mybatis-config.xml"/> </bean> <!-- 4.配置扫描Dao接口包,动态实现Dao接口注入到spring容器中 --> <!--解释 :https://www.cnblogs.com/jpfss/p/7799806.html--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 注入sqlSessionFactory --> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <!-- 给出需要扫描Dao接口包 --> <property name="basePackage" value="com.wyx.dao"/> </bean> </beans>
-
spring-service.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 http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 扫描service相关的bean --> <context:component-scan base-package="com.wyx.service" /> <!--BookServiceImpl注入到IOC容器中 每一个ServiceImpl都需要注入--> <bean id="BookServiceImpl" class="com.wyx.service.BookServiceImpl"> <property name="bookMapper" ref="bookMapper"/> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入数据库连接池 --> <property name="dataSource" ref="dataSource" /> </bean> </beans>
-
spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 配置SpringMVC --> <!-- 1.开启SpringMVC注解驱动 --> <mvc:annotation-driven /> <!-- 2.静态资源默认servlet配置--> <mvc:default-servlet-handler/> <!-- 3.配置jsp 显示ViewResolver视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> <!-- 4.扫描web相关的bean --> <context:component-scan base-package="com.wyx.controller" /> </beans>
-
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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <import resource="spring-dao.xml"/> <import resource="spring-service.xml"/> <import resource="spring-mvc.xml"/> </beans>
-
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"> <!--DispatcherServlet--> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <!--一定要注意:我们这里加载的是总的配置文件,之前被这里坑了!--> <param-value>classpath:applicationContext.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!--encodingFilter--> <filter> <filter-name>encodingFilter</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> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!--Session过期时间--> <session-config> <session-timeout>15</session-timeout> </session-config> </web-app>
-
接下来就是编写前端的 jsp 和 controller ,具体实现看项目就不一一编写
启动项目,记得在项目中添加 lib 包 并将所有依赖的jar包导入