SpringBoot在Web应用中的知识点
本小记学习目标
-
SpringBoot项目War包的生成与Tomcat发布
-
https安全访问配置
-
SpringBoot中数据验证
-
错误页面的配置
-
全局异常处理
-
文件上传功能
-
拦截器
-
AOP拦截器
一、SpringBoot项目War包的生成与Tomcat发布
SpringBoot中默认支持Tomcat容器,当一个SpringBoot打包成为一个jar包并直接运行时会自动启动内部Tomcat容器,除了这种方式也可以把项目打包为war包,采用部署的形式,通过Tomcat进行发布处理。
如何把项目打成war包并在Tomcat中进行发布?
1.在xiaoxieboot项目上点击右键--->New--->Maven Module,建立一个新的Module:xiaoxieboot-web
2.在xiaoxieboot-web的项目中对pom.xml文件进行编辑
指定打包的方式为war
<
packaging
>war
</
packaging
>
新增相关的依赖
<
dependencies
>
<
dependency
>
<
groupId
>
junit
</
groupId
>
<
artifactId
>
junit
</
artifactId
>
<
scope
>test
</
scope
>
</
dependency
>
<
dependency
>
<
groupId
>org.springframework.boot
</
groupId
>
<
artifactId
>spring-boot-starter-test
</
artifactId
>
<
scope
>test
</
scope
>
</
dependency
>
<
dependency
>
<
groupId
>org.springframework.boot
</
groupId
>
<
artifactId
>spring-boot-starter-web
</
artifactId
>
</
dependency
>
<
dependency
>
<
groupId
>org.springframework.boot
</
groupId
>
<
artifactId
>spring-boot-
devtools
</
artifactId
>
</
dependency
>
</
dependencies
>
配置打包的方式
<!-- 打包War包的配置 -->
<
build
>
<
plugins
>
<
plugin
>
<
groupId
>org.apache.maven.plugins
</
groupId
>
<
artifactId
>
maven-war-
plugin
</
artifactId
>
<
configuration
>
<
warName
>xiaoxieboot-web
</
warName
>
</
configuration
>
</
plugin
>
</
plugins
>
</
build
>
3.我们需要更新Maven,项目上点击右键--->Maven--->Update Project...
4.手动新增WEB-INF/web.xml文件,如下图项目结构所示

对于web.xml要符合Tomcat容器的要求,内容如下
<?
xml
version=
"1.0"
encoding=
"UTF-8"
?>
<
web-app
xmlns:xsi=
"
http://www.w3.org/2001/XMLSchema-instance
"
xmlns=
"
http://java.sun.com/xml/ns/javaee
"
xsi:schemaLocation=
"
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd
"
id=
"WebApp_ID"
version=
"2.5"
/>
5.新增SpringBoot程序启动类:com.xiaoxie.SpringBootStartApplication
package com.xiaoxie;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@
SpringBootApplication
public
class SpringBootStartApplication
extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder
builder) {
return
builder.sources(SpringBootStartApplication.
class);
}
public
static
void main(String[]
args) {
SpringApplication.
run(SpringBootStartApplication.
class,
args);
}
}
注意:我们要以war包在Tomcat中运行,那么这个启动类必须要继承SpringBootServletInitializer类,同时要覆盖configure()方法。
6.新增一个Controller包并新增Controller类,com.xiaoxie.controller.TestController
package com.xiaoxie.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public
class TestController {
@GetMapping(
"/test")
public String test() {
return
"TEST!!!";
}
}
7.把项目打包为war包,项目右键--->Run As--->Maven build...,在弹出的对话框中勾Skip Tests,Goals:中录入“clean package”

