Spring AOP APIs :: Spring Framework
设计模式-代理(Proxy)模式_存在,及合理的博客-CSDN博客
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
介绍
AOP(Aspect Oriented Programming):面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP(面向对象编程)的延续,可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码,使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码。
AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入
在程序运行期间,不修改源码对已有方法进行增强。
减少重复代码,提高开发效率维护方便
Java OOP存在哪些局限性
静态化语言:类结构一旦定义,不容易被修改
侵入性扩展:通过继承和组合组织新的类结构
使用场景
日志
诊断上下文,如:log4j或logback中的MDC
辅助信息,如:方法执行时间
统计
方法调用次数
执行异常次数
数据抽样
数值累加
安防场景
熔断,如:Netflix Hystrix
限流和降级:如:Alibaba Sentinel
认证和授权,如:Spring Security
监控,如:JMX
性能场景
缓存,如Spring Cache
超时控制
切入点指示器
SpringAOP-切点指示器_存在,及合理的博客-CSDN博客
术语
Joinpoint:是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的必须放在通知方法参数的第一位
Pointcut :切入点是指我们要对哪些 Joinpoint 进行拦截的定义
Introduction:引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。
Target:代理的目标对象
Weaving:是指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
Proxy一个类被 AOP 织入增强后,就产生一个结果代理类。
Aspect 是切入点和通知(引介)的结合Advice: 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。 method:用于指定通知类中的增强方法名称 ponitcut-ref:用于指定切入点的表达式的引用 poinitcut:用于指定切入点表达式
通知
拦截到 Joinpoint 之后所要做的事情就是通知。
method:用于指定通知类中的增强方法名称
ponitcut-ref:用于指定切入点的表达式的引用
poinitcut:用于指定切入点表达式
前置通知
aop:before用于配置前置通知。指定增强的方法在切入点方法之前执行
利用接入点可以获取方法的相关信息
@Before("pt1()")
public void beforePrintLog(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
String kind = joinPoint.getKind();
Signature signature = joinPoint.getSignature();
String name = signature.getName();
}
后置通知
可以获取方法的返回值
@AfterReturning(value = "pt1()",returning = "result")
public void afterReturningPrintLog(Object result){
System.out.println(result);
}
异常通知
@AfterThrowing(value = "pt1()" ,throwing = "e")
public void afterThrowingPrintLog(Exception e){
}
最终通知
@After("pt1()")
public void afterPrintLog(){
System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。");
}
环绕通知
@Around("pt1()")
public Object aroundPringLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try{
Object[] args = pjp.getArgs();
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");
rtValue = pjp.proceed(args);
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");
return rtValue;
}catch (Throwable t){
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
throw new RuntimeException(t);
}finally {
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
}
}
示例
@Configuration
@Aspect
@Slf4j
public class WebLogAspect {
@Qualifier("objectMapper")
@Autowired
private ObjectMapper mapper;
private static final Integer PRINT_LOG_SIZE_LIMIT = 1000;
@Pointcut("execution(* com.du.mons.request..*.*(..))")
public void webLog() {
}
@Before("webLog()")
public void beforeInvoke(JoinPoint joinPoint) throws JsonProcessingException {
logExport(joinPoint,0);
}
@After("webLog()")
public void doAfter() {
log.debug("======================= Request Done =======================");
log.debug("");
}
@Around("webLog()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws JsonProcessingException {
Object result = null;
try {
StopWatch sw = StopWatch.createStarted();
result = proceedingJoinPoint.proceed();
String resultJson;
resultJson = mapper.writeValueAsString(result);
if (resultJson.length() > PRINT_LOG_SIZE_LIMIT) {
log.trace("Response Args: [{}]", resultJson);
} else {
log.debug("Response Args: [{}]", resultJson);
}
sw.stop();
log.debug("Time Elapsed: [{}ms]", sw.getTime(TimeUnit.MILLISECONDS));
} catch (Exception e) {
logExport(proceedingJoinPoint,1);
if (e instanceof ConstraintViolationException){
ConstraintViolationException constraintViolationException = (ConstraintViolationException) e;
String message = constraintViolationException.getMessage();
ResultVO<Object> objectResultVO = new ResultVO<>();
objectResultVO.setCode(ResultEnumsWarren.PARAMETER_WARREN.getCode());
objectResultVO.setMsg(message);
return objectResultVO;
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return result;
}
/**
* 打印 日志 信息
* @param joinPoint 连接点
* @param i 1 error, 0 debug
* @throws JsonProcessingException g
*/
private void logExport(JoinPoint joinPoint,int i) throws JsonProcessingException {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
HttpServletRequest request = servletRequestAttributes.getRequest();
if (i == 1) {
log.error("======================= Request Coming =======================");
log.error("URL: [{}]", request.getRequestURL().toString());
log.error("HTTP Method: [{}]", request.getMethod());
log.error("Class Method: [{}].[{}]",
joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName());
} else {
log.debug("======================= Request Coming =======================");
log.debug("URL: [{}]", request.getRequestURL().toString());
log.debug("HTTP Method: [{}]", request.getMethod());
log.debug("Class Method: [{}].[{}]",
joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName());
}
if (MyBeanUtils.isNotEmpty(joinPoint.getArgs())){
ArrayList<Object> args = new ArrayList<>();
for (Object arg : joinPoint.getArgs()) {
if (arg instanceof MultipartFile){
continue;
}
args.add(arg);
}
if (args.size()>0){
String requestArgs = mapper.writeValueAsString(joinPoint.getArgs());
if (requestArgs.length() > PRINT_LOG_SIZE_LIMIT) {
log.trace("Request Args: [{}]", requestArgs);
} else {
if (i == 1) {
log.error("Request Args: [{}]", requestArgs);
}else {
log.debug("Request Args: [{}]", requestArgs);
}
}
}
}
}
}