电子商务网站cms,手机app开发 网站建设,重庆网站备案查询系统,南安seo教程前言#xff1a;
整合之前#xff0c;我们要明白aop是什么#xff0c;为什么要用aop#xff0c;aop能帮我们做什么。
答#xff1a;AOP是面向切面编程#xff08;Aspect-Oriented Programming#xff09;的简称#xff0c;它是一种编程思想#xff0c;旨在在面向对象…前言
整合之前我们要明白aop是什么为什么要用aopaop能帮我们做什么。
答AOP是面向切面编程Aspect-Oriented Programming的简称它是一种编程思想旨在在面向对象编程OOP的基础上进行功能模块的解耦和隔离。在传统的业务处理代码中通常需要进行事务处理、日志记录等操作这些操作会分散到各个方法中增加了开发和维护的难度。AOP通过预编译方式和运行期动态代理实现在不修改源代码的情况下将分散在各个方法中的重复代码提取出来然后在程序编译或运行时再将这些提取出来的代码应用到需要执行的地方。
因此AOP能够帮我们做以下事情
降低业务逻辑的耦合性提高程序的可重用型和开发效率。对业务逻辑的各个部分进行隔离便于模块化管理。提取公共功能减少重复代码提高代码的可维护性和可读性。提供一种新的编程视角和工具使开发人员可以专注于业务逻辑的实现而不用过多关注其他功能的实现。
AOP能够提高开发效率和代码质量降低维护成本。 一、AOP介绍
1、名词介绍
1切面Aspect切入点和通知的集合 2连接点Joinpoint目标对象中可以被增强的所有方法 3通知Advice增强的代码逻辑分为前置后置最终异常环绕 4切入点Pointcut目标对象中经过匹配最终增强的方法 5引入Introduction动态的为某个类增加和减少方法 6目标对象Target Object被代理的对象 7AOP代理对象AOP ProxyAOP框架创建的代理对象用于实现切面调用方法 8织入Weaving将通知应用到切入点的过程
2、注解介绍
1EnableAspectJautoProxy 用于springboot启动类代表开启注解aop功能支持 proxyTargetClass 是否强制使用CGlib的动态代理默认false exposeProxy 是否通过aop框架暴露该代理对象aopContext能够访问 2Aspect 用于标注切面类 3Pointcut 用于标识切入点 value 切入点表达式 4 Before 前置通知 5AfterReturning 后置通知 6AfterThrowing 异常通知 7After 最终通知 8Around 环绕通知环绕通知代表了一个完整的流程因此环绕通知和上面的四个通知任选其一使用
3、切入点表达式
1execution - 根据表达式匹配使用最多 execution([修饰符] 返回类型 [包名.类名].方法名(参数列表) [异常]) 支持的通配符有 *匹配所有。..匹配多级包或者多个参数。表示类以及子类
2within - 匹配方法所在的包或者类
3this - 用于向通知方法中传入代理对象的引用
4target - 用于向通知方法中传入目标对象的引用
5args - 用于向通知方法中传入参数并且匹配参数个数
6args - 和args都是匹配参数但是args要求传入切入点的参数必须标注指定注解且不能是SOURCE源码注解比如Lombok的
7within - 匹配加了某个注解的类中的所有方法
8target - 与within类似但是要求标注到类上的注解必须为RUNTIME的
9annotation - 匹配加了某个注解的方法
10bean 通过spring容器中的beName匹配 可以使用通配符*来标识以什么开头以什么结尾
二、整合AOP实现实现日志操作 1、引入依赖
!-- springboot aop --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId
/dependency
dependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactIdversion2.0.32/version
/dependency
2、类
package com.mgx.demo.common.enums;import lombok.AllArgsConstructor;
import lombok.Getter;/*** author mgx*/
AllArgsConstructor
Getter
public enum CharacterEnum {/*** 特殊字符*///空白BLANK()//空格, SPACE( )//换行, NEWLINE(\n)//enter换行, ENTER(\r)//左斜杠, SLASH(/)//双左斜杠, DOUBLE_SLASH(//)//反斜杠, BACKSLASH(\\)//单引号, QUOTES()//双引号, DOUBLE_QUOTES(\)//撇号, APOSTROPHE()//艾特符, AT()//井号, HASHTAG(#)//dollar符, DOLLAR($)//百分号, PERCENT(%)//异或运算符 数字相同返回0否则为1, XOR(^)//and符, AND()//星号, ASTERISK(*)//等于号, EQUAL()//下划线, UNDERSCORE(_)//点, DOT(.)//句号, C_DOT(。)//逗号, COMMA(,)//中文逗号, C_COMMA()//管道符, PIPE(|)//双管道符, DOUBLE_PIPE(||)//问号, Q_MARK(?)//叹号, E_MARK(!)//加号, PLUS()//连字号、短横杠、减号, HYPHEN(-)//小于符, LT()//大于符, GT()//冒号, COLON(:)//分号, SEMICOLON(;)//中文分号, C_SEMICOLON()//左圆括号 round, L_R_BRACKETS(()//右圆括号, R_R_BRACKETS())//左右圆括号, R_BRACKETS(())//左方括号 square, L_S_BRACKETS([)//右方括号, R_S_BRACKETS(])//左右方括号, S_BRACKETS([])//左大括号 curly, L_C_BRACKETS({)//右大括号, R_C_BRACKETS({)//左右大括号, C_BRACKETS({});private final String character;public String value() {return character;}}package com.mgx.demo.utils;import com.alibaba.fastjson.JSON;
import com.mgx.demo.common.enums.CharacterEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Objects;/*** author mgx*/
Slf4j
public class LogUtil {/*** 接口请求日志** param param 接口获取参数*/public static void param(Object... param) {RequestAttributes ra RequestContextHolder.getRequestAttributes();ServletRequestAttributes sra (ServletRequestAttributes) ra;if (Objects.nonNull(sra)) {HttpServletRequest request sra.getRequest();String url request.getRequestURL().toString();log.info(请求\n地址{}\n参数{}, url, Objects.isNull(param) ? CharacterEnum.BLANK.value() : Arrays.toString(param));}}/*** 接口请求日志** param param 封装后的参数若数据结构较复杂请考虑json转化string耗时及性能*/public static void paramObject(Object param) {RequestAttributes ra RequestContextHolder.getRequestAttributes();ServletRequestAttributes sra (ServletRequestAttributes) ra;if (Objects.nonNull(sra)) {HttpServletRequest request sra.getRequest();String url request.getRequestURL().toString();log.info(请求\n地址{}\n参数{}, url, Objects.isNull(param) ? CharacterEnum.BLANK.value() : JSON.toJSONString(param));}}public static void logRequest(HttpServletRequest request) {log.info(请求\n地址{}\n方法{}\nIP{}, request.getRequestURL().toString(), request.getMethod(), request.getRemoteAddr());}}
package com.mgx.demo.config.aop;import com.alibaba.fastjson.JSON;import com.mgx.demo.annotation.LogRequestParam;
import com.mgx.demo.utils.LogUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;/*** 自动打印日志*/
Aspect
Component
public class WebLogAspect {private static final Logger logger LoggerFactory.getLogger(WebLogAspect.class);Pointcut(execution(public * com.mgx.demo.controller.*.*(..)))public void webLog() {}AfterReturning(returning ret, pointcut webLog())public void doAfterReturning(Object ret) {// 处理完请求返回内容logger.info(RESPONSE : {}, JSON.toJSONString(ret));}Before(annotation(logRequestParam) || within(logRequestParam))public void doRequestParamLog(JoinPoint joinPoint, LogRequestParam logRequestParam) {// 获取方法参数Object[] args joinPoint.getArgs();if (args ! null) {if (args.length 1) {LogUtil.paramObject(args[0]);} else {LogUtil.param(args);}}}}package com.mgx.demo.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** author mgx*/
Target({ElementType.TYPE, ElementType.METHOD})
Retention(RetentionPolicy.RUNTIME)
public interface LogRequestParam {
}?xml version1.0 encodingUTF-8?
configurationspringProperty scopecontext namelogPath sourceproject.log.config defaultValue${user.home}/springboot-mgx/logs/property nameLOG_HOME value${logPath}/!-- %m输出的信息, %p日志级别, %t线程名, %d日期, %c类的全名, %i索引 --!-- appender是configuration的子节点,是负责写日志的组件 --appender nameCONSOLE classch.qos.logback.core.ConsoleAppenderencoder!--pattern${CONSOLE_LOG_PATTERN}/pattern --pattern%date{yyyy-MM-dd HH:mm:ss} %highlight(%-5level) (%file:%line\)- %m%n/pattern!-- 控制台也要使用utf-8不要使用gbk --charsetUTF-8/charset/encoder/appender!-- RollingFileAppender:滚动记录文件先将日志记录到指定文件当符合某个条件时将日志记录到其他文件 --!-- 1.先按日期存日志日期变了将前一天的日志文件名重命名为xxx%日期%索引新的日志仍然是sys.log --!-- 2.如果日期没有变化但是当前日志文件的大小超过1kb时对当前日志进行分割 重名名 --appender nameALL classch.qos.logback.core.rolling.RollingFileAppenderFile${LOG_HOME}/sys.log/File!-- rollingPolicy:当发生滚动时决定 RollingFileAppender 的行为涉及文件移动和重命名。 --!-- TimeBasedRollingPolicy 最常用的滚动策略它根据时间来制定滚动策略既负责滚动也负责出发滚动 --rollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicy!-- 活动文件的名字会根据fileNamePattern的值每隔一段时间改变一次 --!-- 文件名pileLog/2020/10/10/sys.2020-10-10_13.log --fileNamePattern${LOG_HOME}/%d{yyyy-MM/dd/HH}/sys.%i.log/fileNamePatterntimeBasedFileNamingAndTriggeringPolicy classch.qos.logback.core.rolling.SizeAndTimeBasedFNATP!-- maxFileSize:这是活动文件的大小默认值是10MB --maxFileSize30MB/maxFileSize/timeBasedFileNamingAndTriggeringPolicy/rollingPolicyencoder!-- pattern节点用来设置日志的输入格式 --pattern%d %p (%file:%line\)- %m%n/pattern!-- 记录日志的编码 --charsetUTF-8/charset !-- 此处设置字符集 --/encoder/appender!--ERROR--appender nameERROR classch.qos.logback.core.rolling.RollingFileAppenderfilter classch.qos.logback.classic.filter.LevelFilterlevelERROR/levelonMatchACCEPT/onMatchonMismatchDENY/onMismatch/filterfile${LOG_HOME}/sys.error.log/fileappendtrue/appendrollingPolicy classch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicyfileNamePattern${LOG_HOME}/%d{yyyy-MM/dd/HH}/sys.error.%i.log/fileNamePatternmaxFileSize30MB/maxFileSize/rollingPolicyencoderPattern%d %p (%file:%line\)- %m%n/PatterncharsetUTF-8/charset/encoder/appendercontextListener classch.qos.logback.classic.jul.LevelChangePropagatorresetJULtrue/resetJUL/contextListener!-- 控制台日志输出级别 --root levelinfoappender-ref refCONSOLE/appender-ref refALL/appender-ref refERROR//root!-- 指定项目中某个包当有日志操作行为时的日志记录级别 --!-- com.dmyc为根包也就是只要是发生在这个根包下面的所有日志操作行为的权限都是DEBUG --!-- 级别依次为【从高到低】FATAL ERROR WARN INFO DEBUG TRACE --logger namecom.mgx.demo.controller levelDEBUG/logger namecom.mgx.demo.mapper levelDEBUG/logger namespringfox levelERROR//configuration
application 需要打印日志的地方在类上加上自定义注解 3、测试