SpringMVC入门学习总结笔记
这段时间发生了很多事,肺炎爆发..蜗壳走了,当天我就急性痛风..😔难受
希望大家减少外出,外出必带口罩,疫情早日稳定,病毒早日消灭。
代码更清楚的话可以看源址:https://blog.csdn.net/qq_41112238/article/details/104117530
介绍
开发服务器端程序主要有两种形式:C/S结构程序和B/S(浏览器/服务器)架构程序,Java基本都是开发B/S。
B/S架构分为三层:
- 表现层 即web层,用来和客户端进行数据交互,一般采用mvc的设计模型
- 业务层 处理具体的业务逻辑
- 持久层 用来操作数据库
mvc全名是model view controller模型视图控制器
- model 数据模型,JavaBean的类,用来封装数据
- view 指jsp,html用来展示数据给用户
- controller 用来接收用户请求,整个流程的控制器,用来进行数据校验等
springMVC是一种基于Java实现的mvc设计模型的请求驱动类型的轻量级Web层框架
作用:
①参数绑定(获取请求参数)②调用业务层 ③响应。
快速入门
需求:
浏览器请求服务器(springmvc),响应成功页面
步骤:
①创建maven工程,导入坐标
②创建controller类,创建方法,添加注解
③创建springmvc.xml(包扫描,注册视图解析器)
④配置web.xml
实现:
首先创建一个maven工程,模板选择webapp
导入以下约束:
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <spring.version>5.0.2.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies>
在main目录下创建java目录和resources目录,并作为source root和resources root
创建一个HelloController类
@Controller public class HelloController { @RequestMapping("/hello") public String sayHello(){ System.out.println("Hello world"); return "success"; } }
创建一个响应页面success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>访问成功</h1> </body> </html>
创建一个名称为springmvc.xml的springconfig文件
<?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"> <!-- 开启包扫描 --> <context:component-scan base-package="com"/> <!-- 注册视图解析器 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 配置前缀 --> <property name="prefix" value="/WEB-INF/pages/"/> <!-- 配置后缀 --> <property name="suffix" value=".jsp"/> </bean> </beans>
配置web.xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <!-- 配置核心控制器 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 读取springmvc.xml --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <!-- 启动时加载 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <!-- /* 匹配所有资源 / 匹配除jsp之外的所有资源 --> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
配置tomcat服务器并启动,访问目录hello,成功跳转至success.jsp,控制台输出了Hello world
入门案例流程
加载流程
- 服务器启动,读取web.xml中配置,因为配置了load-on-startup标签,所以会创建DispatcherServlet对象
- 创建spring容器(加载springmvc.xml)并初始化容器中的单例对象(实例化HelloController和视图解析器)。
springmvc请求响应流程
- 浏览器发送请求(/hello),被 DispatherServlet 捕获,该 Servlet 并不处理请求,而是把请求转发出去(控制器类),转发的路径是根据请求 URL,匹配@RequestMapping 中的内容
- 根据执行方法的返回值(success)和视图解析器(InternalResourceViewResolver),去指定的目录下查找指定名称的JSP文件(success.jsp)
- Tomcat服务器渲染页面,做出响应
涉及组件
DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。HandlerMapping:处理器映射器
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等Handler:处理器
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理。HandlAdapter:处理器适配器
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行View Resolver:视图解析器
负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名
即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。View:视图
SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是 jsp。
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。<mvc:annotation-driven>
在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。
使用该标签自动加载RequestMappingHandlerMapping
(处理映射器)和RequestMappingHandlerAdapter
( 处理器适配器)替代注解处理器和适配器的配置。
注意:
一般开发中,我们都需要写上此标签(入门案例中不写也行)
明确:我们只需要编写处理具体业务的控制器以及视图*
@RequestMapping注解
作用:
建立请求url和处理方法之间的对应关系
作用位置:
类 请求url的第一级访问目录 不写相当于根目录,需要以/
开头
方法 请求url的第二级访问目录,可以不以/
开头
属性:value/path
绑定路径,可写多个,一般只配一个
例:
@RequestMapping(value = {"/hello","h"})//在方法上路径用不用/开头都可以 public String sayHello(){ System.out.println("Hello world"); return "success"; }
method
指定访问方式,可配置多个,默认任何方法都支持
例:指定访问方式为post,使用浏览器(get方式)访问失败,使用post时成功
@Controller public class HelloController { @RequestMapping(value = "/hello",method = RequestMethod.POST) public String sayHello(){ System.out.println("Hello world"); return "success"; } }
<html> <body> <h2>Hello World!</h2> <form method="post" action="/hello"> <input type="submit" value="submit"> </form> </body> </html>
params
指定必须携带的参数,可配置多个,不带将报错
headers
指定必须携带的请求头,可配置多个,不带将报错
请求参数的绑定
基本类型和String
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <body> <h2>Hello World!</h2> <h1>参数绑定</h1> <a href="/account/save?name=kobe&age=41">基本类型和STRING</a> </body> </html>
控制器类
@Controller @RequestMapping("/account") public class AccountController { @RequestMapping("/save") public String save(String name,Integer age){ System.out.println(name+"->"+age); return "success"; } }
启动tomcat服务器,点击链接
跳转到success.jsp,控制台输出用户名和年龄
注意:请求参数(href标签中?后的name和age)必须和方法形参(控制器中的name和age)名称一致
POJO(简单的Java对象)
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <body> <h1>pojo类型</h1> <form action="/accousnt/update" method="post"> 账户编号:<input type="text" name="id"><br> 账户名称:<input type="text" name="name"><br> 账户金额:<input type="text" name="money"><br> <input type="submit" value="提交"> </form> </body> </html>
新建一个Account类
public class Account implements Serializable { private Integer id; private String name; private Double money; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Double getMoney() { return money; } public void setMoney(Double money) { this.money = money; } @Override public String toString() { return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}'; } }
控制器中增加以下方法
@RequestMapping("/update") public String update(Account account){ System.out.println(account); return "success"; }
运行tomcat服务器,输入信息并提交
控制台输出
注意:请求属性(input标签中的id,name和money)必须和pojo属性(Account类中的id,name和money)名称一致
POJO中的list集合和map集合
新建Accounts类
public class Accounts { private List<Account> accounts; private Map<String,Account> accountMap; public List<Account> getAccounts() { return accounts; } public void setAccounts(List<Account> accounts) { this.accounts = accounts; } public Map<String, Account> getAccountMap() { return accountMap; } public void setAccountMap(Map<String, Account> accountMap) { this.accountMap = accountMap; } @Override public String toString() { return "Accounts{" + "accounts=" + accounts + ", accountMap=" + accountMap + '}'; } }
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <body> <h1>pojo的list和map类型</h1> <form action="/account/test" method="post"> 账户1编号:<input type="text" name="accounts[0].id"><br> 账户1名称:<input type="text" name="accounts[0].name"><br> 账户1金额:<input type="text" name="accounts[0].money"><br> 账户2编号:<input type="text" name="accounts[1].id"><br> 账户2名称:<input type="text" name="accounts[1].name"><br> 账户2金额:<input type="text" name="accounts[1].money"><br> --------<br> 账户3编号:<input type="text" name="accountMap['a1'].id"><br> 账户3名称:<input type="text" name="accountMap['a1'].name"><br> 账户3金额:<input type="text" name="accountMap['a1'].money"><br> 账户4编号:<input type="text" name="accountMap['a2'].id"><br> 账户4名称:<input type="text" name="accountMap['a2'].name"><br> 账户4金额:<input type="text" name="accountMap['a2'].money"><br> <input type="submit" value="提交"> </form> </body> </html>
启动tomcat
控制台输出:
Accounts{ accounts=[ Account{id=1, name='a', money=111.0}, Account{id=2, name='b', money=222.0} ], accountMap={ a1=Account{id=3, name='c', money=333.0}, a2=Account{id=4, name='d', money=444.0} } }
请求参数中文乱码的解决
在web.xml中配置
<!--配置编码过滤器 --> <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>
自定义类型转换器
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <body> <h1>类型转换</h1> <a href="/account/convert?leaveday=2020-01-27">转换字符串为日期</a> </body> </html>
自定义转换器,需要实现Converter接口
public class StringToDateConvertor implements Converter<String, Date> { @Override public Date convert(String s) { try { return new SimpleDateFormat("yyyy-mm-dd").parse(s); } catch (ParseException e) { e.printStackTrace(); } return null; } }
控制器中访问方法
@RequestMapping("/convert") public String convert(Date leaveday){ System.out.println(leaveday); return "success"; }
在springmvc.xml中配置
<!-- 注册类型转换器 --> <bean id="conversion" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <!-- 注入自定义类型转换器--> <bean class="com.convert.StringToDateConvertor"/> </property> </bean> <!-- 启用类型转换器--> <mvc:annotation-driven conversion-service="conversion"/>
使用servletAPI对象作为方法参数
SpringMVC 还支持使用原始 ServletAPI 对象作为控制器方法的参数。支持原始 ServletAPI 对象有:
HttpServletRequest
HttpServletResponse
HttpSession
java.security.Principal
Locale
InputStream
OutputStream
Reader
Writer
例:
index.jsp中
<a href="/account/servlet?name=kobe">servlet api</a>
控制器中的方法
@RequestMapping("/servlet") public String servlet(HttpServletRequest request, HttpResponse response, HttpSession session){ String name = request.getParameter("name"); System.out.println(name); System.out.println(response); System.out.println(session); return "success"; }
启动服务器:
常用注解
@RequestParam
作用:
将请求参数和控制器中方法形参绑定(请求参数名和形参名不再要求相同)
属性:name/value
和请求参数名一致,可省略required
指定请求参数是否必填项defaultValue
未提供请求参数时的默认值
控制器类
@Controller @RequestMapping("/anno") public class AnnoController { @RequestMapping("/req") public String testRequestParam(@RequestParam("name") String username, Integer userage){ System.out.println(username+"->"+userage); return "success"; } }
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <body> <h1>常用注解</h1> <h3>@RequestParam</h3> <a href="/anno/req?name=kobe&age=41">test</a> </body> </html>
启动tomcat服务器访问,控制台输出(age没有进行参数绑定,形参名和请求参数名也不一致,所以为null):
@RequestBody
作用:
用于获取请求体的内容,直接使用得到的是key=value形式的字符串
把获取的json数据转换成pojo对象
(get方式不可用)
控制器方法
@RequestMapping("/reqBody") public String testRequestBody(@RequestBody String params){ System.out.println(params); return "success"; }
index.jsp中添加表单
<h3>@RequestBody</h3> <form action="/anno/reqBody" method="post"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> <input type="submit" value="submit"> </form>
启动服务器,填写表单提交,控制台输出:
@PathVariable
作用:
绑定url中的占位符,例如请求url中/delete/{id},{id}就是url占位符
url支持占位符是spring3.0后加入的,是springmvc支持rest风格url的一个重要标志
属性:name/value
指定url中占位符名称required
指定是否必须提供占位符
控制器方法
@RequestMapping("/path/{id}") public String testPathVariable(@PathVariable("id") String userid){ System.out.println(userid); return "success"; }
index.jsp中添加链接
<h3>@PathVariable</h3> <a href="/anno/path/1">test</a>
启动服务器,点击超链接,控制台输出:
@RequestHeader注解
作用:
获取指定请求头的值
属性:value
:请求头的名称
控制器方法
@RequestMapping("/reqH") public String testRequestHeader(@RequestHeader("host") String host){ System.out.println(host); return "success"; }
index.jsp中添加链接
<h3>@RequestHeader</h3> <a href="/anno/reqH">test</a>
启动服务器,点击超链接,控制台输出:
@CookieValue注解
作用:
用于把指定 cookie 名称的值传入控制器方法参数。
属性:value
:指定 cookie 的名称。required
:是否必须有此 cookie。
控制器方法
@RequestMapping("/cookie") public String testCookieValue(@CookieValue("JSESSIONID") String id){ System.out.println(id); return "success"; }
index.jsp中添加链接
<h3>@CookieValue</h3> <a href="/anno/cookie">test</a>
启动服务器,点击超链接,控制台输出:
@ModelAttribute
作用:
该注解是 SpringMVC4.3 版本以后新加入的。它可以用于修饰方法和参数。
出现在方法上,表示当前方***在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。
出现在参数上,获取指定的数据给参数赋值。
属性:value
:用于获取数据的 key。key 可以是 POJO 的属性名称,也可以是 map 结构的 key。
应用场景:
当表单提交数据不是完整的实体类数据时,保证没有提交数据的字段使用数据库对象原来的数据。
例如:
我们在编辑一个用户时,用户有一个创建信息字段,该字段的值是不允许被修改的。在提交表单数据是肯定没有此字段的内容,一旦更新会把该字段内容置为 null,此时就可以使用此注解解决问题。
实体类User
public class User { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
index.jsp中添加链接
<h3>@modelAttribute</h3> <a href="/anno/modelAttribute?name=kobe">test</a>
控制器添加方法
@ModelAttribute public void pre(User user){ user.setAge(41); System.out.println("pre方法执行了,设置了年龄"); } @RequestMapping("/modelAttribute") public String testmodelAttribute(User user){ System.out.println("test方法执行了"); System.out.println(user); return "success"; }
启动服务器,点击链接,控制台输出
带有返回值的例子
控制器中方法:
@ModelAttribute public User pre(String name){ User user = findUserByName(name); System.out.println("初始信息"+user); return user; } //模拟数据库查询 public User findUserByName(String name){ User user=new User(); user.setName(name); user.setAge(40); return user; } @RequestMapping("/modelAttribute") public String testmodelAttribute(User user){ System.out.println("修改方法执行了"); System.out.println("当前信息"+user); return "success"; }
index.jsp中添加表单
<h3>@modelAttribute</h3> <form action="/anno/modelAttribute" method="post"> 用户名:<input type="text" name="name"><br> 年龄:<input type="text" name="age"><br> <input type="submit" value="submit"> </form>
启动服务器,点击链接,控制台输出
在参数上使用
控制器方法:
@ModelAttribute public void pre(String name, Map<Integer,User> map){ User user = findUserByName(name); System.out.println("初始信息"+user); map.put(1,user); } public User findUserByName(String name){ User user=new User(); user.setName(name); user.setAge(40); return user; } @RequestMapping("/modelAttribute") public String testmodelAttribute(@ModelAttribute("1") User user){ System.out.println("当前信息"+user); return "success"; }
启动服务器,点击链接,控制台输出
响应数据
三种响应格式
字符串
控制器中的方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址
逻辑视图:success
物理视图:/WEB-INF/pages/success.jsp
@Controller @RequestMapping("/response") public class ResponseController { @RequestMapping("/string") public String testStr(){ return "success"; } }
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <body> <h1>响应数据</h1> <a href="/response/string">测试返回字符串</a> </body> </html>
测试:
void
默认访问视图解析器前缀+requestmapping路径+视图解析器后缀
index.jsp中添加链接
<a href="/response/void">测试void</a>
控制器中添加方法
@RequestMapping("/void") public void testVoid(){ }
如果带HttpServletRequest/HttpServletResponse参数,访问空白页面
@RequestMapping("/void") public void testVoid(HttpServletResponse response) throws IOException { response.getWriter().write("success"); }
ModelandView
可以通过request域存放对象,通过设置视图名实现跳转
index.jsp中添加链接
<a href="/response/modelandview">测试modelAndView</a>
控制器中添加方法
@RequestMapping("/modelandview") public ModelAndView testModelandView() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("success"); modelAndView.addObject("msg","Hello World"); return modelAndView; }
修改success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html> <head> <title>Title</title> </head> <body> <h1>访问成功</h1><br> 获取request域存储的值:${msg} </body> </html>
转发和重定向
前提:控制器方法返回值类型必须是String
转发
转发到页面:
语法:forward:+绝对地址
@RequestMapping("/forward") public String forward(){ return "forward:/WEB-INF/pages/suceess.jsp"; }
index.jsp中添加链接
<a href="/response/forward">测试转发</a>
测试
转发到控制器其他方法:
语法:forward:+类上requestmapping的地址+方法上requestmapping的地址
@RequestMapping("/forward") public String forward(){ return "forward:/response/string"; }
结果和上面是一样的(等同于访问之前响应类型为字符串的例子效果)
重定向
重定向到页面:
语法:redirect:+绝对地址
注意:不能重定向访问WEB-INF下的资源
@RequestMapping("/testRedirect") public String testRedirect(){ return "redirect:/redirect.jsp"; }
index.jsp中添加链接
<a href="/response/forward">测试转发</a>
在webapp目录下新建一个redirect.jsp
运行tomcat服务器,访问链接
重定向到控制器其他方法:
语法:redirect:+类上requestmapping的地址+方法上requestmapping的地址
@RequestMapping("/testRedirect") public String testRedirect(){ return "redirect:/response/string"; }
结果和上面转发到控制器方法的例子是一样的
重定向到外部链接
@RequestMapping("/testRedirect") public String testRedirect(){ return "redirect:http://www.baidu.com"; }
可重定向到百度。
转发和重定向的区别
- 转发是一次请求,重定向是两次
- 转发地址栏不变,重定向改变
- 转发只能到内部资源,重定向可以到内部或外部
- 转发可以到WEB-INF下资源,重定向不可
RequestBody获取json数据实体类User
如果获取json数据,控制器方法参数类型为String
如果想将json数据转换为JavaBean类型,控制器方法类型为JavaBean类型
一个实体类User
public class User implements Serializable { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
用于获取json格式数据的控制器方法
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/test") public String test(@RequestBody String user){//如果想获取JavaBean类型,把String改为User即可 System.out.println(user); return "success"; } }
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <script src="js/jquery.min.js"></script> <script> function requestJson() { $.ajax({ url:"user/test", type:"post", contentType:"application/json;charset=UTF-8", data:'{"name":"kobe","age":41}', dataType:"json", success:function (data) { alert(data); } }); } </script> <body> <h1>RequestBody获取json数据</h1> <input type="button" value="获取json" onclick="requestJson()"> </body> </html>
测试:点击按钮,控制台输出
获取到的是json字符串格式
ResponseBody响应json数据
步骤
1 导入Jackson坐标
2 把什么对象转换为json,方法返回值就定义什么类型
3 添加@ResponseBody注解,放在方法上或返回类型前都可以
Jackson坐标
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.0</version> </dependency>
控制器方法只需稍作修改
@RequestMapping("/test") public @ResponseBody User test(@RequestBody User user){ System.out.println(user); return user; }
在springmvc中进行忽略静态资源的配置(否则js文件引入失败)
<!-- 忽略静态资源 mapping:匹配请求地址 location:对应资源所在的目录(webapp) --> <mvc:resources mapping="/js/**" location="/js/"/> ------------------------------------------------------- <!-- 或者使用以下配置,使用默认servelt处理器 --> <mvc:default-servlet-handler/>
index.jsp不变
文件上传
浏览器端要求
- 表单提交方式
post
(get有文件大小限制) - 提供文件上传框
input type="file"
- 表单的
entype
属性必须为multipart/form-data
服务器端要求
- 使用
request.getInputStream()
获取数据 - springmvc底层封装了commons-fileupload文件上传工具包
传统方式上传
导入依赖坐标
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3</version> </dependency>
控制器方法
@Controller @RequestMapping("/file") public class FileController { @RequestMapping("/upload1") public String upload1(MultipartFile file, HttpSession session) throws IOException { String filename = file.getOriginalFilename();//获取文件名 String path = session.getServletContext().getRealPath("upload");//获取文件目录路径 File targetFile = new File(path + "/" + filename); file.transferTo(targetFile);//上传 return "success"; } }
在springmvc.xml中配置文件上传解析器
<!-- 配置文件上传解析器 bean的id必须为multipartResolver--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 限制文件大小为5MB 1024*1024*5--> <property name="maxUploadSize" value="5242880"/> </bean>
前端jsp
<h1>文件上传</h1> <form action="file/upload1" method="post" enctype="multipart/form-data"> 文件:<input type="file" name="file"><br> <input type="submit"> </form>
在webapp目录下新建一个upload文件夹保存上传的文件(如果文件夹为空可能会出现问题,随便建立一个文件)。
启动tomcat服务器,选择一张图片进行上传
点击提交按钮后
在target目录中可以看到文件上传成功
跨服务器上传
图片服务器环境搭建
新建一个maven工程作为图片服务器
工程创建完毕后,跟之前一样在webapp目录下创建一个upload文件夹用来保存图片数据(初始时随便建立一个文件防止上传失败问题)
配置tomcat服务器,防止端口号冲突将http端口改为8081,jmx端口改为1098。
测试图片服务器是否可以正常启动
还需要对tomcat的配置文件进行修改,找到tomcat文件夹下conf配置文件文件夹的web.xml
在约110行左右增加以下设置,将readonly设置为false
再次启动tomcat服务器,可以对upload文件夹下的文件在浏览器进行访问(空白页面因为txt文件内容为空)。
应用服务器功能实现
添加jersey坐标依赖(负责拷贝文件上传的必备 jar 包)
<dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-core</artifactId> <version>1.18.1</version> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-client</artifactId> <version>1.18.1</version> </dependency>
控制器方法
@RequestMapping("/upload2") public String upload2(MultipartFile file, HttpSession session) throws IOException { //获取文件名 String filename = file.getOriginalFilename(); //调用jerseyAPI上传文件 Client client=Client.create();//创建客户端 String path = "http://localhost:8081/upload/";//图片服务器路径 WebResource resource = client.resource(path + filename);//建立连接 resource.put(file.getBytes());//上传 return "success"; }
注意Client是jersey下的
应用服务器端的前端jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <body> <h1>文件上传</h1> <form action="file/upload2" method="post" enctype="multipart/form-data"> 文件:<input type="file" name="file"><br> <input type="submit"> </form> </body> </html>
启动图片服务器和应用服务器
在应用服务器上传图片
在图片服务器的upload文件夹下成功获取了上传的图片
异常处理
系统中异常包括两类:预期异常和运行时异常 RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的 dao、service、controller 出现都通过 throws Exception 向上抛出,最后由 springmvc 前端控制器交由异常处理器进行异常处理
传统方式
dao类
public class UserDao { public void save(){ System.out.println("save account"); } }
service类
public class UserService { private UserDao userDao=new UserDao(); public void save(){ int i=1/0;//模拟异常 userDao.save(); } }
控制器方法
@Controller @RequestMapping("/user") public class UserController { private UserService userService = new UserService(); @RequestMapping("/save") public String save(HttpServletRequest request){ try{ userService.save(); }catch (Exception e){ request.setAttribute("msg",e.getMessage()); return "error"; } return "success"; } }
前端index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <body> <h1>处理异常</h1> <a href="user/save">保存账户</a> </body> </html>
新建一个error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html> <head> <title>Title</title> </head> <body> ${msg} </body> </html>
启动tomcat服务器测试
输出异常信息
springmvc自定义异常处理器
控制器方法改为
@RequestMapping("/save") public String save(HttpServletRequest request){ System.out.println("save方法执行了"); userService.save(); return "success"; }
自定义异常处理器类
public class ExceptionHandler implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { ModelAndView mv=new ModelAndView(); mv.setViewName("error"); mv.addObject("msg",e.getMessage()); return mv; } }
方式一:在springmvc.xml中配置异常处理器
<!-- 注册异常处理器 --> <bean id="exceptionHandler" class="com.exp.ExceptionHandler"/>
(方式二:或者在自定义异常处理类上加@Component注解也可以)
启动服务器,进行测试
***
Spring MVC 的***类似于 Servlet 中的过滤器 Filter,用于对处理器进行预处理和后处理,用户可以自己定义一些***来实现特定的功能。
***链:将***按一定的顺序联结成一条链,在访问被拦截的方法或字段时,***链中的***就会按其之前定义的顺序被调用。
它和过滤器相似,但是也有区别:
- 过滤器是 servlet 规范中的一部分,任何 java web 工程都可以使用。
***是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用。 - 过滤器在 url-pattern 中配置了
/*
之后,可以对所有要访问的资源拦截。
***它是只会拦***问的控制器方法,如果访问的是 jsp,html,css,image 或者 js 是不会进行拦截的。 - 它也是 AOP 思想的具体应用。
- 我们要想自定义***, 要求必须实现:HandlerInterceptor 接口。
前端index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <body> <h1>***</h1> <a href="user/save">保存账户</a> </body> </html>
***类
public class CustomIntercept implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("***pre方法执行了"); return true;//返回值true表示放行,false表示不放行 } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("***post方法执行了"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("***after方法执行了"); } }
在springmvc.xml中配置***
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean id="customIntercept" class="com.intercept.CustomIntercept"/> </mvc:interceptor> </mvc:interceptors>
启动服务器,观察控制台输出
注意:如果抛出异常(之前userService中模拟了1/0的异常),post方法是不执行的。
也可以使用***进行转发和重定向
例:
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("***01pre方法执行了"); request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response); return false;//返回值true表示放行,false表示不放行 }
***方法的说明
- preHandle
- 按***定义顺序调用
- 只要配置了都会调用
- 如果程序员决定该***对请求进行拦截处理后还要调用其他的***,或者是业务处理器去进行处理,则返回 true。如果程序员决定不需要再调用其他的组件去处理请求,则返回 false。
- postHandle
- 按***定义逆序调用
- 在***链内所有***返回成功时调用
- 在业务处理器处理完请求后,但是 DispatcherServlet 向客户端返回响应前被调用, 在该方法中对用户请求 request 进行处理。
- afterCompletion
- 按***定义逆序调用
- 只有 preHandle 返回 true 才调用
- 在 DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。
执行顺序演示
正常情况
将之前的***类添加后缀01,复制***类,将后缀修改为02
public class CustomIntercept02 implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("***02pre方法执行了"); return true;//返回值true表示放行,false表示不放行 } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("***02post方法执行了"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("***02after方法执行了"); } }
配置
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean id="customIntercept01" class="com.intercept.CustomIntercept01"/> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**"/> <bean id="customIntercept02" class="com.intercept.CustomIntercept02"/> </mvc:interceptor> </mvc:interceptors>
测试,观察控制台输出
中断情况
将***02的pre方法中true改为false后,启动服务器,观察控制台输出
中断流程如下图红色箭头流程