JavaWeb学习中关于SpringMVC的相关知识 💯💤
SpringMVC
¶三层架构
- 开发服务器端程序,一般都基于两种形式,一种C/S架构程序,一种B/S架构程序。
- 使用Java语言基本上都是开发B/S架构的程序,B/S架构又分成了三层架构。
- 表现层:Web层,用来和客户端进行数据交互的,如SpringMVC框架。
- 业务层:Service层,处理公司具体的业务逻辑的,Spring框架。
- 持久层:Dao层,用来操作数据库的,MyBatis框架。
- MVC模型,即Model View Controller
- Model:数据模型,JavaBean的类,用来进行数据封装。
- View:通常指JSP、HTML,用来展示数据给用户。
- Controller:用来接收用户的请求,处理程序逻辑,整个流程的控制器。
¶SpringMVC入门
¶1.SpringMVC概述
- SpringMVC是一种基于Java实现的MVC设计模型的请求驱动类型的轻量级WEB框架。
- 属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。
- 使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts2等。
- SpringMVC在三层架构中的表现层。
¶2.SpringMVC入门程序
-
创建WEB工程,选择maven项目中的webapp模板类型。
-
注意:创建maven项目时过慢的问题,解决方法:在创建项目时,在项目的Properties中增加archetypeCatalog,值为internal。
-
建立项目的目录结构并在pom中导入相关依赖坐标。
<properties> <!-- 版本锁定 --> <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>
-
-
在web.xml中配置一个servlet的映射,以核心控制器DispatcherServlet为例。
<servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 设置初始化参数,表示servlet一创建,就可以加载spring的配置文件,加载的路径由param-value标签中指定,进而初始化spring容器。当开启注解扫描后,扫描到的类将会处理为spring对象存入容器中 --> <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>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
-
在resources目录下配置spring的配置文件springmvc.xml。
开启注解扫描,保证能够扫描到java目录下的相关类,从而创建到spring容器中。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 配置spring创建容器时要扫描的包 --> <context:component-scan base-package="cn.sucrelt"/> <!-- 配置视图解析器 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 配置spring开启注解mvc的支持--> <mvc:annotation-driven/> </beans>
⭐️注:在SpringMVC的各个组件中,处理器映射器、处理器适配器、视图解析器称为SpringMVC的三大组件。 在
SpringMVC.xml
配置文件中使用<mvc:annotation-driven>
可以自动加载RequestMappingHandlerMapping(处理映射器)和RequestMappingHandlerAdapter(处理适配器),相当于注解处理器和适配器的配置。 -
编写index.jsp和HelloController控制器类。
-
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h3>入门程序</h3> <a href="hello">入门程序</a> </body> </html>
-
HelloController
@Controller public class HelloController { @RequestMapping(path = "/hello") public String sayHello() { System.out.println("hello springMVC"); return "success"; } }
-
-
在WEB-INF目录下创建pages文件夹,编写success.jsp的成功页面。
-
配置tomcat服务器并启动后执行该程序。
¶3.入门程序分析
-
入门案例的执行流程
- 当启动Tomcat服务器的时候,首先加载
web.xml
配置文件。正常情况下,servlet会在发送请求时创建,但是因为配置了load-on-startup
标签的值为1,所以会在启动服务器时就创建servlet对象,即DispatcherServlet对象,并设置初始化参数,加载springmvc.xml
配置文件。 - 在
springmvc.xml
中开启了注解扫描,java目录下的HelloController
类将会被spring扫描并创建到容器中。 - 从
index.jsp
发送请求,请求会先到达DispatcherServlet
核心控制器,核心控制器获取路径path的值并根据配置的@RequestMapping
注解进行匹配,从而找到执行的具体方法sayHello
。 - 根据执行方法的返回值
success
,再根据springmvc.xml
中配置的视图解析器,根据配置的前缀和后缀去指定的目录下查找指定名称的JSP文件。 - Tomcat服务器渲染页面,做出响应。
- 当启动Tomcat服务器的时候,首先加载
-
SpringMVC中的组件介绍
SpringMVC框架基于组件执行流程
-
前端控制器(DispatcherServlet)
- 用户请求到达前端控制器,它就相当于mvc模式中的c,是整个流程控制的中心,由它调用其它组件处理用户的请求,降低组件之间的耦合性。
- 接受用户的request请求,请求路径中包好“/hello”。
-
处理器映射器(HandlerMapping)
- 负责根据用户请求找到Handler即处理器,SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
- 将用户请求的路径中的hello与spring注解中扫描到的resultmapping的值为hello的方法进行映射,找到sayhello方法,并返回给前端控制器。
-
处理器适配器(HandlAdapter)
- 通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
- 前端控制器将找到的sayhello方法发送给适配器进行该方法的执行。
-
处理器(Handler)
- 开发中要编写的具体业务控制器。由DispatcherServlet把用户请求转发到Handler。由Handler对具体的用户请求进行处理。
- 适配器调用处理器进行方法的执行,并返回视图相关信息success到前端控制器。
-
视图解析器(View Resolver)
- 负责将处理结果生成View视图,View Resolver首 先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
- 前端控制器将返回结果传给试图解析器,并生成结果页面success.jsp。
- 视图(View)
-
¶4.RequestMapping注解
-
RequestMapping注解的作用是建立请求URL和处理方法之间的对应关系。
-
RequestMapping注解可以作用在方法和类上。
例如:浏览器跳转的访问路径为:
user/hello
- 当
RequestMapping
作用在类(如User类)上:代表第一级的访问目录(/user)。 - 当
RequestMapping
作用在类中的方法上(如User类中的hello方法):代表第二级的访问目录(/hello)。 - 这样可以实现项目的分模块开发。
- 当
-
RequestMapping的属性
-
path
:指定请求路径的url。 -
value
:和path属性是一样的。 -
mthod
:指定该方法的请求方式。@RequestMapping(value="/saveAccount",method=RequestMethod.POST)
-
⭐️
params
:指定限制请求参数的条件。控制器代码
@RequestMapping(value="/removeAccount",params= {"accountName","money>100"}) public String removeAccount() { System.out.println("删除了账户"); return "success"; }
JSP代码
<!-- 请求参数的示例 --> <a href="account/removeAccount?accountName=aaa&money>100">删除账户,金额100</a> <br/> <a href="account/removeAccount?accountName=bbb&money>100">删除账户,金额150</a> <a href="account/removeAccount?accountName=bbb&money>150">删除账户,金额150</a>
请求路径中的参数需要与params中的值匹配,当params中仅有参数名时,其值无需匹配,所以前两个超链接均可访问,最后一个超链接不能访问。
-
headers
:发送的请求中必须包含的请求头。
-
¶请求参数绑定
¶1.请求参数的绑定说明
- 绑定机制
- 表单提交的数据都是k=v格式的username=haha&password=123。
- SpringMVC的参数绑定过程是把表单提交的请求参数,作为控制器中方法的参数进行绑定的。
- 要求:前端提交表单的属性名必须和控制器方法中的参数名相同。
- 支持的数据类型
- 基本数据类型和字符串类型
- 实体类型(JavaBean)
- 集合数据类型(List、map集合等)
¶2.基本数据类型和字符串类型
-
提交表单的name和参数的名称是相同的。
-
区分大小写。
JSP代码
<h3>请求参数绑定-基本数据类型</h3> <a href="param/testParam?username=hehe">请求参数绑定</a>
控制器方法
@RequestMapping("/testParam") public String testParam(String username) { System.out.println("执行了..."); System.out.println("用户名:" + username); return "success"; }
¶3.实体类型(JavaBean)
-
提交表单的name和JavaBean中的属性名称需要一致。
-
⭐️如果一个JavaBean类中包含其他的引用类型,那么表单的name属性需要编写成:对象.属性。例如:
address.name
JSP代码
<h3>请求参数绑定-实体类型JavaBean</h3>
<form action="param/saveAccount" method="post">
姓名:<input type="text" name="username"><br/>
用户密码:<input type="text" name="password"><br/>
金额:<input type="text" name="money"><br/>
用户名:<input type="text" name="user.uname"><br/>
用户年龄:<input type="text" name="user.age"><br/>
<input type="submit" value="保存">
</form>
控制器代码
@RequestMapping("/saveUserAccount")
public String saveUserAccount(Account account) {
System.out.println("执行了...");
System.out.println(account);
return "success";
}
这种情况下,前端表单提交的所有属性都会再控制器中封装到Account对象中,封装过程默认会调用Account的setter方法,所有前端的属性名和Account对象中的属性要相同。
¶4.给集合属性数据封装
如果封装的对象中包含集合属性,如list和map,则在前端jsp页面中对应属性名的写法应该为:list[下标].属性名或map[‘键名’].属性名。
¶5.请求参数中文乱码的解决
在web.xml中配置Spring提供的过滤器类
<!--配置解决中文乱码的过滤器-->
<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>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
¶6.自定义类型转换
表单提交的任何数据类型全部都是字符串类型,但是后台定义Integer类型,数据也可以封装上,说明Spring框架内部会默认进行数据类型转换。如果想自定义数据类型转换,例如实现字符串到日期类型的转换,需要实现Converter接口。
-
新建一个自定义的类型转换的类,将字符串转换成日期类型,实现Converter接口。
public class StringToDateConverter implements Converter<String, Date> { @Override public Date convert(String source) { if (source == null) { throw new RuntimeException("值为空!"); } try { DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); Date date = df.parse(source); return date; } catch (Exception e) { throw new RuntimeException("类型转换错误!"); } } }
-
在springmvc配置文件中定义转换器的配置以及开启转换器的支持,springmvc中自定义类型转换的类为
ConversionServiceFactoryBean
,该类中包含属性private Set<?> converters
,注入该属性,其值为自定义类的全限定类名。<!--配置自定义的类型转换器--> <bean id="ConversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="cn.sucrelt.utils.StringToDateConverter"/> </set> </property> </bean> <!-- 配置spring开启注解mvc的支持--> <mvc:annotation-driven conversion-service="ConversionService"/>
-
在控制器中添加相关代码实现自定义转换。
User类中加入Date属性
public class User implements Serializable { private String uname; private Integer age; private Date date; ... }
前端jsp界面传递String类型的日期,输入为“yyyy-MM-dd”格式,经过自定义的类型转换后正常执行。
<form action="param/saveUser" method="post"> 用户名称:<input type="text" name="uname"><br/> 用户年龄:<input type="text" name="age"><br/> 用户生日:<input type="text" name="date"><br/> <input type="submit" value="保存"> </form>
控制器代码
@RequestMapping("/saveUser") public String saveUser(User user){ System.out.println("执行了..."); System.out.println(user); return "success"; }
¶常用注解
¶1.RequestParam注解
-
作用:把表单请求中指定名称的参数传递给控制器中的形参赋值,即当表单请求的属性名和控制器方法的参数名不一致时可使用此注解使其二者相关联。
-
属性
- value:请求参数中的名称。
- required:默认为true,表示请求参数中必须为此名称。
-
控制器代码示例
@RequestMapping(path="/hello") public String sayHello(@RequestParam(value="username",required=false)String name) { System.out.println(name); return "success"; }
¶2.RequestBody注解
-
作用:用于获取请求体的内容,get类型的请求不可用。
-
属性
-
required:是否必须有请求体,默认值是true
-
控制器代码示例
@RequestMapping(path="/hello") public String sayHello(@RequestBody String body) {//加上注解后,表示获取请求体而不是获取一个名为body的属性值 System.out.println(body); return "success"; }
¶3.PathVariable注解
-
作用:用于获取URL中的占位符。例如:url中有/delete/100,100就是占位符。
-
属性
-
value:指定url中的占位符名称
-
控制器代码示例
@RequestMapping(path="/hello/{id}") public String sayHello(@PathVariable(value="id") String id) { System.out.println(id); return "success"; }
-
REST编程风格及占位符
-
请求路径一样,可以根据不同的请求方式去执行后台的不同方法。
在REST中,四个表示操作方式的动词:GET、POST、PUT、DELETE,分别去对应四种基本操作:GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源。
- /account HTTP DELETE: 删除 id = 1的 account
- /account HTTP PUT: 更新id = 1的 account
- /account HTTP POST: 新增 account
- /account/1 HTTP GET : 得到 id = 1 的 account
- /account/2 HTTP GET: 得到 id = 2 的 account
因此,REST使用不同的请求方式来区分执行具体的控制器方法,url中都是相同的路径,当有相同url且请求方式也相同时,通过增加占位符进行区分。
-
restful风格的URL优点
- 结构清晰
- 符合标准
- 易于理解
- 扩展方便
-
¶4.RequestHeader注解
-
作用:获取指定请求头的值
-
属性
-
value:请求头的名称
-
控制器代码示例
@RequestMapping(path="/hello") public String sayHello(@RequestHeader(value="Accept") String header) { System.out.println(header); return "success"; }
¶5.CookieValue注解
-
作用:用于获取指定cookie的名称的值
-
属性
-
value:cookie的名称
-
控制器代码示例
@RequestMapping(path="/hello") public String sayHello(@CookieValue(value="JSESSIONID") String cookieValue) { System.out.println(cookieValue); return "success"; }
¶6.ModelAttribute注解
-
作用
- 出现在方法上:表示当前方法会在控制器方法执行前执行。
- 出现在参数上:获取指定的数据给参数赋值。
-
应用场景
- ⭐️当提交表单数据不是完整的实体数据时,缺少的字段为空值,保证没有提交的字段使用数据库原来的数据。
-
具体的代码
-
修饰方法有返回值
@ModelAttribute //该方法会先于控制器方法updateUSer执行 public User showUser(String name) { System.out.println("showUser执行了..."); // 模拟从数据库中查询对象 User user = new User(); user.setName("哈哈"); user.setPassword("123"); user.setMoney(100d); return user; } @RequestMapping(path="/updateUser") public String updateUser(User user) { System.out.println(user); return "success"; }
-
修饰方法没有返回值
@ModelAttribute public void showUser(String name,Map<String, User> map) { System.out.println("showUser执行了..."); // 模拟从数据库中查询对象 User user = new User(); user.setName("哈哈"); user.setPassword("123"); user.setMoney(100d); map.put("abc", user); } @RequestMapping(path="/updateUser") public String updateUser(@ModelAttribute(value="abc") User user) { System.out.println(user); return "success"; }
-
¶7.SessionAttributes注解
-
作用:用于多次执行控制器方法间的参数共享
-
属性
-
value:指定存入属性的名称
-
代码如下
@SessionAttributes(value= {"username","password","age"},types={String.class,Integer.class}) // 把数据存入到session域对象中 放在类上,该类中的控制器方法共享这些参数
¶响应数据和结果视图
¶1.返回值为String
-
在控制器方法中,以Model为方法参数,String作为返回类型。Model的addAttribute(keyname,keyvalue)方法可以将key放入request中,并转发到前端jsp文件中。
-
这种方式由视图解析器解析视图:控制器方法返回字符串success,作为逻辑视图名由视图解析器解析得到视图对象并返回给前端界面。
-
前端请求界面
<body> <a href="user/testString">testString</a><br> </body>
-
控制器代码
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/testString") public String testString(Model model) { System.out.println("testString执行了..."); //模拟从数据库查询数据 User user = new User(); user.setUsername("张三"); user.setAge(20); user.setPassword("123"); model.addAttribute("user", user); return "success"; } }
-
响应视图界面
<body> <h3>执行成功!</h3> <p>用户名:${user.username}</p> <p>用户年龄:${user.age}</p> </body>
¶2.返回值为void
-
控制器方法返回值为void时,可以通过请求转发、重定向以及直接响应三种方式进行数据响应。⭐️由于不经过视图解析器,所有路径名需要手动补全。
-
前端请求界面
<body> <a href="user/testVoid">testVoid</a><br> </body>
-
控制器代码
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/testVoid") public void testVoid(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("testVoid执行了..."); //1.request进行请求转发 // request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request, response); //2.response进行重定向 response.sendRedirect(request.getContextPath() + "/index.jsp"); //3.直接进行数据响应 // response.setCharacterEncoding("UTF-8"); // response.setContentType("text/html;charset=UTF-8"); // response.getWriter().print("响应void返回值"); return; } }
-
响应视图界面
<!--success.jsp--> <body> <h3>执行成功!</h3> <p>用户名:${user.username}</p> <p>用户年龄:${user.age}</p> </body> <!--index.jsp--> <body> index! </body>
¶3.返回值为ModelAndView的对象
-
ModelAndView时Spring提供的一个对象,可以在第一种方式的基础上,将返回值内容和逻辑视图名全部封装到该对象中并响应给前端界面。
- 调用
setViewName(ViewName)
来确定转发的jsp页面。 - 用
addObject(keyname,keyvalue)
把key放入request中。 - 最后返回ModelAndView对象。
- 调用
-
前端请求界面
<body> <a href="user/testModelAndView">testModelAndView</a> </body>
-
控制器代码
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/testModelAndView") public ModelAndView testModelAndView() { System.out.println("testModelAndView执行了..."); //模拟从数据库查询数据 User user = new User(); user.setUsername("李四"); user.setAge(21); user.setPassword("456"); //创建ModelAndView对象 ModelAndView mv = new ModelAndView(); //存储user对象,该方法会将该对象存入request域中 mv.addObject("user", user); //设置逻辑视图 mv.setViewName("success"); return mv; } }
-
响应视图界面
<body> <h3>执行成功!</h3> <p>用户名:${user.username}</p> <p>用户年龄:${user.age}</p> </body>
¶4.通过SpringMVC框架提供的转发和重定向返回(了解)
-
代码
@RequestMapping("/testForwardOrRedirect") public String testForwardOrRedirect(){ System.out.println("testForwardOrRedirect方法执行了..."); // 请求的转发 // return "forward:/WEB-INF/pages/success.jsp"; // 重定向 return "redirect:/index.jsp"; }
-
说明
- forward相当于
request.getRequestDispatcher("url").forward(request,response)”
; - redirect相当于
response.sendRedirect(url)
。重定向界面不能时WEB-NIF中的jsp界面。 - ⭐️⭐️⭐️WEB-INF是Java的WEB应用的安全目录。所谓安全就是客户端无法访问,只有服务端可以访问的目录。页面放在WEB-INF目录下面可以限制访问,提高安全性。既然WEB-INF是安全目录,客户端无法访问,而重定向就相当于用户直接从客户端访问了的路径,就无法访问WEN-INF下的界面,只有程序内部进行请求转发时才能转发到WEB-INF下的JSP界面。
- forward相当于
¶5.实现Ajax异步传递json数据
前端通过ajax异步发送数据,控制器接受数据,导入的jar包将json数据转为JavaBean对象,做相应的业务处理后返回响应结果到前端,前端接受json数据并显示。
-
解决静态资源拦截的问题
在前端控制器的配置中,设置了路径为
/
,即所有目录下都会经过前端控制器处理,对于静态资源,注入js、css、images等会被拦截导致前端界面加载失败。在springMVC的配置文件中使用
mvc:resources
抱歉保证静态资源不被过滤,location
表示静态资源的位置,mapping
表示访问静态资源的匹配路径。假设浏览器访问的静态资源路径为/js/jquery.min.js
,实际将会访问项目中的/js/jquery.min.js
。<!-- 设置静态资源不过滤 --> <mvc:resources location="/css/" mapping="/css/**"/> <mvc:resources location="/images/" mapping="/images/**"/> <mvc:resources location="/js/" mapping="/js/**"/>
-
json字符串和JavaBean对象互相转换的过程中,需要使用jackson的jar包实现自动转换。
<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>
-
前端使用ajax发送异步请求
<script> $(function () { $("#btn").click(function () { $.ajax({ url: "user/testAjax", contentType: "application/json;charset=UTF-8", data: '{"username":"zhangsan","age":20, "password":"123"}', dataType: "json", type: "post", //接收到响应的数据后显示 success: function (data) { alert(data); alert(data.username); alert(data.age); alert(data.password); } }); }); }); </script> ... <button id="btn">发送ajax请求</button>
-
控制器代码
@ResponseBody
注解修饰返回值,会自动把返回值的JavaBean对象转换成json字符串再响应。@RequestBody
注解修饰参数,会把前端传递的json字符串自动转换成JavaBean的对象。
@RequestMapping("/testAjax") public @ResponseBody User testAjax(@RequestBody User user) { System.out.println("testAjax方法执行了..."); System.out.println(user); //模拟查询数据库 user.setUsername("lisi"); user.setAge(21); //响应给前端 return user; }
¶文件解析器组件实现文件上传
¶1. 应用场景
传统方式的文件上传,指的是上传的文件和访问的应用存在于同一台服务器上。 并且上传完成之后,浏览器可能跳转。
¶2. 导入文件上传相关的jar包
<!-- 文件传输的依赖包 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
¶3. 前端界面
<h3>文件上传</h3>
<form action="file/fileUpload" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="uploadFile"/><br/>
<input type="submit" value="上传"/>
</form>
- form表单的enctype取值必须是:multipart/form-data,enctype:是表单请求正文的类型;
- method属性取值必须是Post;
- 提供一个文件选择域input。
¶4. 控制器代码
@Controller
@RequestMapping("/file")
public class FileUpLoadController {
@RequestMapping("/fileUpload")
public String fileUpLoad(HttpServletRequest request, MultipartFile uploadFile) throws IOException {
System.out.println("SpringMVC方式进行文件上传...");
// 先获取到要上传的文件目录
String path = request.getSession().getServletContext().getRealPath("/uploads");
// 创建File对象,一会向该路径下上传文件
File file = new File(path);
// 判断路径是否存在,如果不存在,创建该路径
if (!file.exists()) {
file.mkdirs();
}
// 获取到上传文件的名称
String filename = uploadFile.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
// 把文件的名称唯一化
filename = uuid + "_" + filename;
// 上传文件
uploadFile.transferTo(new File(file, filename));
return "success";
}
}
- ⭐️SpringMVC框架提供MultipartFile类用于实现文件上传的组件。控制器方法的参数中MultipartFile的参数名必须与前端文件的name属性相同。
- 为每个文件设置一个uuid,保证多次上传相同的文件时不会因为同名而覆盖。
¶5. 配置文件解析器
<!-- 配置文件解析器对象,要求id名称必须是multipartResolver -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"/>
</bean>
¶6.⭐注意事项
-
前端界面中的相对路径:
-
不加斜杠:参考当前访问的路径。
-
加斜杠:参考根路径。
所以在本实例中前端路径应该不加/,index.jsp的路径为
http://localhost:8080/SpringMVC_FileUpLoad_war/
,不加/点击跳转后的路径为http://localhost:8080/SpringMVC_FileUpLoad_war/file/fileUpload
,如果加上/就变成http://localhost:8080/file/fileUpload
导致404错误。
-
-
后台服务器中的代码,参考的根目录为webapp,如在springmvc.xml中设置的静态资源不过滤
<mvc:resources location="/css/" mapping="/css/**"/>
,前面加上/表示相对webapp下。 -
控制器中获取的path为文件上传到服务器的目录,uploads是目标文件夹,取决于服务器布置的位置,使用tomcat布置时,该path的值为
D:\Java\tomcat8\webapps\SpringMVC_FileUpLoad_war\uploads
。
¶异常处理器组件实现异常处理
¶1. 应用场景
当service、web、dao层的业务发生异常时,后台代码都会向上抛出异常,在前端控制器设置一个异常处理器用于自定义处理异常,并在前端返回一个用户友好的出错提示界面。
¶2.自定义异常类
public class SysException extends Exception {
private String message;
public SysException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
¶3.控制器代码
模拟业务发生异常的情况,使用10/0作为模拟的异常情况,出现异常后使用自定义的异常类存储异常信息。
@Controller
public class HelloController {
@RequestMapping(path = "/hello")
public String sayHello() throws SysException {
System.out.println("hello springMVC");
// 模拟发生异常
try {
int a = 10 / 0;
} catch (Exception e) {
// 打印异常信息
e.printStackTrace();
// 抛出自定义异常信息
throw new SysException("模拟异常:查询所有用户出现错误了...");
}
return "success";
}
}
¶4.实现异常处理器类
过程分析📋:该类实现了SpringMVC框架中提供的HandlerExceptionResolver
类,其中参数Exception为业务异常时抛出的异常,这里实际上是自定义的异常SysException。对该异常进行相应的处理最后返回ModelAndView对象,将异常信息发送给前端并设置跳转页面。
public class SysExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
SysException e = null;
// 获取到自定义的异常对象
if (ex instanceof SysException) {
e = (SysException) ex;
} else {
// 非自定义异常对象,提示出错
e = new SysException("系统正在维护...");
}
// 创建ModelAndView对象
ModelAndView mv = new ModelAndView();
// 存入错误的提示信息
mv.addObject("errorMsg", e.getMessage());
// 跳转的Jsp页面
mv.setViewName("error");
return mv;
}
}
¶5. 配置异常处理器
在springmvc.xml中配置自定义的异常处理器
<bean id="sysExceptionResolver" class="cn.sucrelt.exception.SysExceptionResolver"/>
¶6. 设置相关的前端界面
index.jsp
<body>
<h3>异常处理</h3>
<a href="hello">异常处理</a>
</body>
error.jsp
<body>
${errorMsg}
</body>
¶拦截器
¶1. 拦截器概述
-
SpringMVC中的拦截器用于在控制器方法执行前后进行预处理和后处理。
-
拦截器类似于Servlet中的过滤器
- 过滤器是Servlet规范的一部分,任何框架都可以使用过滤器技术;拦截器是SpringMVC框架独有的。
- 过滤器配置了/*,可以拦截任何资源;拦截器只会对控制器中的方法进行拦截。
¶2. 自定义拦截器类
自定义拦截器需要实现SpringMVC框架中的HandlerInterceptor接口。
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器执行了...");
return true;
}
}
该接口中的三个主要方法:
- preHandle,在controller方法前执行
- 可以使用request或者response跳转到指定的页面。
- 返回true表示放行,执行下一个拦截器,如果没有拦截器,执行controller中的方法。
- 返回false表示不放行,不会执行controller中的方法。
- postHandle,在controller方法后、JSP视图前执行
- 可以使用request或者response跳转到指定的页面。
- 如果指定了跳转的页面,controller方法依旧执行,但其中的跳转页面将不会显示而现实拦截器指定的页面。
- afterCompletion,在JSP执行后执行
- request或者response不能再跳转页面了。
- 在 前端控制器完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。
¶3. 配置拦截器
在springmvc.xml中配置拦截器,配置拦截器链时需要使用 <mvc:interceptor>
配置多个拦截器。
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 哪些方法进行拦截 -->
<mvc:mapping path="/user/*"/>
<!-- 哪些方法不进行拦截
<mvc:exclude-mapping path=""/>
-->
<!-- 注册拦截器对象 -->
<bean class="cn.sucrelt.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>