springboot 第三章
springboot 日志处理
介绍
springboot框架 集成日志 logback 日志
Logback是由log4j创始人设计的又一个开源日志组件。目前,logback分为三个模块:logback-core,logback-classic和logback-access。是对log4j日志展示进一步改进!
总结: logback 也是一个开源日志组件 和 log4j作用一致 都是用来生成日志 logback更加轻量
日志级别
> All < Trace < `DEBUG < INFO < WARN < ERROR` < Fatal < OFF
- OFF | 关闭:最高级别,不打印日志。
- FATAL | 致命:指明非常严重的可能会导致应用终止执行错误事件。
- ERROR | 错误:指明错误事件,但应用可能还能继续运行。
- WARN | 警告:指明可能潜在的危险状况。
- INFO | 信息:指明描述信息,从粗粒度上描述了应用运行过程。
- DEBUG | 调试:指明细致的事件信息,对调试应用最有用。
- TRACE | 跟踪:指明程序运行轨迹,比DEBUG级别的粒度更细。
- ALL | 所有:所有日志级别,包括定制级别。
> 日志级别由低到高: `日志级别越高输出的日志信息越多`
项目中日志分类
# 日志分类:
- 一种是rootLogger(根全局日志) : 用来监听项目中所有的运行日志 包括引入依赖jar中的日志
- 一种是logger(指定包级别日志) : 用来监听项目中指定包中的日志信息,自定义某个包下面日志打印的级别
自定义配置日志
logging:
file:
name: springbootday3.log ## 日志名称
path: ./logs ## 指定日志输出的文件目录
level:
com.fashion: debug # 子日志,自定义包下日志输出级别
root: info #指定根日志级别(一般不推荐修改根日志,输出信息太多,推荐使用子日志)
项目中使用
@RequestMapping("hello")
public String hello() {
System.out.println(" =============== hello ok ");
logger.trace("TRACE:{}","信息");
logger.debug("DEBUG:{}","信息");
logger.info("INFO:{}","信息");
logger.warn("WARN:{}","信息");
logger.error("ERROR:{}","信息");
return "hello ok";
}
当我们在配置文件配置相应的级别后,对应的日志就可以输出来了
切面编程 aop
springboot是对原有项目中spring框架和springmvc的进一步封装,因此在springboot中同样支持spring框架中AOP切面编程,不过在springboot中为了快速开发仅仅提供了注解方式的切面编程.
1、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、相关注解
# 切面注解
- @Aspect 用来类上,代表这个类是一个切面
- @Before 用在方法上代表这个方法是一个前置通知方法
- @After 用在方法上代表这个方法是一个后置通知方法 @Around 用在方法上代表这个方法是一个环绕的方法
- @Around 用在方法上代表这个方法是一个环绕的方法
- @Configuration 用来标记是一个spring.xml 文件
3、 相关表达式
- execution 表达式,后面可以写,具体切到某一个方法,颗粒度很细,但是也代表解析时间越长
- * 第一个参数为返回值
- com.fashion.*.* 具体某个包 .*.*代表是包下面所有子包
- (..) 方法参数 ..代表是任意参数
- with 后面写直接切到某个包下面,颗粒度较粗
- @Annation 标记有注解的才被切点捕捉到
4、前置切面
@Configuration
@Aspect
public class MyAspect {
public final Logger logger = LoggerFactory.getLogger(this.getClass());
@Before("execution(* com.fashion.controller.*.*(..))")
public void before(JoinPoint joinpoint) {
logger.info("我是前置通知=========》");
logger.debug("执行目标对象:{}",joinpoint.getTarget().getClass().getName());
logger.debug("方法签名:{}",joinpoint.getSignature());
logger.debug("请求参数:{}",joinpoint.getArgs());
}
}
结果
5、后置切面
@After("within(com.fashion.controller.*)")
public void after(JoinPoint joinpoint) {
logger.info("我是后置通知=========》");
logger.debug("执行目标对象:{}",joinpoint.getTarget().getClass().getName());
logger.debug("方法签名:{}",joinpoint.getSignature());
logger.debug("请求参数:{}",joinpoint.getArgs());
}
6、环绕切面
@Around("within(com.fashion.controller.*)")
public Object around(ProceedingJoinPoint joinpoint) throws Throwable {
logger.info("我是环绕通知=========》");
logger.debug("执行目标对象:{}",joinpoint.getTarget().getClass().getName());
logger.debug("方法签名:{}",joinpoint.getSignature());
logger.debug("请求参数:{}",joinpoint.getArgs());
Object result = joinpoint.proceed();// 执行目标方法
return result;
}
结果
注意: 环绕通知存在返回值,参数为ProceedingJoinPoint,如果执行放行,不会执行目标方法,一旦放行必须将目标方法的返回值返回,否则调用者无法接受返回数据
文件上传下载功能
文件上传
用户将自己计算机的文件通过浏览器上传到服务器指定位置,在上传的过程我们称为文件上传;
1、jsp 页面开发
<%@page pageEncoding="UTF-8" contentType="text/html; UTF-8" isELIgnored="false" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>文件上传</title>
</head>
<body>
<div style="margin:auto;" >
<h1>springboot 文件上传</h1>
<form action="/file/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file"><br><br>
<button type="submit" value="提交">提交</button>
</form>
</div>
</body>
</html>
页面效果
2、controller 编写
@Controller
@RequestMapping("/file/")
public class FileController {
public final Logger logger = LoggerFactory.getLogger(this.getClass());
@Value("${file.dir}")
private String path;
@RequestMapping("upload")
public String upload(MultipartFile file) {
logger.info("文件原始名称:{}",file.getOriginalFilename());
logger.info("文件大小kb:{}",file.getSize() / 1024);
String oriName = file.getOriginalFilename();
String subFix = oriName.substring(file.getOriginalFilename().lastIndexOf("."));
logger.info("文件后缀:{}",subFix);
// 新文件名称
String newFileName = new Date().getTime() + subFix;
File uploadFile = new File(path,newFileName);
try {
file.transferTo(uploadFile);
} catch (IOException e) {
e.printStackTrace();
}
return "redirect:/upload.jsp";
}
}
3、yml 配置
- 配置文件根目录,指定将文件上传到某个文件的根目录
- 文件大小限制,指定最大文件大小
server:
servlet:
context-path: /
jsp:
init-parameters:
development: true # 热部署
## 视图解析器配置
spring:
mvc:
view:
suffix: .jsp
prefix: /
#文件过大,会报异常,默认 10M
nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (38443713) exceeds the configured maximum (10485760)
#修改上传文件大小:
spring:
http:
multipart:
max-request-size: 100M #用来控制文件上传大小的限制
max-file-size: 100M #用来指定服务端最大文件大小
文件下载
将服务器的某个资源下载到本地磁盘用于查看是使用
1、jsp 页面开发
<%@page pageEncoding="UTF-8" contentType="text/html; UTF-8" isELIgnored="false" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>文件下载</title>
</head>
<body>
<div style="margin:auto;" >
<h1>springboot 文件下载</h1>
<a href="/file/download?fileName=1728045592798.png">1728045592798.png</a>
<a href="/file/download?fileName=aa.md">aa.md</a>
<a href="/file/download?fileName=我是文件.txt">我是文件.txt</a>
</div>
</body>
</html>
2、controller编写
/**
* 文件下载
* @param fileName
* @param response
* @return
*/
@RequestMapping("download")
public void download(String fileName, HttpServletResponse response) throws Exception {
logger.info("需要下载的文件名称:{}",fileName);
File downLoadFile = new File(path,fileName);
logger.info("文件绝对路径:{}",downLoadFile.getAbsolutePath());
ServletOutputStream os = response.getOutputStream();
response.setHeader("content-disposition","attachment;fileName="+ URLEncoder.encode(fileName,"UTF-8"));
IOUtils.copy(new FileInputStream(downLoadFile),os);
IOUtils.closeQuietly(os);
}
3、页面效果
拦截器
拦截器相当于 aop ,底层实际上也是使用的 aop 功能, 拦截器(inteceptor) 和 过滤器(Filter) 的区别是,Filter 过滤器可以拦截所有请求,包含.jsp .js .css 等全部资源;
但是 interceptor 拦截器只能拦截 controller方法,也就是 url 方法
1、编写拦截器
public class MyInterceptor implements HandlerInterceptor {
public final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("=========== 步骤1 ==========");
return true; //返回true 放行 返回false阻止
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("=========== 步骤2 ==========");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("=========== 步骤3 ==========");
}
}
2、将自定义的拦截器加入注册器中
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**")// 拦截的路径
.excludePathPatterns("/hello") //放行的路径
.order(1);// 指定加载的顺序,按照正序排序
}
}
3、效果
当我们访问http://localhost:8081/download.jsp的时候,拦截器没有出来,当我们访问下载的url后,日志就出来了;
- 拦截器的order 用来编排有多个拦截器,需要指定顺序的时候使用