8.打包的过程在控制台会显示提示信息,如果正常打包完成,会在项目所在目录的target下生成一个war包,我们的war包为"xiaoxieboot-web.war"
9.把这个war包复制到Tomcat的webapps目录下,启动Tomcat,会自动对war包解压,同时在命令框提示信息中看到相应的启动信息
完成启动后访问:http://localhost:8080/xiaoxieboot-web/test
在页面中可以看到信息:
TEST!!!
注意:访问时需要带上打包的名称xiaoxieboot-web(这个名称是在pom.xml中打war的配置中设置的)
二、https安全访问配置
SpringBoot启动时默认使用的是http通信,为了保证安全通常需要使用https来进行访问。
通常来说,https的访问是需要证书的,并且为了保证这个证书的安全,一定要在项止中使用CA进行认证。
1.使用JDK中的keytool命令生成证书 keystore.p12,命令如下:
keytool -genkey -alias mytomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore D:/keystore.p12 -validity 3650 -dname "CN=Web Server,OU=Unit,O=Organization,L=City,S=State,C=US" -storepass P@ssw0rd
2.通过上面的命令则在D盘下生成了一个证书:keystore.p12
把这个证书复制到项目的src/main/resources/resources目录下
同时在这个目录下新增配置文件application.yml
server:
port: 433
#https的端口设置为433
ssl:
key-store: classpath:keystore.p12
#keystore配置文件路径
key-store-type: PKCS12
#keystore的类型为PKCS12
key-alias: mytomcat
#设置别名
key-store-password: P@ssw0rd
#访问别名
3.在pom.xml的打包配置文件中对资源的访问添加p12类型,在<build></build>内部下添加
<
resources
>
<
resource
>
<
directory
>
src/main/resources
</
directory
>
<
includes
>
<
include
>**/*.properties
</
include
>
<
include
>**/*.
yml
</
include
>
<
include
>**/*.
xml
</
include
>
<
include
>**/*.
tld
</
include
>
<
include
>**/*.p12
</
include
>
</
includes
>
<
filtering
>false
</
filtering
>
</
resource
>
</
resources>
4.新增一个配置类,以便于用户访问80端口的时候跳转到安全链接433端口上
package com.xiaoxie.config;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public
class HttpConnectorConfig {
public Connector initConnector() {
Connector
connector =
new Connector(
"org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme(
"http");
connector.setPort(80);
connector.setSecure(
false);
connector.setRedirectPort(433);
return
connector;
}
@Bean
public TomcatServletWebServerFactory servletContainerFactory() {
TomcatServletWebServerFactory
tswf =
new TomcatServletWebServerFactory() {
protected
void postProcessContext(Context
context) {
//定义安全访问策略
SecurityConstraint
securityConstraint =
new SecurityConstraint();
securityConstraint.setUserConstraint(
"CONFIDENTIAL");
//定义用户访问约束要求
SecurityCollection
collection =
new SecurityCollection();
collection.addPattern(
"/*");
//匹配所有访问路径
securityConstraint.addCollection(
collection);
//追加路径映射访问配置
context.addConstraint(
securityConstraint);
}
};
tswf.addAdditionalTomcatConnectors(initConnector());
return
tswf;
}
}
三、SpringBoot中数据验证
SpringBoot中的验证可以使用hibernate-vidator组件包实现验证处理,这个组件包支持的验证注解如下:
注解
|
描述
|
@Null
|
被注解的元素必须为null
|
@NotNull
|
被注解的元素不为null
|
@AssertTrue
|
被注解的元素必须为true
|
@Min(value)
|
被注解的元素必须为一个数值,且必须要大于等于指定的值
|
@Max(value) |
被注解的元素必须为一个数值,且必须要小于等于指定的值
|
@DecimalMin(value)
|
被注解的元素必须为一个数值,且必须要大于等于指定的值
|
@DecimalMax(value)
|
被注解的元素必须为一个数值,且必须要小于等于指定的值
|
Size(max=,min=)
|
被注解的元素大小必须在指定的范围内
|
@Digits(integer,fraction)
|
被注解的元素必须是一个数字,其值 必须在可接受的范围内
|
@Past
|
被注解的元素必须是一个过去的日期
|
@Future
|
被注解的元素必须是一个将来的日期
|
@Pattern(regex=,flag=)
|
被注解的元素必须符合指定的正则表达式
|
@NotBlank(message=)
|
被注解的元素字符串非null,且长度必须大于0
|
@Email
|
被注解的元素必须是电子邮箱地址
|
@Length(min=,max=)
|
被注解的字符串的长度必须在指定范围内
|
@NotEmpty
|
被注解的字符串必须非空
|
@Range(min=,max=,message=)
|
被注解的元素必须在合适的范围内
|
1.在项目中新增错误提示信息的资源文件,ValidationMessages.properties 在src/main/resources目录下
注意:这里资源文件的名称必须是:ValidationMessages.properties,同时要注意使用ISO-8859-1的文件编码(SpringBoot 2.0 默认就是使用这个格式去读取资源文件的)
employee.no.notnull.error=
员工编号不可以为空
employee.no.email.error=
员工编号必须使用正确的邮箱
employee.name.notnull.error=
员工名称不可以为空
employee.post.notnull.error=
员工岗位不可以为空
employee.joinDate.past.error=
员工的入职日期不可以大于当前时间
employee.salary.digits.error=
员工的薪资必须是数值
2.新增一个vo类:com.xiaoxie.vo.Employee
package com.xiaoxie.vo;
import java.util.Date;
import javax.validation.constraints.Digits;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
public
class Employee {
@NotNull(message=
"{employee.no.notnull.error}")
@Email(message=
"{employee.no.email.error}")
private String
no;
//员工编号
@NotNull(message=
"{employee.name.notnull.error}")
private String
name;
//员工姓名
@NotNull(message=
"{employee.post.notnull.error}")
private String
post;
//员工所在岗位
@Past(message=
"{employee.joinDate.past.error}")
private Date
joinDate;
//员工入职日期
@Digits(integer=20,fraction=2,message=
"{employee.salary.digits.error}")
private Double
salary;
//基本工资
public String getNo() {
return
no;
}
public
void setNo(String
no) {
this.
no =
no;
}
public String getName() {
return
name;
}
public
void setName(String
name) {
this.
name =
name;
}
public String getPost() {
return
post;
}
public
void setPost(String
post) {
this.
post =
post;
}
public Date getJoinDate() {
return
joinDate;
}
public
void setJoinDate(Date
joinDate) {
this.
joinDate =
joinDate;
}
public Double getSalary() {
return
salary;
}
public
void setSalary(Double
salary) {
this.
salary =
salary;
}
}
从上面可以看到,在属性上添加了注解验证
3.新增一个Controller类,com.xiaoxie.controller.EmployeeController
package com.xiaoxie.controller;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.Iterator;
import javax.validation.Valid;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.xiaoxie.vo.Employee;
@Controller
public
class EmployeeController {
@GetMapping(
"/input_emp")
public String input() {
return
"input_emp";
}
@PostMapping(
"/save_emp")
@ResponseBody
public Object save(
@Valid Employee
emp,BindingResult
result) {
if(
result.hasErrors()) {
//验证存在错误
Iterator<ObjectError>
iterator =
result.getAllErrors().iterator();
while(
iterator.hasNext()) {
//遍历所有错误
ObjectError
error =
iterator.next();
//在控制台打印错误信息
System.
out.println(
"错误信息--->Code:"+
error.getCode() +
" ,message:" +
error.getDefaultMessage());
}
return
result.getAllErrors();
}
else {
//没有错误的情况
return
emp;
}
}
@InitBinder
public
void initBinder(WebDataBinder
wdb) {
//对日期格式化处理
SimpleDateFormat
sdf =
new SimpleDateFormat(
"yyyy-MM-dd");
//注册一个日期格式化处理程序类
wdb.registerCustomEditor(Date.
class,
new CustomDateEditor(
sdf,
true));
}
}
对于方法input,它是跳转到Thymeleaf的模板页面input_emp.html,这时需要新增一包src/main/view/templates,并在其下新增模板页面input_emp.html
<!
DOCTYPE
html>
<
head
>
<
meta
charset=
"UTF-8"
>
<
title
>新增一个员工
</
title
>
</
head
>
<
body
>
<
h2
>新增员工
</
h2
>
<
form
th:action=
"@{save_emp}"
method=
"post"
>
员工编码:
<
input
type=
"text"
name=
"no"
/><
span
style="
color:
gary;"
>(请使用邮箱)
</
span
>
员工姓名:
<
input
type=
"text"
name=
"name"
/>
员工岗位:
<
input
type=
"text"
name=
"post"
/>
入职日期:
<
input
type=
"text"
name=
"joinDate"
/>
员工薪资:
<
input
type=
"text"
name=
"salary"
/>
<
input
type=
"submit"
value=
"提交"
/>
<
input
type=
"reset"
value=
"重置"
/>
</
form
>
</
body
>
</
html
>
在上面的页面中可以看到表单提交时提交到/save_emp,它对应到控制器类的save方法。
四、错误页面的配置
当我们程序出现错误时,为了给用户展现一个友好的错误提示页面,我们需要配置对应的错误信息提示页。错误提示页面一般来说属于静态页面。
1.在src/main/view/static目录下我们新增两个页面error-404.html、error-500.html
error-404.html
<!
DOCTYPE
html>
<
html
>
<
head
>
<
meta
charset=
"UTF-8"
>
<
title
>错误提示
</
title
>
</
head
>
<
body
>
<
h2
>404,页面消失了~~~
</
h2
>
</
body
>
</
html
>
error-500.html
<!
DOCTYPE
html>
<
html
>
<
head
>
<
meta
charset=
"UTF-8"
>
<
title
>错误提示
</
title
>
</
head
>
<
body
>
<
h2
>500,对不起让我先崩溃会~~
</
h2
>
</
body
>
</
html
>
2.新增错误页面的配置,com.xiaoxioe.Config.ErrorPageConfig
package com.xiaoxie.config;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
@Configuration
public
class ErrorPageConfig
implements ErrorPageRegistrar{
@Override
public
void registerErrorPages(ErrorPageRegistry
registry) {
ErrorPage
error404 =
new ErrorPage(HttpStatus.
NOT_FOUND,
"/error-404.html");
ErrorPage
error500 =
new ErrorPage(HttpStatus.
INTERNAL_SERVER_ERROR,
"/error-500.html");
registry.addErrorPages(
error404,
error500);
}
}
通过上面的配置,当程序发生404,500错误时会跳转到指定的错误页面
五、全局异常处理
全局异常处理指的是针对程序中产生的Exception进行处理,当产生了异常后,可以跳转到指定的页面进行错误提示或者通过Restful的形式来返回错误信息。
1.建立一个全局的异常处理类,它可以处理所有的Exception异常,com.xiaoxie.advice.Exception
package com.xiaoxie.advice;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice
//控制层切面
public
class ExceptionAdvice {
public
static
final String
DEFAULT_ERROR_VIEW =
"error";
//默认错误显示页面error.html
@ExceptionHandler
public ModelAndView defaultErrorHandler(HttpServletRequest
request,Exception
e) {
ModelAndView
mav =
new ModelAndView(
DEFAULT_ERROR_VIEW);
//设置跳转路径
//绑定相关数据
mav.addObject(
"exception",
e);
mav.addObject(
"url",
request.getRequestURL());
return
mav;
}
}
2.上面默认的错误显示页面是error.html,所以在templates下新增error.html
<!
DOCTYPE
html>
<
head
>
<
meta
charset=
"UTF-8"
>
<
title
>异常信息页面
</
title
>
</
head
>
<
body
>
<
p
th:text=
"${'错误访问路径:'+url}"
/>
<
p
th:text=
"${'错误信息:'+exception.message}"
/>
</
body
>
</
html
>
3.新增一个Controller,com.xiaoxie.controller.ExceptionController
package com.xiaoxie.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public
class ExceptionController {
@GetMapping(
"/cal/{num}")
public String cal(
@PathVariable(
"num") Integer
num) {
int
result = 100/
num;
return
"100除以"+
num +
"的结果是:" +
result;
}
}
通过上面我们定义了一个请求路径/cal/{num},这里的num必须是Integer
当请求的路径为/cal/n时,页面显示内容如下:
错误访问路径:https://localhost:433/cal/n
错误信息:Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer'; nested exception is java.lang.NumberFormatException: For input string: "n"
当请求路径为/cal/0时,页面显示内容如下:
错误访问路径:https://localhost:433/cal/0
错误信息:/ by zero
当请请路径为/cal/10时,页面显示内容如下:
100除以10的结果是:10
如果我们需要以Restful的形式回应异常信息,则可以把全局的异常处理切面类修改为如下
@RestControllerAdvice
public
class ExceptionAdvice{
@ExceptionHandler
public Object defaultErrorHandler(HttpServletRequest
request,Exception
e) {
ErrorInfo
error =
new ErrorInfo();
error.setCode(HttpStatus.
INTERNAL_SERVER_ERROR.value());
error.setMessage(
e.getMessage());
error.setUrl(
request.getRequestURL().toString());
return
error;
}
ErrorInfo新定的一个vo类
package com.xiaoxie.vo;
public
class ErrorInfo {
private Integer
code;
private String
message;
private String
url;
public Integer getCode() {
return
code;
}
public
void setCode(Integer
code) {
this.
code =
code;
}
public String getMessage() {
return
message;
}
public
void setMessage(String
message) {
this.
message =
message;
}
public String getUrl() {
return
url;
}
public
void setUrl(String
url) {
this.
url =
url;
}
}
六、文件上传功能
SpringBoot本身是支持文件上传操作的,它采用了FileUpload组件实现文件的上传处理,在控制器中可以使用MultipartFile类进行接收处理。
1.新增一个控制器类 com.xiaoxie.controller.UploadController
package com.xiaoxie.controller;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
@Controller
public
class UploadController {
//跳转到upload.html
@GetMapping(
"/upload_pre")
public String uploadPre() {
return
"upload";
}
@PostMapping(
"/upload")
@ResponseBody
public Object upload(String
name,MultipartFile
photo)
throws Exception {
Map<String,Object>
map =
new HashMap<String,Object>();
if(
photo !=
null &&
photo.getSize() != 0) {
//这里说明有文件上传
map.put(
"name-param",
name);
map.put(
"photo-name",
photo.getName());
map.put(
"content-type",
photo.getContentType());
map.put(
"photo-size",
photo.getSize());
//按字节单位计算
//创建一个上传后保存的文件名称
String
fileName = UUID.
randomUUID() +
"."+
photo.getContentType().substring(
photo.getContentType().lastIndexOf(
"/")+1);
//文件保存路径
String
filePath = ((ServletRequestAttributes)RequestContextHolder.
getRequestAttributes()).getRequest().getServletContext().getRealPath(
"/") +
fileName;
map.put(
"photo-path",
filePath);
File
saveFile =
new File(
filePath);
//文件保存
photo.transferTo(
saveFile);
return
map;
}
else {
return
"nothing";
}
}
}
这里有两个方法一个是uploadPre方法,它的作用是用来做访问的跳转转发到upload.html页面上去;第二个是upload方法,它接收一个post请求用来处理文件上传
注意:MaltipartFile这个类,它封装了上传文件的信息
2.在src/main/view/template下新增一个upload.html页面
<!
DOCTYPE
html>
<
head
>
<
meta
charset=
"UTF-8"
>
<
title
>上传文件
</
title
>
</
head
>
<
body
>
<
form
th:action=
"@{/upload}"
method=
"post"
enctype=
"multipart/form-data"
>
名称:
<
input
type=
"text"
name=
"name"
/><
br
/>
图片:
<
input
type=
"file"
name=
"photo"
/><
br
/>
<
input
type=
"submit"
value=
"上传"
/>
</
form
>
</
body
>
</
html
>
在实际的开发过程中,一般是需要对用户上传文件的大小进行限制的,这样可以做到综合平衡服务器资源及用户需求。
可以在aplication.yml配置文件中新增上传限制
spring:
servlet:
multipart:
enabled:
true
max-file-size: 10MB
#设置单个文件的大小限制
max-request-size: 20MB
#设置总体文件大小
file-size-threshold: 512KB
#当上传文件达到指定配置量的时候,把文件内容写入磁盘
location: /
#临时目录
除了上面的在配置文件中对上件文件大小进行配置,也可以添加Bean的配置类进行配置
在com.xiaoxie.config包下添加配置类UploadConfig
package com.xiaoxie.config;
import javax.servlet.MultipartConfigElement;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public
class UploadConfig {
@Bean
public MultipartConfigElement getMultipartConfig() {
MultipartConfigFactory
config =
new MultipartConfigFactory();
config.setMaxFileSize(
"10MB");
config.setMaxRequestSize(
"100MB");
config.setLocation(
"/");
return
config.createMultipartConfig();
}
}
注意:当配置aplication.yml配置文件和UploadConfig配置类都存在的时候,优先以配置类UploadConfig类中的配置来做校验
在实际开发中我们如果一次需要上传多个文件则需要使用MultipartHttpServletRequest进行文件接收
1.新增一个html的模板页面upload_m.html
<!
DOCTYPE
html>
<
head
>
<
meta
charset=
"UTF-8"
>
<
title
>多个上传文件
</
title
>
</
head
>
<
body
>
<
form
th:action=
"@{/upload_m}"
method=
"post"
enctype=
"multipart/form-data"
>
名称:
<
input
type=
"text"
name=
"name"
/><
br
/>
图片1:
<
input
type=
"file"
name=
"photo"
/><
br
/>
图片2:
<
input
type=
"file"
name=
"photo"
/><
br
/>
图片3:
<
input
type=
"file"
name=
"photo"
/><
br
/>
<
input
type=
"submit"
value=
"上传"
/>
</
form
>
</
body
>
</
html
>
2.在UploadController类中新增两个Controller方法及一个文件的保存方法
//跳转到upload_m.html
@GetMapping(
"/upload_pre_m")
public String uploadPrem() {
return
"upload_m";
}
@PostMapping(
"/upload")
@ResponseBody
public Object upload(String
name,MultipartFile
photo)
throws Exception {
Map<String,Object>
map =
new HashMap<String,Object>();
if(
photo !=
null &&
photo.getSize() != 0) {
//这里说明有文件上传
map.put(
"name-param",
name);
map.put(
"photo-name",
photo.getName());
map.put(
"content-type",
photo.getContentType());
map.put(
"photo-size",
photo.getSize());
//按字节单位计算
//创建一个上传后保存的文件名称
String
fileName = UUID.
randomUUID() +
"."+
photo.getContentType().substring(
photo.getContentType().lastIndexOf(
"/")+1);
//文件保存路径
String
filePath = ((ServletRequestAttributes)RequestContextHolder.
getRequestAttributes()).getRequest().getServletContext().getRealPath(
"/") +
fileName;
map.put(
"photo-path",
filePath);
File
saveFile =
new File(
filePath);
//文件保存
photo.transferTo(
saveFile);
return
map;
}
else {
return
"nothing";
}
}
@PostMapping(
"/upload_m")
@ResponseBody
public Object uploadm(String
name,HttpServletRequest
request) {
List<String>
result =
new ArrayList<String>();
if(
request
instanceof MultipartHttpServletRequest) {
MultipartHttpServletRequest
mrequest = (MultipartHttpServletRequest)
request;
List<MultipartFile>
files =
mrequest.getFiles(
"photo");
Iterator<MultipartFile>
it =
files.iterator();
while(
it.hasNext()) {
//读取每一个上传的文件
MultipartFile
photo =
it.next();
//保存上传信息
try {
result.add(saveFile(
photo));
}
catch (Exception
e) {
e.printStackTrace();
}
}
}
return
result;
}
private String saveFile(MultipartFile
file)
throws Exception {
String
path =
"nothing";
if(
file !=
null &&
file.getSize() > 0) {
String
fileName = UUID.
randomUUID() +
"." +
file.getContentType().substring(
file.getContentType().lastIndexOf(
"/")+1);
path = ((ServletRequestAttributes)RequestContextHolder.
getRequestAttributes()).getRequest().getServletContext().getRealPath(
"/") +
fileName;
File
saveFile =
new File(
path);
file.transferTo(
saveFile);
}
return
path;
}
}
注意:当多个文件上传过程中有一个不成功时都不会完成保存文件的操作
七、拦截器
Web请求处理的过程中,拦截器是服务器端进行数据处理的最后一道关卡,在这里可以对所有用户请求的信息做拦截验证。
1.在程序中新增一个拦截器的类:com.xiaoxie.interceptor.Myinterceptor,这个类需要实现拦截器类HandlerInterceptor
package com.xiaoxie.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
//自定义拦截器实现拦截器接口
public
class MyInterceptor
implements HandlerInterceptor {
private
static
final Logger
log = LoggerFactory.
getLogger(MyInterceptor.
class);
@Override
public
boolean preHandle(HttpServletRequest
request, HttpServletResponse
response, Object
handler)
throws Exception {
//在这里可以添加拦截器处理代码,在执行控制器之前执行,如果返回true则继续,返回false则不再继续请求
return
true;
}
@Override
public
void postHandle(HttpServletRequest
request, HttpServletResponse
response, Object
handler,
ModelAndView
modelAndView)
throws Exception {
// 拦截器处理代码
log.info(
"MyInterceptor中postHandler=> " +
modelAndView);
}
@Override
public
void afterCompletion(HttpServletRequest
request, HttpServletResponse
response, Object
handler, Exception
ex)
throws Exception {
//拦截器处理代码
}
}
2.如果需要拦截器生效需要新增配置类,对MVC做配置,并在其中添加自定义的拦截器及指定匹配的地址,在com.xoiaoxie.config包下新增类MyWebApplicationConfig类,继承WebMvcConfigurationSupport类
package com.xiaoxie.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import com.xiaoxie.interceptor.MyInterceptor;
@Configuration
public
class MyWebApplicationConfig
extends WebMvcConfigurationSupport {
@
Override
protected
void addInterceptors(InterceptorRegistry
registry) {
registry.addInterceptor(
new MyInterceptor()).addPathPatterns(
"/**");
//匹配路
super.addInterceptors(
registry);
}
}
八、AOP拦截器
AOP:面向切面编程。它的主要功能就是对业务层的方法调用进行拦截处理,SpringBoot默认是没有含AOP拦截器的,如果需要使用则要在项目中添加sping-boot-starter-aop依赖
在使用AOP之前需要在pom.xml中新增如下依赖
<!-- AOP依赖 -->
<
dependency
>
<
groupId
>org.springframework.boot
</
groupId
>
<
artifactId
>spring-boot-starter-
aop
</
artifactId
>
</
dependency
>
1.新增一个Service接口:com.xiaoxie.service.IinfoService
package com.xiaoxie.service;
public
interface IinfoService {
public String echo(String
message);
}
2.新增service接口的实现类:com.xiaoxie.service.impl.InfoServiceImpl
package com.xiaoxie.service.impl;
import org.springframework.stereotype.Service;
import com.xiaoxie.service.IinfoService;
@Service
public
class InfoServiceImpl
implements IinfoService {
@Override
public String echo(String
message) {
return
"【ECHO】" +
message;
}
}
3.新增一个切面类对业务代码进行拦截,com.xiaoxie.aspcet.ServiceAspect
package com.xiaoxie.aspect;
import java.util.Arrays;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Aspect
@Component
public
class ServiceAspect {
private
static
final Logger
log = LoggerFactory.
getLogger(ServiceAspect.
class);
@Around(
"execution(* com.xiaoxie.service..*.*(..))")
public Object arroundInvoke(ProceedingJoinPoint
point)
throws Throwable{
log.info(
"【serviceBefore】执行参数:"+Arrays.
toString(
point.getArgs()));
//具体的业务调用
Object
obj =
point.proceed(
point.getArgs());
//返回结果
log.info(
"【serviceAfter】返回结果:" +
obj);
return
obj;
}
}
4.编写测试类测试在业务层调用方法是的拦截是否生效,在src/test/java下新增类com.xiaoxie.test.InfoSeviceTest
package com.xiaoxie.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import com.xiaoxie.SpringBootStartApplication;
import com.xiaoxie.service.IinfoService;
@SpringBootTest(classes=SpringBootStartApplication.
class)
@RunWith(SpringJUnit4ClassRunner.
class)
@WebAppConfiguration
public
class InfoServiceTest {
@Autowired
private IinfoService
infoService;
@
Test
public
void testinfo() {
System.
out.println(
infoService.echo(
"SpringBoot-AOP拦截"));
}
}