SpringMVC
文章目录
- SpringMVC
什么是SpringMVC
Spring MVC属于Spring FrameWork的一部分,是一种基于Java的实现了Web MVC设计模式的轻量级Web框架。
官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc
为什么要学习SpringMVC
Spring MVC的特点:
1.轻量级,简单易学
2.高效,基于请求响应的MVC框架
3.与Spring兼容性好,无缝结合
4.约定优于配置
5.功能强大:RESTful、数据验证、格式化、本地化、主题等
6.简洁灵活
Spring的web框架围绕DispatcherServlet[调度Servlet ]设计。
回顾MVC
MVC 架构模式
高内聚低耦合
MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。
- Model(模型) - 模型代表一个存取数据的对象或 实体类对象。它也可以带有逻辑,在数据变化时更新控制器。
- View(视图) - 视图代表模型包含的数据的可视化。
- Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。
MVC框架要做什么事情
Controller:控制器
- 取得表单数据
- 调用业务逻辑
- 转向指定的页面
Model:模型
- 业务逻辑
- 保存数据的状态(持久化)
View:视图
- 显示视图
IDEA搭建Web项目
步骤
- 新建普通的Maven项目
- 删除Src目录作为父项目
- 新建子项目,添加框架支持
-
-
问题描述
若启动项目,tomcat正常访问,但对于Controller控制器中映射地址的访问无效,可能是打包时jar包未导入进去。
组件扫描器未进行生效。
解决方案
手动导入
1、选中项目,点击项目结构
2、在WEB-INF目录下,新建lib文件夹
3、选中lib目录,选择所要导入的jar包,点击应用,重启tomcat
回顾Servlet
配置环境、编码、测试
实现步骤
-
创建Web项目
-
导入依赖
<dependencies> <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet/jsp-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl --> <dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies>
-
编写Servlet
继承HttpServlet,重写get和post方法
package com.rui.controller; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("userName", "李四"); req.getRequestDispatcher("/WEB-INF/jsp/show.jsp").forward(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
- 编写视图
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> ${userName} </body> </html>
- 启动tomcat测试
/ 和 /* 的区别
总结
两者都可以匹配任何资源,只不过两者的匹配的优先级是不一样的;当请求资源与其它Servlet都无法匹配的时候,/所配置的Servlet才会匹配。
注意点:
在Tomcat中conf/web.xml中有关于/和.jsp页面处理的Servlet,当自己所配的web.xml文件中配置/时,
会使Tomcat中的DefaultServlet无法访问,导致静态资源无法访问,因此在SpingMVC配置文件中要开启处理静态资源的开关。
重点:
/ 能匹配路径型URL,不能匹配后缀型URL(除静态资源类后缀的,例:.png 、.jpg 、.html),
/* 能匹配任何类型URL,在配置视图的时候尽量用/这种方式。
即:使用 / ,DispatcherServlet对于 .jsp 等带有后缀的资源不进行拦截。
而使用 /* DispatcherServlet会对其进行拦截,查找对应的Controller控制器,查找不到则报404错误!
SpringMVC中解决静态资源访问问题的两种方式
-
使用tomcat的默认Servlet进行静态资源的处理
<mvc:default-servlet-handler/>
-
声明静态资源,进行特殊处理
<!-- mapping:映射 location:本地资源路径,注意必须是webapp根目录下的路径。 两个*,它表示映射resources/下所有的URL,包括子路径(即接多个/) --> <mvc:resources mapping="/photo/**" location="/photo/"/>
SpringMVC执行流程
流程图示
流程描述
-
DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。
-
我们假设请求的url为 : http://localhost:8080/SpringMVC/test
如上url拆分成三部分:
http://localhost:8080服务器域名SpringMVC部署在服务器上的web站点
test表示控制器的映射地址
通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的test对应的控制器。
-
-
HandlerMapping为处理器映射。DispatcherServlet调用
HandlerMapping,HandlerMapping根据请求url查找Handler。 -
HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:test。
-
HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。
-
HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。
-
Handler让具体的Controller执行。
-
Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。
-
HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。
-
DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。
-
视图解析器将解析的逻辑视图名传给DispatcherServlet。
-
DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。
-
最终视图呈现给用户。
SpringMVC的第一个程序
XML的方式开发
-
构建web项目,导入依赖
-
配置*调度器(DispatchServlet)
<?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"> <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-MVC.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
-
编写控制器(Controller)
- 实现Controller接口,重写handleRequest()方法
package com.rui.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 StudentServlet implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("username", "学生:张三"); mv.setViewName("/WEB-INF/jsp/show.jsp"); return null; } }
package com.rui.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 UserServlet implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("username", "用户:lisi"); mv.setViewName("/WEB-INF/jsp/show.jsp"); return mv; } }
-
编写Spring-MVC的配置,声明控制器对象
- 不需要额外声明BeanNameUrlHandlerMapping和SimpleControllerHandlerAdapter。
- Debug模式下,调试发现SpringMVC初始化时自动创建。
<?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"> <bean id="/getUser" class="com.rui.controller.UserServlet"/> <bean id="/getStudent" class="com.rui.controller.StudentServlet"/> </beans>
-
编写视图界面,测试
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> ${username} </body> </html>
总结:
以上代码存在很多缺点
1. 一个控制器(Controller)只能有一个方法,返回值固定。
2. 每创建一个控制器,则需要在SpringMVC的配置文件中进行配置。
3. 视图路径,存在冗余。
视图解析器
解决视图路径冗余问题
使用步骤
- 在SpringMVC的配置文件中声明视图解析器
<bean id="view" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
-
控制器(Controller)中,编写的响应地址可以省略前缀和后缀
package com.rui.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 UserServlet implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("username", "用户:lisi"); mv.setViewName("show"); return mv; } }
-
视图解析器原理
内部实现是进行字符串的拼接
-
特定情况下,不想让视图解析器起作用
- forward和redirect都是关键字, 有一个共同的特点不和视图解析器一同工作(不加关键字:默认为转发操作)
package com.rui.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 UserServlet implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("username", "用户:lisi"); mv.setViewName("forward:/WEB-INF/jsp/show.jsp"); return mv; } }
注解式开发
解决控制器类单一方法、特定返回值的问题
@Controller
-
作用:声明的类为控制器类
package com.rui.controller; import org.springframework.stereotype.Controller; @Controller public class UserServlet { }
-
使用:需要声明组件扫描器
<?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 "> <context:component-scan base-package="com.rui"/> </beans>
@RequestMapping
-
使用方式(类上、方法上)
- 声明在类上面,表示模块名称
- 声明在方法上面,表示映射的地址名称
- 则请求地址为:http://localhost:8080/项目名/模块名/请求地址
package com.rui.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller @RequestMapping("/user") public class UserServlet { @RequestMapping(value = "/getName",method = RequestMethod.GET) public ModelAndView showName(){ ModelAndView mv = new ModelAndView(); mv.addObject("username", "username:lisi"); mv.setViewName("showName"); return mv; } @RequestMapping("/getAge") public ModelAndView showAge(){ ModelAndView mv = new ModelAndView(); mv.addObject("age", "age:21"); mv.setViewName("showAge"); return mv; } @RequestMapping("/forward") public ModelAndView forward(){ ModelAndView mv = new ModelAndView(); mv.setViewName("forward:index.jsp"); return mv; } }
-
属性
不加method属性,请求不受限制,可以接受任意请求方式的请求
-
value/path属性
声明映射地址
@RequestMapping("/forward")
-
method属性
声明请求方式类型
@RequestMapping(value = "/forward",method = RequestMethod.GET)
存在问题:
- 声明请求类型,代码繁琐
- 返回值资源浪费问题(有时不需要model,有时不需要view,使用ModelAndView浪费资源)
-
-
解决声明请求类型代码繁琐的问题
@RequestMapping注解衍生出5中明确请求类型的mapping注解
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
@RequestParam
逐个接收请求参数中, 解决请求中参数名形参名不一样的问题
-
使用方式:
@RequestMapping("/forward") public ModelAndView forward(@RequestParam(value = "name",required = false) String name){ ModelAndView mv = new ModelAndView(); mv.setViewName("forward:index.jsp"); return mv; }
-
属性:
- value 请求中的参数名称
- required 是一个boolean,默认是true
- true:表示请求中必须包含此参数。
@ResponseBody
注解 @ResponseBody,使用在控制层(controller)的方法上
作用:
-
将方法的返回值,以特定的格式写入到response的body区域,进而将数据返回给客户端。
-
当方法上面没有写ResponseBody,底层会将方法的返回值封装为ModelAndView对象。
-
如果返回值是字符串,那么直接将字符串写到客户端;如果是一个对象,会将对象转化为json串,然后写到客户端。
@GetMapping(value = "/getUser",produces = "text/html;charset=utf-8") @ResponseBody public String getUser(){ User user = new User(1,"李四"); return user.toString(); }
注意编码格式
-
如果返回对象,按utf-8编码。
-
如果返回String,默认按iso8859-1编码。
-
页面可能出现乱码。
-
因此在注解中我们可以手动修改编码格式,例如@RequestMapping(value="/hello",produces=“text/html;charset=utf-8”),前面是请求的路径,后面是编码格式
@RestController
是@ResponseBody和@Controller的组合
@ControllerAdvice
-
通过@ControllerAdvice注解可以将对于控制器的全局配置放在同一个位置。
-
注解了@ControllerAdvice的类的方法可以使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上。
- @ExceptionHandler:用于全局处理控制器里的异常。
- @InitBinder:用来设置WebDataBinder,用于自动绑定前台请求参数到Model中。
- @ModelAttribute:本来作用是绑定键值对到Model中,此处让全局的@RequestMapping都能获得在此处设置的键值对
-
@ControllerAdvice注解将作用在所有注解了@RequestMapping的控制器的方法上。
@ExceptionHandler
- 基本使用方法
- 方法加上@ExceptionHandler注解,这个方法就会处理类中其他方法(被@RequestMapping注解)抛出的异常
- 注解的参数
- @ExceptionHandler注解中可以添加参数,参数是某个异常类的class,代表这个方法专门处理该类异常
- 就近原则
- 当异常发生时,Spring会选择最接近抛出异常的处理方法
联合使用:全局异常处理,可以跳转指定的错误页面,也可以采用@ResponseBody,返回字符串
package com.rui.controller.advice;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(NullPointerException.class)
public String isNullEmp(){
return "empIsNull";
}
@ExceptionHandler(Exception.class)
public String isNullEmp(Exception ex){
System.out.println(ex.getMessage());
return "error";
}
}
处理器方法的形参
前端数据的接收
形参类型
- HttpServletRequest
- HttpServletResponse
- HttpSession
- 用户提交的数据
框架自动进行赋值,方法中可以直接使用
请求参数的接收方式
-
逐个接收
- 处理器(控制器)方法的形参名和请求中参数名必须一致。
- 同名的请求参数赋值给同名的形参
@RequestMapping("/forward") public ModelAndView forward(String name,String age){ ModelAndView mv = new ModelAndView(); System.out.println(name); mv.setViewName("forward:index.jsp?name="+name); return mv; }
-
对象接收
@RequestMapping("/forward") public ModelAndView forward(User user){ ModelAndView mv = new ModelAndView(); System.out.println(name); mv.setViewName("forward:index.jsp?name="+name); return mv; }
返回值类型
解决返回值资源浪费问题
ModelAndView
-
若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据
@RequestMapping(value = "/getName",method = RequestMethod.GET) public ModelAndView showName(){ ModelAndView mv = new ModelAndView(); mv.addObject("username", "username:lisi"); mv.setViewName("showName"); return mv; }
String
-
处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址
-
也可以通过@ResponseBody,返回字符串
@GetMapping(value = "/getIndex") public String getUserString(){ return "redirect:index.jsp"; }
@GetMapping(value = "/getUserString",produces = "text/html;charset=utf-8") @ResponseBody public String getUserString(){ User user = new User(1,"李四"); return user.toString(); }
void
-
若处理器对请求处理后,无需跳转到其它任何资源。例:AJAX响应
-
处理器方法返回值void,不能表示数据,也没有视图。
-
可以通过使用HttpServletResponse的输出对象,把数据输出到浏览器
@PostMapping("/getUserJson") public void getUserJson(HttpServletResponse response) throws IOException { User user = new User(1,"李四"); ObjectMapper om = new ObjectMapper(); String userJson = om.writeValueAsString(user); PrintWriter out = response.getWriter(); out.print(userJson); out.flush(); out.close(); }
Object
-
返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。
-
返回对象,需要使用@ResponseBody注解,将转换后的JSON数据放入到响应体中
处理对象到json格式,需要消息转化器、json依赖。
-
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.0</version> </dependency>
-
<mvc:annotation-driven/>
执行过程源代码分析
-
没有加入注解驱动标签时的状态 org.springframework.http.converter.ByteArrayHttpMessageConverter org.springframework.http.converter.StringHttpMessageConverter org.springframework.http.converter.xml.SourceHttpMessageConverter org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter 加入注解驱动标签时的状态 org.springframework.http.converter.ByteArrayHttpMessageConverter org.springframework.http.converter.StringHttpMessageConverter org.springframework.http.converter.ResourceHttpMessageConverter org.springframework.http.converter.ResourceRegionHttpMessageConverter org.springframework.http.converter.xml.SourceHttpMessageConverter org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
中文乱码问题
解决方案
创建过滤器,进行请求、响应字符集过滤!
-
<filter> <filter-name>charsetEncoding</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>charsetEncoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
源码分析
-
@Nullable private String encoding; private boolean forceRequestEncoding = false; private boolean forceResponseEncoding = false;
-
String encoding = getEncoding(); if (encoding != null) { if (isForceRequestEncoding() || request.getCharacterEncoding() == null) { request.setCharacterEncoding(encoding); } if (isForceResponseEncoding()) { response.setCharacterEncoding(encoding); } } filterChain.doFilter(request, response);
RestFul风格
它是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件,它主要用于客户端和服务端交互类的软件。基于这个风格设计的软件可以更简介,更有层次,更易于实现缓存等机制
状态转化
HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。
分别对应四种基本操作:
1. GET用来获取资源
2. POST用来新建资源
3. PUT用来更新资源
4. DELETE用来删除资源
如何设计RESTful应用程序的API
路径设计:数据库设计完毕之后,基本上就可以确定有哪些资源要进行操作,相对应的路径也可以设计出来。
动词设计:也就是针对资源的具体操作类型,有HTTP动词表示,常用的HTTP动词如下:POST、DELETE、PUT、GET
SpringMVC对RESTful的支持
RESTful的URL路径变量
URL-PATTERN:设置为/,方便拦截RESTful请求。
@PathVariable:可以解析出来URL中的模板变量({id}/{name})
RESTful的CRUD
@RequestMapping:通过设置method属性的CRUD,可以将同一个URL映射到不同的HandlerMethod方法上。
@GetMapping、@PostMapping、@PutMapping、@DeleteMapping注解同@RequestMapping注解的method属性设置。
RESTful的资源表述
RESTful服务中一个重要的特性就是一种资源可以有多种表现形式,在SpringMvc中可以使用ContentNegotiatingManager这个内容协商管理器来实现这种方式。
使用
@PostMapping("/add/{a}/{b}")
public String add(@PathVariable int a,@PathVariable int b, Model model) throws IOException {
model.addAttribute("result", a+b);
return "test";
}
@GetMapping("/add/{a}/{b}")
public String delete(@PathVariable int a,@PathVariable int b, Model model) throws IOException {
model.addAttribute("result", a-b);
return "test";
}
整合SSM
搭建环境
-
数据库
create DATABASE springmvc; use springmvc; drop table if EXISTS emp; CREATE TABLE emp( eNo int(10) primary KEY COMMENT '员工编号', eName varchar(255) not NULL COMMENT '员工名', sal double(11,2) not NULL COMMENT '薪资' )ENGINE=INNODB DEFAULT charset=utf8 INSERT into emp(eNo,eName,sal) VALUES (1,'zhangsan',5000.0), (2,'lisi',4352.0), (3,'king',10000.0);
-
创建maven项目,构建为Web项目,导入依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.rui</groupId> <artifactId>sprngmvc-ssm</artifactId> <version>1.0-SNAPSHOT</version> <!-- 依赖: junit,数据库驱动,连接池,servlet ,jsp,mybatis, spring,mybatis,mybatis-spring,spring-web,spring-jdbc jackson,aspectj(aop) --> <dependencies> <!-- junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.1</version> <scope>test</scope> </dependency> <!-- 数据库 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--数据库连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.5</version> </dependency> <!-- servlet-jsp --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>jstl-api</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.glassfish.web</groupId> <artifactId>jstl-impl</artifactId> <version>1.2</version> <scope>runtime</scope> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> <!-- spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.5</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.5</version> </dependency> <!--aspectj依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.5.RELEASE</version> </dependency> <!--jackson--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> <!--文件下载--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.8.0</version> </dependency> <!-- 邮件 --> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.5.0-b01</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build> </project>
-
注册*调度器,字符集过滤器
<?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"> <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-MVC.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <filter> <filter-name>charsetEncoding</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>charsetEncoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <session-config> <session-timeout>15</session-timeout> </session-config> </web-app>
编码
-
实体类(对应数据库)
-
package com.rui.entity; public class Emp { private int eNo; private String eName; private double sal; public Emp() { } public Emp(int eNo, String eName, double sal) { this.eNo = eNo; this.eName = eName; this.sal = sal; } public int geteNo() { return eNo; } public void seteNo(int eNo) { this.eNo = eNo; } public String geteName() { return eName; } public void seteName(String eName) { this.eName = eName; } public double getSal() { return sal; } public void setSal(double sal) { this.sal = sal; } @Override public String toString() { return "Emp{" + "eNo=" + eNo + ", eName='" + eName + '\'' + ", sal=" + sal + '}'; } }
-
-
dao层(dao层接口,接口对应的mapper文件,mybatis的主配置文件,spring-mybatis的配置文件,数据库配置文件)
-
package com.rui.dao; import com.rui.entity.Emp; import org.apache.ibatis.annotations.Param; import java.util.List; public interface EmpDao { //查询一个员工 Emp toIdSelectEmp(@Param("eNo") int eNo); //删除一个员工 int toIdDeleteEmp(@Param("eNo") int eNo); //更新一个员工 int toIdUpdateEmp(Emp emp); //添加一个员工 int addEmp(Emp emp); //展示所有员工 List<Emp> selectAllEmp(); }
-
<?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.rui.dao.EmpDao"> <select id="toIdSelectEmp" resultType="emp"> select * from emp where eNo =#{eNo} </select> <delete id="toIdDeleteEmp" > delete from emp where eNo =#{eNo} </delete> <insert id="addEmp"> insert into emp(eNo,eName,sal) values(#{eNo},#{eName},#{sal}) </insert> <update id="toIdUpdateEmp"> update emp set eName=#{eName},sal=#{sal} where eNo =#{eNo} </update> <select id="selectAllEmp" resultType="emp"> select * from emp; </select> </mapper>
-
<?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> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <typeAliases> <package name="com.rui.entity"/> </typeAliases> <mappers> <package name="com.rui.dao"/> </mappers> </configuration>
-
<?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"> <!--配置数据源--> <context:property-placeholder location="classpath:jdbc.properties" /> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}" /> <property name="maxActive" value="${jdbc.maxActive}" /> </bean> <!--定义sqlSessionFactory对象--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:mybatis-config.xml" /> </bean> <!--定义Mapper扫描配置器--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> <property name="basePackage" value="com.rui.dao"/> </bean> </beans>
-
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/springmvc?useSSL=false&useUnicode=true&characterEncoding=UTF-8 jdbc.username=root jdbc.password=guo12345 jdbc.maxActive=20
-
-
service层(接口,实现类,spring配置文件(扫描组件)),service层调dao层,进行组合
-
package com.rui.service; import com.rui.entity.Emp; import java.util.List; public interface EmpService { Emp toIdSelectEmp(int eNo); int toIdDeleteEmp(int eNo); int toIdUpdateEmp(Emp emp); int addEmp(Emp emp); List<Emp> selectAllEmp(); }
-
package com.rui.service.impl; import com.rui.dao.EmpDao; import com.rui.entity.Emp; import com.rui.service.EmpService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class EmpServiceImpl implements EmpService { @Autowired private EmpDao empDao; public void setEmpDao(EmpDao empDao) { this.empDao = empDao; } @Override public Emp toIdSelectEmp(int eNo) { return empDao.toIdSelectEmp(eNo); } @Override public int toIdDeleteEmp(int eNo) { return empDao.toIdDeleteEmp(eNo); } @Override public int toIdUpdateEmp(Emp emp) { return empDao.toIdUpdateEmp(emp); } @Override public int addEmp(Emp emp) { return empDao.addEmp(emp); } @Override public List<Emp> selectAllEmp() { return empDao.selectAllEmp(); } }
-
<?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"> <context:component-scan base-package="com.rui.service"/> </beans>
-
-
controller层(spring-mvc配置文件,控制器编写),controller层调service层,进行组合
-
首页
-
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title> 员工系统 </title> </head> <body> <a href="${pageContext.request.contextPath}/allEmps">进入员工系统</a> </body> </html>
-
-
查询所有员工,跳转显示
-
@GetMapping("/allEmps") public ModelAndView allEmps(){ ModelAndView mv = new ModelAndView(); List<Emp> emps = empService.selectAllEmp(); mv.addObject("emps", emps); mv.setViewName("empList"); return mv; }
-
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <html> <head> <title>员工列表</title> </head> <body> <a href="${pageContext.request.contextPath}/toAddPage">添加</a> <br> <table border="1" > <tr> <td>员工编号</td> <td>员工姓名</td> <td>员工薪资</td> <td colspan="2">操作</td> </tr> <c:forEach var="emp" items="${emps}"> <tr> <td>${emp.eNo}</td> <td>${emp.eName}</td> <td>${emp.sal}</td> <td><a href="${pageContext.request.contextPath}/toUpdatePage?eNo=${emp.eNo}">修改</a></td> <td><a href="${pageContext.request.contextPath}/delete?eNo=${emp.eNo}">删除</a></td> </tr> </c:forEach> </table> </body> </html>
-
-
添加员工
-
@GetMapping("/toAddPage") public String toAddPage(){ return "add"; } @PostMapping("/add") public String addEmp(@RequestParam("eNo")int eNo,@RequestParam("eName")String eName,@RequestParam("sal")double sal){ Emp emp = new Emp(eNo,eName,sal); empService.addEmp(emp); return "redirect:/allEmps"; }
-
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>添加</title> </head> <body> <form action="${pageContext.request.contextPath}/add" method="post"> 编号<input name="eNo" type="text"/><br> 姓名<input name="eName" type="text"/><br> 薪资<input name="sal" type="text"/><br> <input type="submit" value="添加"/> </form> </body> </html>
-
-
更新员工信息
-
@GetMapping("/toUpdatePage") public String toUpdatePage(@RequestParam("eNo")int eNo,Model model){ Emp emp = empService.toIdSelectEmp(eNo); model.addAttribute("emp", emp); return "update"; } @PostMapping("/update") public String updateEmp(@RequestParam("eNo")int eNo,@RequestParam("eName")String eName,@RequestParam("sal")double sal){ Emp emp = new Emp(eNo,eName,sal); empService.toIdUpdateEmp(emp); return "redirect:/allEmps"; }
-
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>更新</title> </head> <body> <form action="${pageContext.request.contextPath}/update" method="post"> 编号<input name="eNo" type="text" value="${emp.eNo}" readonly/><br> 姓名<input name="eName" type="text" value="${emp.eName}"/><br> 薪资<input name="sal" type="text" value="${emp.sal}"/><br> <input type="submit" value="添加"/> </form> </body> </html>
-
-
删除员工
-
@GetMapping("/delete") public String deleteEmp(@RequestParam("eNo")int eNo){ empService.toIdDeleteEmp(eNo); return "redirect:/allEmps"; }
-
-
spring-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"> <context:component-scan base-package="com.rui.controller"/> <!--视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> <!--处理静态资源--> <mvc:default-servlet-handler/> <mvc:annotation-driven/> </beans>
-
-
-
整合spring-*的配置文件,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="classpath:spring-mvc.xml"/> <import resource="classpath:spring-dao.xml"/> <import resource="classpath:spring-service.xml"/> </beans>
-
错误及解决方案
Jstl依赖导入,jsp中引入时爆红问题!
解决方法:导入jsp-api,另外导入jstl-impl
-
<dependency> <groupId>org.glassfish.web</groupId> <artifactId>jstl-impl</artifactId> <version>1.2</version> <scope>runtime</scope> </dependency>
Jackson包导入可能出现的问题
org.apache.catalina.startup.ContextConfig.processAnnotationsJar Unable to process Jar entry [module-info.class] from Jar
解决方法:
在Maven本地仓库中找到相应的jar包,使用压缩软件打开,删除module-info.class
拦截器
作用是拦截指定的用户请求,并进行相应的预处理与后处理。
拦截器的使用
自定义拦截器,需要实现HandlerInterceptor接口(三个默认实现方法)。
boolean preHandle(request,response, Object handler)
- 在处理器方法执行之前执行。若为true,则紧接着会执行处理器方法,
- 且会将afterCompletion()方法放入到一个专门的方法栈中等待执行。
void postHandle(request,response, Object handler,modelAndView)
- 该方法在处理器方法执行之后执行。处理器方法若最终未被执行,则该方法不会执行。
- 由于该方法是在处理器方法执行完后执行,且该方法参数中包含ModelAndView,即该方法可以修改处理器方法的处理结果数据,且可以修改跳转方向。
void afterCompletion(request,response, Object handler, Exception ex)
- 当preHandle()方法返回true时,会将该方法放到专门的方法栈中,等到对请求进行响应的所有工作完成之后才执行该方法。
- 即该方法是在*调度器渲染(数据填充)了响应页面之后执行的,此时对ModelAndView再操作也对响应无济于事
- afterCompletion最后执行的方法,清除资源,例如在Controller方法中加入数据
使用
-
package com.rui.controller.interceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class authorityVerify implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if("张三".equals("张三")){ System.out.println("处理器1方法执行前"); return true; }else{ System.out.println("处理器1方法执行前"); return false; } } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("处理器1方法执行后"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("页面1渲染执行后"); } }
注册拦截器
-
<!-- <mvc:mapping/>用于指定当前所注册的拦截器可以拦截的请求路径,而/**表示拦截所有请求。--> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.rui.controller.interceptor.authorityVerify"/> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.rui.controller.interceptor.authorityVerify1"/> </mvc:interceptor> </mvc:interceptors>
拦截器的执行流程
多个拦截器
执行结果
编程注意事项
避免重复性“造*”
例如:方法的重载
-
数据库关闭资源(以下代码造成了重复性“造*”)
public static void close(ResultSet rs, Statement ps, Connection conn){ if (rs == null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (ps == null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn == null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } public static void close(Statement ps, Connection conn){ if (ps == null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn == null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
-
优化(避免了重复性“造*”)
public static void close(ResultSet rs, Statement ps, Connection conn){ if (rs == null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (ps == null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn == null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } public static void close(Statement ps, Connection conn){ close(null,ps,conn); }
避免多余分工
对于一个类的相关类,若相关类只作用于该类,可以定义为内部类,降低项目的复杂度。
可以避免查看源码时不停的跳转
避免火箭式开发
多层嵌套,可读性极差,可以分开判断(不需要太考虑效率的问题)
-
public boolean verifyUser{ char[] chars = null ; if(user != null){ if(user.name.equals("张三")){ chars = user.name.toCharArray(); for(char a :chars){ if(a == 'a'){ System.out.print(a); } } }else{ return false; }else{ return false; } return true; } public boolean verifyUser{ char[] chars = null ; if(user == null){ return false; } if(user.name.equals("张三")){ chars = user.name.toCharArray(); }else{ return false; } for(char a :chars){ if(a == 'a'){ System.out.print(a); } } return ture; }
注:构建项目->由上向下、排查->由下向上
扩展
文件上传与下载
-
导包
<!--文件下载--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.8.0</version> </dependency>
-
设置表单类型及提交方式
- 表单类型:multipart/form-data
- 提交方式(必须是post请求,不能为get请求)
<form action="${pageContext.request.contextPath }/fileUpload2" method="post" enctype="multipart/form-data"> 文件:<input type="file" name="file"/> <br> <input type="submit" value="上传"/> </form>
-
若使用SpringMVC实现,需要配置
<!-- 用于文件上传、下载的配置 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="utf-8"></property> <property name="maxUploadSize" value="2097152"></property> </bean>
第三方jar包实现
-
上传
-
public class RegisterServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { // 判断请求是否为multipart请求 if( !ServletFileUpload.isMultipartContent(request) ) { throw new RuntimeException("当前请求不支持文件上传"); } try { // 创建一个FileItem工厂 DiskFileItemFactory factory = new DiskFileItemFactory(); // 设置使用临时文件的边界值,大于该值,上传文件会先保存在临时文件中 //否则,上传文件将直接写入到内存。 // 单位:字节。本例设置边界值为1M factory.setSizeThreshold(1024 * 1024 * 1); // 设置临时文件 String tempPath = this.getServletContext().getRealPath("/temp"); File temp = new File(tempPath); factory.setRepository(temp); // 创建文件上传核心组件 ServletFileUpload upload = new ServletFileUpload(factory); // 设置每一个item的头部字符编码,其可以解决文件名的中文乱码问题 upload.setHeaderEncoding("UTF-8"); // 设置单个上传文件的最大边界值为2M upload.setFileSizeMax(1024 * 1024 * 2); // 设置一次上传所有文件的总和最大值为5M(对于上传多个文件时起作用) upload.setSizeMax(1024 * 1024 * 5); // 解析请求,获取到所有的item List<FileItem> items = upload.parseRequest(request); // 遍历items for (FileItem item : items) { if(item.isFormField()) { // 若item为普通表单项 String fieldName = item.getFieldName(); // 获取表单项名称 String fieldValue = item.getString("UTF-8"); // 获取表单项的值 } else { // 若item为文件表单项 String fileName = item.getName(); // 获取上传文件原始名称 fileName = System.currentTimeMillis() + fileName; // 获取输入流,其中有上传文件的内容 InputStream is = item.getInputStream(); // 获取文件保存在服务器的路径 String path =this.getServletContext().getRealPath("/file"); // 若该目录不存在,则创建这个目录 File dirFile = new File(path); if (!dirFile.exists()) { dirFile.mkdirs(); } // 创建目标文件,将来用于保存上传文件 File descFile = new File(path, fileName); // 创建文件输出流 OutputStream os = new FileOutputStream(descFile); // 将输入流中的数据写入到输出流中 int len = -1; byte[] buf = new byte[1024]; while((len = is.read(buf)) != -1) { os.write(buf, 0, len); } // 关闭流 os.close(); is.close(); // 删除临时文件 item.delete(); } } } catch (FileUploadException e) { e.printStackTrace(); } } }
下载
-
public class DownloadServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String fileName = "桌面.jpg"; // 打散:按当前的字符编码进行打散 byte[] bytes = fileName.getBytes("UTF-8"); // 组装:按目标字符编码进行组装 fileName = new String(bytes, "ISO8859-1"); // 修改响应的头部属性content-disposition值为attachment response.setHeader("content-disposition", "attachment;filename=" + fileName); // 获取连接服务端资源文件的输入流 InputStream is = this.getServletContext().getResourceAsStream("/resources/aaa.jpg"); // 获取输出流 ServletOutputStream os = response.getOutputStream(); // 将输入流中的数据写入到输出流中 int len = -1; byte[] buf = new byte[1024]; while((len = is.read(buf)) != -1) { os.write(buf, 0, len); } // 关闭流 os.close(); is.close(); }
servlet3.0新特性
上传
-
@MultipartConfig public class LoadupServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取服务端保存上传文件的目录路径 String path = this.getServletContext().getRealPath("/file"); // 从请求中获取Multipart请求中的上传文件“部分”对象 request.setCharacterEncoding("utf-8"); Part part = request.getPart("file"); // 解析出原始文件名 // 获取指定的头部属性 String header = part.getHeader("Content-Disposition"); int index = header.lastIndexOf("="); String fileName = header.substring(index + 2, header.length() - 1); // 完成文件上传 part.write(path + fileName); } }
springmvc实现
上传(方式一)
-
//@RequestParam("file")将name-file控件得到的文件封装成CommonsNultipartFile对象 //批量上传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"; } //上传路径保存设置 String path = request.getServletContext( ).getRealPath("/upload"); //如果路径不存在,创建一个 File realPath = new File(path); if (!realPath.exists()){ realPath. mkdir(); } 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 来保存上传的文件 */ @RequestMapping("fileUpload2") public String fileUpload2(@RequestParam("file") CommonsMultipartFile file,HttpServletRequest request) throws IOException { //获取文件名 String fileName = file.getOriginalFilename(); //生成随机编号(避免文件名相同) UUID uuid = UUID.randomUUID(); String path=request.getServletContext().getRealPath("/file")+"\\"+uuid+fileName; System.out.println(path); File newFile = new File(path); //通过CommonsMultipartFile的方法直接写文件(注意这个时候) file.transferTo(newFile); return "forward:/index.jsp"; }
下载
-
@RequestMapping(value="/download") public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{ //要下载的图片地址 String path = request.getServletContext().getRealPath("/web"); String fileName ="桌面.PNG" ; //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 null; }
邮件的发送
在网络上实现邮件功能,必须要有专门的邮件服务器。这些邮件服务器类似于现实生活中的邮局,它主要负责接收用户投递过来的邮件,并把邮件投递到邮件接收者的电子邮箱中。
SMTP服务器地址:一般是 smtp.xxx.com,比如qq邮箱是smtp.qq.com。
SMTP协议:通常把处理用户smtp请求(邮件发送请求)的服务器称之为SMTP服务器(邮件发送服务器)。
POP3协议:通常把处理用户pop3请求(邮件接收请求)的服务器称之为POP3服务器(邮件接收服务器)。
- 导包
<!-- 邮件 -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.5.0-b01</version>
</dependency>
普通邮件发送
public class test {
public static void main(String[] args) throws MessagingException, GeneralSecurityException {
//创建一个配置文件并保存
Properties properties = new Properties();
properties.setProperty("mail.host","smtp.qq.com");
properties.setProperty("mail.transport.protocol","smtp");
properties.setProperty("mail.smtp.auth","true");
//QQ存在一个特性设置SSL加密
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
properties.put("mail.smtp.ssl.enable", "true");
properties.put("mail.smtp.ssl.socketFactory", sf);
//创建一个session对象
Session session = Session.getDefaultInstance(properties, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("1423457753@qq.com","授权码");
}
});
//开启debug模式
session.setDebug(true);
//获取连接对象
Transport transport = session.getTransport();
//连接服务器
transport.connect("smtp.qq.com","1423457753@qq.com","16位授权码");
//创建邮件对象
MimeMessage mimeMessage = new MimeMessage(session);
//邮件发送人
mimeMessage.setFrom(new InternetAddress("1423457753@qq.com"));
//邮件接收人
mimeMessage.setRecipient(Message.RecipientType.TO,new InternetAddress("1423457753@qq.com"));
//邮件标题
mimeMessage.setSubject("Hello world");
//邮件内容
mimeMessage.setContent("System.out.print","text/html;charset=UTF-8");
//发送邮件
transport.sendMessage(mimeMessage,mimeMessage.getAllRecipients());
//关闭连接
transport.close();
}
}
复杂的邮件
MIME(多用途互联网邮件扩展类型)
MimeBodyPart类
javax.mail.internet.MimeBodyPart类 表示的是一个MIME消息,它和MimeMessage类一样都是从Part接口继承过来。
MimeMultipart类
javax.mail.internet.MimeMultipart是抽象类 Multipart的实现子类,它用来组合多个MIME消息。一个MimeMultipart对象可以包含多个代表MIME消息的MimeBodyPart对象
public class test {
public static void main(String[] args) throws GeneralSecurityException, MessagingException {
Properties prop = new Properties();
prop.setProperty("mail.host", "smtp.qq.com"); // 设置QQ邮件服务器
prop.setProperty("mail.transport.protocol", "smtp"); // 邮件发送协议
prop.setProperty("mail.smtp.auth", "true"); // 需要验证用户名密码
// QQ邮箱设置SSL加密
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable", "true");
prop.put("mail.smtp.ssl.socketFactory", sf);
//1、创建定义整个应用程序所需的环境信息的 Session 对象
Session session = Session.getDefaultInstance(prop, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
//传入发件人的姓名和授权码
return new PasswordAuthentication("1423457753@qq.com","tvqczbqkyrfabbeh");
}
});
//2、通过session获取transport对象
Transport transport = session.getTransport();
//3、通过transport对象邮箱用户名和授权码连接邮箱服务器
transport.connect("smtp.qq.com","1423457753@qq.com","tvqczbqkyrfabbeh");
//4、创建邮件,传入session对象
MimeMessage mimeMessage = complexEmail(session);
//5、发送邮件
transport.sendMessage(mimeMessage,mimeMessage.getAllRecipients());
//6、关闭连接
transport.close();
}
public static MimeMessage complexEmail(Session session) throws MessagingException {
//消息的固定信息
MimeMessage mimeMessage = new MimeMessage(session);
//发件人
mimeMessage.setFrom(new InternetAddress("1423457753@qq.com"));
//收件人
mimeMessage.setRecipient(Message.RecipientType.TO,new InternetAddress("1423457753@qq.com"));
//邮件标题
mimeMessage.setSubject("带图片和附件的邮件");
//邮件内容
//准备图片数据
MimeBodyPart image = new MimeBodyPart();
DataHandler handler = new DataHandler(new FileDataSource("D:\\电脑备份\\桌面.png"));
image.setDataHandler(handler);
image.setContentID("photo.png"); //设置图片id
//准备文本
MimeBodyPart text = new MimeBodyPart();
text.setContent("一段文字中插入图片<img src='cid:photo.png'>下一段文字", "text/html;charset=utf-8");
//附件
MimeBodyPart appendix = new MimeBodyPart();
appendix.setDataHandler(new DataHandler(new FileDataSource("D:\\电脑备份\\settings.xml")));
appendix.setFileName("settings.xml");
//拼装邮件正文
MimeMultipart mimeMultipart = new MimeMultipart();
mimeMultipart.addBodyPart(image);
mimeMultipart.addBodyPart(text);
mimeMultipart.setSubType("related");//文本和图片内嵌成功
//将拼装好的正文内容设置为主体
MimeBodyPart contentText = new MimeBodyPart();
contentText.setContent(mimeMultipart);
//拼接附件
MimeMultipart allFile = new MimeMultipart();
allFile.addBodyPart(appendix);//附件
allFile.addBodyPart(contentText);//正文
allFile.setSubType("mixed"); //正文和附件都存在邮件中,所有类型设置为mixed
//放到Message消息中
mimeMessage.setContent(allFile);
mimeMessage.saveChanges();//保存修改
return mimeMessage;
}
}