统一记录日志 并不仅仅记录异常
能不能利用 控制器通知 来统一处理呢?
控制器通知 只能在控制器 发生异常的时候 统一处理。而统一记录日志 不发生异常也需要记录
能否利用拦截器呢?拦截器是针对控制器的处理。而记日志未必只是针对控制器
也可能是针对业务组件。
也可能是数据访问层。
统一记录日志 可以对所有模块的 services方法记日志
AOP是一种编程思想,是对面向对象(OOP)的补充,可以进一步提高编程的效率。
AOP和OOP是互补关系,并非竞争。
AOP的术语:
AOP的实现![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/120d335084ab6d2326906d35fe96007e.png)
例子:
新建aspect包,AlphaAspect实例
package com.nowcoder.community.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component//将其声明为bean,然后让容器来管理
@Aspect//表明其为 方面组件
public class AlphaAspect {
@Pointcut("execution(* com.nowcoder.community.service.*.*(..))")//括号里描述 哪些bean,哪些方法是我要处理的目标。service.*表示service下的所有业务组件。service.*.*下一个.*代表所有方法的返回值。(..)表示所有参数
public void pointcut() {//定义切点
}
@Before("pointcut()")//在连接点pointcut 前面记日志
public void before() {
System.out.println("before");
}
@After("pointcut()")//在连接点pointcut 后面记日志
public void after() {
System.out.println("after");
}
@AfterReturning("pointcut()")//在返回值以后
public void afterRetuning() {
System.out.println("afterRetuning");
}
@AfterThrowing("pointcut()")//抛异常之后织入代码
public void afterThrowing() {
System.out.println("afterThrowing");
}
@Around("pointcut()")//前后都织入逻辑 ProceedingJoinPoint,程序织入的 部位
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around before");
Object obj = joinPoint.proceed();
System.out.println("around after");
return obj;
}
}
以上代码对community.service所有代码都有效,而service所有代码都不用改
测试:
首页:
控制台:
控制台输出多次这样的标志,因为调用了很多次业务组件,每次调用都会触发
且按如上顺序。
因为程序没有报错,所以不会输出"afterThrowing"
一个正式实例:
ServiceLogAspect
package com.nowcoder.community.aspect;
import org.aspectj.lang.JoinPoint;
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;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
@Aspect
public class ServiceLogAspect {
private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);//因为要记日志,所以要实例化Logger
@Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
public void pointcut() {
}
@Before("pointcut()")//使用前置通知
public void before(JoinPoint joinPoint) {
// 日志格式:用户[1.2.3.4](这是ip地址),在[xxx](时间),访问了[com.nowcoder.community.service.xxx()](某种功能).
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// (ServletRequestAttributes) 是RequestContextHolder.getRequestAttributes();返回的子类型
HttpServletRequest request = attributes.getRequest();//得到request,就有了ip地址。
String ip = request.getRemoteHost();//得到ip
String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());//按格式yyyy-MM-dd HH:mm:ss 拼一个时间
String target = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();//joinPoint.getSignature().getDeclaringTypeName()得到类名。joinPoint.getSignature().getName()得到方法名。
logger.info(String.format("用户[%s],在[%s],访问了[%s].", ip, now, target));//logger级别是info,因为没有报错。用String.format方法来拼
}
}
测试:
重新编译
刷新首页
控制台打印如下:
总之:
统一记录日志 并不仅仅记录异常
可否利用 上节课所讲 控制器通知处理,控制器通知仅仅是在控制器发生异常时可以统一处理
可否利用 拦截器 拦截所有请求,但是 拦截器仅仅是针对控制器处理的,我们记录日志未必是对 控制器的