1、SpringMVC
1.1 SpringMVC 第一天
1.1.1 今日内容
1.1.1.1 SpringMVC的开始:
1. 三层架构:平时是C/S或B/S架构,我们平时做web开发是用B/S架构的开发;服务端就经常分为三层架构;
1. 表现层:框架[SpringMVC]:浏览器与服务器端进行数据的交互;服务器接收浏览器的参数,然后调用业务层的方法,拿到结果哦数据,然后响应给浏览器结果;
2. 业务层:框架[Spring]:业务层与持久层进行交互,它响应表现层进行方法执行,并联系持久层的该方法,拿到结果返回给表现层;
3. 持久层:框架[Mybatis]:通过业务层的指令执行对数据库的操作,拿到数据后返回给业务层;
2. MVC设计模型:
1. M:model 模型 JavaBean [mybatis,Spring] -->执行者
2. V:view 视图 JSP [jsp,html等页面] -->显示
3. C:Controller 控制器 Servlet [SpringMvc] -->接收请求完成响应
3. 概述:
* 基于java实现的MVC设计模型的请求驱动类型的轻量级WEB框架,属于SpringFrameWork的后续产品,已经融合在Spring web Flow里面;
4. 优势:
1. 清晰的角色划分 [模块开发]
* 前端控制器
* 请求到处理器映射 [请求路径]
* 处理器适配器
* 视图解析器 [跳转到xxx.jsp页面]
* 处理器或页面控制器
* 验证器
* 命令对象
* 表单对象
->>当请求进入SpringMVC中的时候,会经过这些一系列的模块,每个组件都会对这个请求体做一些事情,当走完的时候就直接返回结果;
->>
* 清晰明了,扩展修改容易,每个各司其职;
* 可以使用命令对象直接作为业务对象
* ...
5. SpringMVC和Struts2的优劣分析:
1. 共同点:
* 都是表现层框架,基于MVC模型编写的
* 底层都离不开原始ServletAPI
* 处理请求的机制都是一个核心控制器
2. 区别:
* Spring MVC入口是Servlet,而Struts2是Filter [Struts的核心就是过滤器]
* SpringMVC 是基于方法设计的,Struts是基于类;struts每次执行都会创建一个类,所以SpringMVC会稍微比Struts2快些; [SpringMVC是单例的,所以省去创建对象这个过程]
* SpringMVC使用更加简洁,同时还支持JSR303,处理ajax的请求更方便
* Struts2的OGNL的表单式使页面的开发效率相比Spring MVC更高些,但是执行效率并没有JSNL表达式高;
1.1.1.2 SpringMVC的入门程序:
1. 需求分析: 编写index.jsp超链接标签,发送请求给SpringMVC表现层的后台,编写一个类并编写方法并返回结果,让程序转发到成功的jsp页面上;
2. 入门实现案例:
1. 开始:
1. 需要搭建开发的环境
* 构建Maven项目 jdk.1.8|骨架构建|后缀为:webapp、
* cn.itcast|springmvc_day01_01_start
* 下一步后添加一组键值对:[解决maven创建过慢] archetypeCatalog | internal
2. 环境搭建:
1. 在main下创建java包,resources包[Mark Diretory as 将java和resources的类型转换]
2. pom.xml引入:
* 拷入依赖:
* <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> </dependencies>
3. 配置前端控制器: [servlet]
1. 在web.xml 中添加:
* <web-app>
* <display-name>Archetype Created Web Application</display-name>
* <servlet>
* <servelt-name>dispatcherServlet</servlet-name>
* <servelt-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
* <init-param>
* <param-name>contextConfigLocation</param-name>
* <param-value>classpath:springmvc.xml</param-value>
* </init-param>
* <load-on-startup>1</load-on-startup>
* </servlet>
* <servelt-mapping>
* <servlet-name>dispatcherServlet</servlet-name>
* <url-prrten>/</url-pattern>
* </serlvet-mapping>
* </wen-app>
2. 配置核心配置文件:
* 创建springmvc.xml [resources下] 导入其约束
3. 开始入门程序
1. 在index.jsp中编写客户端;
2. 在java.cn.itcast.controller.HelloController中编写控制器类:
* public String sayHello(){
* System.out.println("Hello StringMVC")
* return "sucess"; //返回susess,就得提供这个名字的jsp名字
* }
3. 将该类变成对象,即在springmvc.xml中开启注解扫描:在文档中找
* <context compontent...
4. 在HeeloController上面添加@Controller注解[servelt标识]和@RequestMapping(path="/user")【一级目录】,并在方法上添加注解@RequestMapping(path="/hello") [请求映射,这个注解路径hello就是以后该类该方法的请求路径]
5. 让mvc这个配置文件加载,在前端控制器中配置加载,web.xml,提供初始化参数:
* <init-param>
* <param-name>contextConfigLocation</param-name>
* <param-value>classpath:springmvc.xml</param-value>
* </init-param>
6. 创建一个名为sucess.jsp的成功界面
* webapp.WEB-INF.pages.success.jsp
* 在springmvc.xml配置视图解析器:
* <bean id="internalResourceViewResolver" class="org.spring...internalResourceViewResolver..."></bean>
* <property name="prefix" value="/WEB-INF/pages"> //prefix 前缀
* <property name="suffix" value=".jsp"> //suffix 后缀
* </bean>
* //第一个hellocontruller中的sayhello方法中返回sueccess,会通过视图解析器添加前后缀和路径,直接找到了动态包中的seccess.jsp页面;可以直接访问静态页面,但是WEB-INF里面的内容只能请求转发进去;
* 很多公司就将一些登录后的页面直接放到web-inf里面,这样就免去了很多的验证操作;
* 开启springMVC框架注解的支持
* <mvc:annotation-driven/>
7. 补全两个jsp网页
* <a href="hello" >入门程序</a>
* 没有虚拟路径的时候,/hello或者hello都可以
* 有虚拟路径的时候
* "/hello" ->[绝对路径] http://locathost:8080/hello
* "hello" ->[相对路径] http://locathost:8080/springmvc-day01/hello
->> 相对路径是相对于当前文件的路径,会添加上虚拟路径跳到相对路径上;绝对路径就会调到http://locathost:8080/hello,不会添加虚拟路径地址了;
* 但是在工作中我们会直接使用绝对路径,使用相对路径多次的跳转容易出现问题
3. 解析:
1. 启动服务器,加载一些配置文件
1. DispatcherServlet对象呗创建
2. springmvc.xml被加载了
3. HelloController创建成对象
2. 发送请求,后台处理请求
3. 流程分析
1. 连接指向项目名称/hello->DispatcherServlet[控制作用,指挥中心]->先找@RequestMapping(path="hello")sayHello方法执行,返回success,然后控制器找视图解析器对象:InternalResourceViewResolver然后拼接前后缀和位置,找到succes.jsp然后返回结果;
2. DispatcherServlet:它就是一个控制器,实际上没有处理问题的能力,但是它可以调度其他的模块组件,完成任务;
3. 组件功能:
1. 处理器映射器:[HandlerMapping]--> 负责解析浏览器发来的地址,让Controller类中方法去执行/hello 返回具体的哪个类和哪个方法
2. 处理器适配器:[HandlerAdapter]-->适配器模式,让过来的任何类和任何方法都转为可识别的方法和类,然后进行执行操作; //将千奇百怪的东西适配成格式统一的类,方法然后给处理器
3. 处理器:[Handler]:处理器平常也叫作Controller :拿到执行代码进行执行,然后给视图解析器结果;
4. 视图解析器:如果是返回"success"等,就会调用视图解析器进行解析,并跳转到xxx.jsp页面;
5. View 视图:接收到数据,将数据进行渲染,变成好看的页面Jsp,Freemarker,Excel,Pdf等...
6. 处理器映射器,处理器适配器,视图解析器称为SpringMVC的三大组件;
1.1.1.3 请求参数的绑定
1. @RequestMapping的参数内容:
1. @AliasFor("path"): 这个注解的意思是别名 因为@RequestMapping里的path和value值一样,所以平时写指定路径的时候,可以不写path,因为path=value,而value又可以省略;
2. @RequestMapping()里面的值:
1. value:用于指定请求的URL。它和path属性的作用是一样的。 [指定发送请求的路径]
2. method:用于指定允许的请求方式,不是此请求的方式将不被允许; [指定发送请求的方式]
3. params={"username"}:表示浏览器发送的请求必须有key值为username [指定发送请求的条件]
4. params={"username=heihei"}:表示浏览器发送的请求必须含有key值为username,对应的value值为heihei [指定发送请求的条件]
5. headers={"Accept"}:表示浏览器发送的请求头必须包含Accept [指定限制请求消息头的条件]
2. 请求参数绑定入门 [把表单提交的请求参数,作为控制器中方法的参数进行绑定]
1. 绑定机制:
1. 表单提交的数据都是k=v格式的。username=haha&password=123
2. 把表单提交的请求参数,作为控制器中方法的参数进行绑定 [方法的参数名和表单中的key值是一样的,那么mvc就会自动的将表单的value值绑定到方法的参数上;]
3. 要求:提交表单的name和方法参数的名字一样;
2. 请求参数的绑定: [示例]
1. 浏览器:
* <a href="param/testParam?username=heihei&password=123"></a>
* //这里不加/如果加了/则会是绝对路径,如果加/必须提交上下文路径,就是添加上虚拟路径;
2. 服务器:ParamController类加注解:@Controller和@RequestMapping("param")
3. 示例:
* @Controller //该注解交给容器管理,表名它是一个控制器
* @RequestMapping()
* public class ParamController{
* //请求参数绑定入门
* @RequestMapping("/testParam")
* public String testParam(String username,String passowrd){
* System.out.println("执行了");
* System.out.println("username");
* System.out.println("password");
* return "succuess";
* }};
4. 传入对象:
1. 对象的封装:
1. 注意:如果方法内传入的是对象,而请求传入的是单一的数据,如果对象内的成员变量与request传入的数据是一样的,那么数据会自动与传入的对象绑定;
2. 示例:
1. 实体类
* public Account implements Serializable{
* private String username;
* pricate String password;
* private Date birthday;
* get,set方法,to string方法...
* }
2. * 浏览器:
* <form action="param/saveAccount" method="post">
* 用户名:<input type=""text" name="username"></br/>
* 密码:<input type=""text" name="password"></br/>
* 金额:<input type=""text" name="birthday"></br/>
* <input type=""submit" value="提交"></br/>
3. 控制器:
* @Controller //该注解交给容器管理,表名它是一个控制器
* @RequestMapping()
* public class ParamController{
* //请求参数绑定把数据封装到javaBean中;
* @RequestMapping("/saveAccount")
* public String saveAccount(Account account){
* System.out.println("执行了");
* System.out.println("account");
* return "success";
* }};
2. 引用类型的封装:如果在实体类中还有一个引用对象:
1. 实体类:Account
* public Account implements Serializable{
* private String username;
* pricate String password;
* private Date birthday;
* private User user;
* user 的get,set等方法...
* get,set方法,to string方法...
* }
2. 实体类:User
* private User implements Serializable{
* private String uname;
* private String age;
* }
3. 浏览器:
* <form action="param/saveAccount" method="post">
* 用户名:<input type=""text" name="username"></br/>
* 密码:<input type=""text" name="password"></br/>
* 生日:<input type=""text" name="birthday"></br/>
* user的名字:<input type=""text" name="user.uname"></br/>
* user的年龄:<input type=""text" name="user.age"></br/>
* <input type=""submit" value="提交"></br/>
3. 解决中文乱码问题:
1. 导入过滤器即可;
2. 如果web-app标签报错:
* 将过滤器放在servlet配置文件的前面
* 导入约束,提高版本;
3. 配置文件中的问题:
1. classpath的意思:类路径,就是编译过后的类路径
2. <load-on-startup>1</load-on-startup>
* 服务器启动的时候servlet不会被创建,它是在需要的时候第一次被创建,以后就不会再创建了; [单例]
* 只要加上此代码,则服务器一启动就会创建Servlet对象;在工作中,都会使用此,让加载时间花在启动中,也不要让用户停留等待;
3. 对象中嵌套集合,集合中储存对象,对象内含有成员数据;
1. 浏览器:
* <form action="account/updateAccount" method="post">
* 用户名称:<input type="text" name="username" ><br/>
* 用户密码:<input type="password" name="password" ><br/>
* 用户年龄:<input type="text" name="age" ><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].name" ><br/>
* 账户 2 金额:<input type="text" name="accounts[1].money" ><br/>
* 账户 3 名称:<input type="text" name="accountMap['one'].name" ><br/>
* 账户 3 金额:<input type="text" name="accountMap['one'].money" ><br/>
* 账户 4 名称:<input type="text" name="accountMap['two'].name" ><br/>
* 账户 4 金额:<input type="text" name="accountMap['two'].money" ><br/>
* <input type="submit" value="保存">
* </form>
2. 客户端:
* @RequestMapping("/updateAccount")
* public String updateAccount(User user) {
* System.out.println("更新了账户。。。。"+user);
* return "success";
* }
4. 自定义类型 【在进行表单数据与控制器方法参数的绑定的时候,大多数的数据都可以进行自动转换】
1. String-->Integer [常用类型可以自动转换]
2. 2000/11/11 -->Date [可以转换]
3. 2000-11-11 -->转换失败,报错~ [spring只认识/的日期,不认识/的日期]
* 案例解决:
1. 浏览器:
* <form action="param/saveUser" method="post">
* 用户生日<input type="text" name="age" ><br/>
* <input type="submit" value="保存">
2. 控制器方法:
* @RequestMapping("saveUser")
* public String saveUser(User user){
* System.out.println("user.date")
* return "success";
* }
3. 创建工具类:utils.StringToDateConverter
* public class StringToDateConverter implements Converter<String,Date>{
* @Overide
* public Date convert(String source){
* //传入进来的字符串
* public Date convert(String source){
* if(source=null){
* throw new RuntimeException("请您传入数据");
* }
* DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
* //把字符串转换成日期
* try{
* return df.parse(source);}
* catch(Exception e){
* throw new RuntimeException("数据类型转换出现错误");
* }} };
4. 在springmvc.xml中配置自定义类型转换器:
* <bean id="conversionService" class="rg.springframework.context.support.ConversionServiceFactoryBean">
* <property name="converters">
* <set>
* <bean class="cn.itcast.utils.StringToDateConverter">
* </set>
* </bean>
5. 在springmvc.xml中配置支持:
* <mvc:annotation-driven conversion-service="conversionService"/>
* //使用此配置,将开启对自定义类型转换器的支持;
4. 使用此方法,只能解决使用-的问题可以识别日期,但是自动的/的办法就不能使用了;
1. 问题:如何使输入日期2012/6/5 和2012-6-5都可以被识别;
答:在工具中修改
* public class StringToDateConverter implements Converter<String,Date>{
* Date date=null
* @Overide
* public Date convert(String source){
* //传入进来的字符串
* public Date convert(String source){
* if(source=null){
* throw new RuntimeException("请您传入数据");
* }
* DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
* //把字符串转换成日期
* try{
* return df.parse(source);}
* catch(Exception e){
* throw new RuntimeException("数据类型转换出现错误");
* }} };
2. 中文乱码解决:使用前面的代码只能解决post的中文乱码问题,表单提交也基本使用psot,如果需要使get请求的乱码问题也解决,可以使用tomcat 8以上或者网上搜索解决方案;
1.1.1.4 常用注解
1. 原生API:
* @RequestMapping
* public String testServlet(HttpServletRequest request,HttpServletResponse response){
* //拿到request
* System.out.prinlnt(request);
* //拿到session
* HttpSession session=request.getSession();
* //拿到ServletContext
* ServletContext servletContext=session.getServletContext();
* }
2. 常用注解:
1. @RequsetParam:对方法的参数里进行定义:必须传入此属性,如果不传则报错,浏览器发送请求带的数据必须与这个一样;
* public String testRequestParam(@RequestParam(name="name")String username){
* System.out.println(username);
* return "success";
* };
2. @RequestBody:用于获取请求体的内容,直接使用得到的是key=value&key=value...结构的数据,它拿到的是整个请求体的内容;
1. 示例:
* @RequestMapping("/testRequstBody")
* public String testRequestBody(@RequestBody String body){
* System.out.println("执行了...");
* System.out.println("body");
* return "success";
* }
2. 作用范围:JSON的数据在请求体里,它可以与json配合使用;使用该注解可以直接拿到json的全部内容;
3. @PathVaribale注解
1. REST风格URL:
1. 不是规范,不是强制要求使用,觉得好就用;
2. 优点:
2. restful编程风格
1. 原来方式:
* UserController类
* @RequestMapping(path="/user/save")
* save
*
* @RequestMapping("/user/save")
* update
*
* @RequestMapping("/user/save")
* findAll
2. 现在的方式:
* UserController类
* path="/user" post
* save
*
* save="/user" put
* update
*
* path="/user" get
* findAll
* localhost:8080/user get
*
* path="/user" get
* fmdByID(id)
* localhost:8080/user/{id} get
3. 演示:
1. 演示控制类方法
* @RequestMapping("/testPathVariable/{id}")
* public String testPathVariable(@PathVariable(name="sid")String id){
* System.out.println(id);
*
* }
2. 浏览器方法:
* <a href="/testPathVariable/10">点我</a>
4. 使用过滤器方式将请求进行转换:
1. WebClient类可以直接使用静态方法发送请求,并且可以模拟各种请求方式进行测试;
2. 不是很重要,需要的话可以查看笔记;
5. 注意:
* 使用相对路径,又转发到本页面,会出现重复相对路径,导致地址出错,无法访问;因为相对路径的查找其他路径是从它本来相对路径的基础上进行查找,就会重叠,如果查找页面的时候使用绝对路径就不会出现这个问题了;
* isELIgnored="false" 不忽略el表达式;
* 示例:
* <%@ page contextType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
1.2 SpringMVC 第二天
1.2.1响应
1.2.1.1 概述:就是把后台的数据返回给浏览器/用户;
1.2.1.2搭建环境:
1.创建项目:Maven->选择骨架或者手动添加… 在main中创建java,resources,webapp三个包,webapp包下创建WEB-INF,pages:创建succes.jsp;[成功的返回页面]
2.导入相关坐标… [可以应用版本锁定]
3.引入前端控制器[固定写法]
4.springmvc.xml:开启注解扫描,视图解析器,注解支持
5.删除webapp中的index.jsp重新创建一个;
1.2.1.3 响应数据和结果视图:
1.Controller方法返回字符串 【使用Model存入数据再返回】
1.解析: Controller方法执行后,返回的字符串根据视图解析器所前后拼接的文件名和地址,就能成功的跳转到所需的页面;
2.jsp页面接收对象:
1.在Controller方法中,使用model.addAttribute(“user”,user) [该方法需传入一个Model]
2.Model不需要我们创建,这是框架为我们生成的;
3.isELIgnored=“false” [识别el表达式]
4.使用${user.username}进行取值…
示例:
* @Controller
* @RequestMapping("/user")
* public class UserController {
* /**
* *请求参数的绑定
* */
* @RequestMapping(value="/initUpdate")
* public String initUpdate(Model model) {
* // 模拟从数据库中查询的数据
* User user = new User();
* user.setUsername("张三");
* user.setPassword("123");
* user.setMoney(100d);
* user.setBirthday(new Date());
* model.addAttribute("user", user);
* return "update";
* }
* }
2.Controller方法返回void 【return;】
1.请求转发:请求转发是一次请求,不能简写 [手动跳动转发,不会触动视图解析器,需要自己写全路径和文件格式]
示例:request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);
2.重定向:请求转发是可以直接访问web-inf下的文件,所以不用写虚拟地址;
reponse.sendRedirect(request.getContextPath()+"WEB-INF/pages/xxx.jsp")
3.解决中文乱码:
reponse.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
4.直接响应:
response.getWriter().print("你好");
5.注意:程序运行到响应,那么响应后面的代码应写上return;
1.一次请求对应一次响应,所以response.getWriter.print(“xxx”),里面打印的需要在整个方法执行完才会整体的去响应,在此之前它是将打印的语句储备起来,还未发送响应;所以使用return;进行结尾,那么就可以不让框架继续执行其他,直接返回;
6.使用框架后,request,response这些以后都不经常用了;
7.为什么domain实体类要实现Serializable接口?
1.它是实体类,专门用来存储类,它存在于内存中,实现序列化接口,可以将封装好的类保存到硬盘中,如果突然断电关机什么意外情况,可以反序列化还原;
2.实现该接口,才可以将该对象远程调用,远程传输到其他的服务器,传输时以流的形式
8.实体类中的常见问题:
1.如果实体类中写了有参构造,那么需要再为它创建一个无参构造;
2.成员变量最好使用private修饰,加强安全性,只能通过get,set对其操作;
3.成员变量尽量使用包装类型,不要使用基本类型,即尽量使用Integer类,而不使用int基本类型;
4.Integer修饰的变量可以为空,但基本类型不能为null; 比如:private Integer ge=null;
3.ModelAndView:
1.把user对象储存到mv对象中,也会把user对象存入到request对象;
2.它既可以储存键值对[发挥model],它也可以转发视图[view]的作用;
示例:
* @RequestMapping("/testModelAndView")
* public ModelAndView testModelAndView(){
* ModelAndView mv =new ModelAndView();
* System.out.println("testModelAndView方法执行了...")
* User user=new User();
* user.setUsername("小枫")
* user.setPassword(123);
* user.setAge(30);
* mv.addObject("user",user);
* mv.setViewName("success");
* reutrn mv;
* }
3.使用关键字的方式进行转发或者重定向 [使用关键字是不会被视图解析器所管理,需要自己手动补全路径。
@RequestMapping("/testForwardOrRedirect")
public String testForwardOrRedirect(){
//请求的转发:
return "forward:/WEB-INF/pages/success.jsp";
//重定向
return "redirect:/index.jsp";
}
4.ResponseBody响应json数据
1.DispatcherServlet会拦截到所有的资源,导致一个问题就是静态资源(img、css、js)也会被拦截到,从而不能被使用。解决问题就是需要配置静态资源不进行拦截,在springmvc.xml配置文件添加如下配置:
1.//前端控制前,哪些资源不拦截:
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/images/" mapping="/images/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>
2.特点:
1.mvc:resources标签配置不过滤
2.ocation元素表示webapp目录下的包下的所有文件
3.mapping元素表示以/static开头的所有请求路径,如/static/a 或者/static/a/b
5.模拟示例:
1.模拟异步请求响应:
1.服务器:
@RequestMapping("/testAjax")
public void testAjax(@RequestBody String body){
//
System.out.println(body);
}
2.浏览器:
$(function(){
$("#btn").click(funcation(){
$.ajax({
url:""user/testAjax",
contextType:"application/json;charset=UTF-8", //设置了这种格式,提交是以json格式提交,如果不以此设置,$.get |$.post 则都是表单提交格式
data:'{"username":"heihei","password":"123","age":30}',
dataType:"json",
type:"post",
success:function(date){
//data服务器端响应的json数据,进行解析。
}
})
})
如果传入已经定义好了的domain里User实体类,那么也可以直接传入这个实体类,只要属性值对应的上,那么springmvc就会自动绑定,与ajax发送的属性值一一对应;
1.2.2 文件上传
1.2.2.1文件上传概述
1.文件上传三要素: [只要是文件上传,就必须满足这三个要素]
1.form表单的enctype取值必须是:multipart/form-data
默认值是:application/x-222-form-urlencoded
enctype:是表单请求正文的类型
2.method属性必须是post
get请求会把文件携带在地址栏上,而get请求是有大小限制的;
3.提供一个文件选择域而且必须有name属性:
<input type="file">
2.借助第三方组件实现文件上传:
导入commons-fileupload-1.3.1.jar和commons-io-2.4.jar
3.案例实现:
1.2.2.2 文件上传实战案例
1.搭建开发环境:
1. 创建一个新的maven项目: 坐标,springmvc.xml,web.xml
<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>
2.补全包路径
3.编写文件上传的JSP页面:
<h3>文件上传</h3>
<form action="user/fileupload" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"/><br/>
<input type="submit" value="上传文件"/>
</form>
4.编写文件上传的Controller控制器:
@RequestMapping(value="/fileupload")
public String fileupload(HttpServletRequest request) throws Exception {
// 先获取到要上传的文件目录
String path = request.getSession().getServletContext().getRealPath("/uploads");
// 创建File对象,一会向该路径下上传文件
File file = new File(path);
// 判断路径是否存在,如果不存在,创建该路径
if(!file.exists()) {
file.mkdirs();
}
// 创建磁盘文件项工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(factory);
// 解析request对象
List<FileItem> list = fileUpload.parseRequest(request);
// 遍历
for (FileItem fileItem : list) {
// 判断文件项是普通字段,还是上传的文件
if(fileItem.isFormField()) {
}else {
// 上传文件项
// 获取到上传文件的名称
String filename = fileItem.getName();
// 上传文件
fileItem.write(new File(file, filename));
// 删除临时文件
fileItem.delete();
}
}
return "success";
}
5.SpringMVC实现原理分析:
1.选择文件后点击上传;
2.当文件上传后,通过request进入到前端控制器
3.前端控制器通过添加的配置文件解析器,解析request并返回upload
4.前端控制器调用Controller,将upload传入Controller类下的fileuoload2(MultipartFile upload),然后对其进行操作;
5.注意:前端的name属性值必须和后端Controller类下finduoload2内传入的参数名保持一致;
6.实现代码:
1.SpringMVC传统方式文件上传:
1.SpringMVC框架提供了MultipartFile对象,该对象表示上传的文件,要求变量名必须和表单file标签的name属性相同;
2.代码如下:
@RequestMapping(value="/fileupload2")
public String fileupload2(HttpServletRequest request,MultipartFile upload) throws
Exception {
System.out.println("SpringMVC方式的文件上传...");
// 先获取到要上传的文件目录
String path = request.getSession().getServletContext().getRealPath("/uploads");
// 创建File对象,一会向该路径下上传文件
File file = new File(path);
// 判断路径是否存在,如果不存在,创建该路径
if(!file.exists()) {
file.mkdirs();
}
// 获取到上传文件的名称
String filename = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
// 把文件的名称唯一化
filename = uuid+"_"+filename;
// 上传文件
upload.transferTo(new File(file,filename));
return "success";
}
3.配置文件解析器对象: 【如果是传统的文件上传那么会报错,只有SpringMVC的文件上传才能起作用】
<!-- 配置文件解析器对象,要求id名称必须是multipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"/> // 10485760=1024*1024*10=10MB
</bean>
2.SpringMVC跨服务器文件上传:
1.搭建图片服务器:
1.根据文档配置tomcat9的服务器,现在是两个服务器
2.导入资料中day02_SpringMVC5_02image项目,作为图片服务器使用 [两个tomcat模拟两台服务器,一台服务器作为控制器接收请求,一台服务器作为图片储存]
2.实现SpringMVC跨服务器文件上传
7.导入开发需要的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>
编写文件上传JSP页面:
<h3>跨服务器的文件上传</h3>
<form action="user/fileupload3" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"/><br/>
<input type="submit" value="上传文件"/>
</form>
编写控制器:
@RequestMapping(value="/fileupload3")
public String fileupload3(MultipartFile upload) throws Exception {
System.out.println("SpringMVC跨服务器方式的文件上传...");
// 定义图片服务器的请求路径
String path = "http://localhost:9090/day02_springmvc5_02image/uploads/";
// 获取到上传文件的名称
String filename = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
// 把文件的名称唯一化
filename = uuid+"_"+filename;
// 向图片服务器上传文件
// 创建客户端对象
Client client = Client.create();
// 连接图片服务器
WebResource webResource = client.resource(path+filename);
// 上传文件
webResource.put(upload.getBytes());
return "success";
}
1.2.3 异常处理
1.2.3.1 SpringMVC异常处理流程:
1.浏览器-> 前端控制器-> web-> service-> dao
2.如果出现错误,默认处理方案是将错误依次往上抛出:
1.假如dao层出现错误,会抛给service->web->前端控制器->浏览器
3.出现错误如果将错误出现在浏览器上,会降低用户体验,产生不好的影响;
1.此时可在前端控制器添加一个异常处理器组件,它将从内部抛给前端控制器的异常拦截,并将异常进行处理,返回给浏览器一个友好的错误提示页面;
4.Controller调用service,service调用dao,异常都是向上抛出的,最终有DispathcherServlet找异常处理器进行异常的处理。
5.示例见下方:
1.2.3.2 自定义异常类:
package cn.itcast.exception;
public class SysException extends Exception{
private static final long serialVersionUID = 4055945147128016300L;
// 异常提示信息
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public SysException(String message) {
this.message = message;
}}
1.2.3.3 自定义异常处理器
package cn.itcast.exception;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
/**
* 异常处理器
* @author rt
*/
public class SysExceptionResolver implements HandlerExceptionResolver{
/**
* 跳转到具体的错误页面的方法
*/
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse
response, Object handler,
Exception ex) {
ex.printStackTrace();
SysException e = null;
// 获取到异常对象
if(ex instanceof SysException) {
e = (SysException) ex;
}else {
e = new SysException("请联系管理员");
}
ModelAndView mv = new ModelAndView();
// 存入错误的提示信息
mv.addObject("message", e.getMessage());
// 跳转的Jsp页面
mv.setViewName("error");
return mv;
}
}
1.2.3.4 配置异常处理器:
< bean id="sysExceptionResolver" class="cn.itcast.exception.SysExceptionResolvaer"/>
1.2.4 拦截器
1.SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理;
2.拦截器链:(Interceptor Chain)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
3.解释:
1.过滤器:是servlet规范中的一部分,任何java web工程都可以使用。
2.拦截器:是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能用。
3.过滤器:是在url-pattern中配置了/*之后,可以对所有要访问的资源拦截;
4.拦截器:它是只会拦截访问的控制器方法,如果访问的jsp,html,css,image或者js是不会进行拦截的。
5.它也是AOP思想的具体应用。
6.我们要想自定义拦截器,要求必须实现:HandlerInterceptor接口。
4.不同点:过滤器什么都拦截,而拦截器只能拦截特定的;即过滤器的范围大于拦截器;