仿牛客网社区项目(二十)统一记录日志
全局异常控制日志
需求:
- 帖子模块
- 评论模块
- 消息模块
AOP的概念:
- Aspect Oriented Programing,即面向方面(切面)编程。
- AOP是一种编程思想,是对OOP的补充,可以进一步提高编程的效率。

AOP的术语:

AOP的实现
- AspectJ
- AspectJ是语言级的实现,它扩展了Java语言,定义了AOP语法。
- AspectJ在编译期织入代码,它有一个专门的编译器,用来生成遵守Java字节码规范的class文件。
- Spring AOP
- Spring AOP使用纯Java实现,它不需要专门的编译过程,也不需要特殊的类装载器。
- Spring AOP在运行时通过代理的方式织入代码,只支持方法类型的连接点。
- Spring支持对AspectJ的集成。
Spring AOP
- JDK动态代理
- Java提供的动态代理技术,可以在运行时创建接口的代理实例。
- Spring AOP默认采用此种方式,在接口的代理实例中织入代码。
- CGLib动态代理
- 采用底层的字节码技术,在运行时创建子类代理实例。
- 当目标对象不存在接口时,Spring AOP会采用此种方式,在子类实例中织入代码。
1、AOP#
上一节中统一处理Controller的异常,是通过控制器通知,当控制器异常时,统一处理。但是想记录日志,不一定有异常。而拦截器也是针对控制器的。没有对业务组件、数据访问层统一处理。
想对业务层统一记录日志,而统一记录日志是系统功能,不要和业务功能混在一起实现。否则在想对记录日志的位置进行改变时,将会非常麻烦,因为业务bean有很多个,不好一个一个改。
由此引入了AOP的方式,即面向切面编程,切面是一个一个组件。业务Bean是一个一个target。我们要先声明切点的位置,再通知要做什么事。只需要对切面组件编程即可,不需要再进到业务Bean中去改,提升了编程效率。
框架为切面提供了织入的功能,有编译时(运行快)、类装载、运行时织入(满足特殊要求,所有条件都知道,慢一些)。
Aspect切面:
- 注解
@Component @Aspect
- 声明切点的位置
@Pointcut(切点的位置:返回值 包.类.方法.参数) pointcut()
- 通知具体逻辑,5个注解
@Before @After AfterReturning @AfterThrowing @Around
Target: 是业务Bean
AOP实现有两种:
AspectJ和Spring AOP。一般用后者即可。它是运行时织入,通过代理的方式,只在方法处有连接点。
Spring AOP 有两种动态代理方式:
为什么要代理:在织入切面代码时,不在原来的实例中织入,而是在代理对象中织入。调用时也是调用代理对象,而不是调用原始对象。容器调用对象,如果有AOP作用,就调用代理对象。
JDK动态代理(自带的)和CGLib动态代理(第三方)。前者生成接口的实现类,在代理实例中织入,要求必须有业务接口;后者在业务不存在接口时,创建子类实例以实现代理、
2、使用演示#
AlphaAspect
//@Component
//@Aspect
public class AlphaAspect {
// 切点:返回值 包 类 方法 参数
@Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
public void pointcut() {
}
// 通知:连接点前、后、返回值、抛异常、连接点前后。
@Before("pointcut()")// 针对该切点pointcut()
public void before() {
System.out.println("before");
}
@After("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()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {// 参数:连接点
System.out.println("around before");
Object obj = joinPoint.proceed();// 连接点调用目标组件的方法,返回目标组件的返回值
System.out.println("around after");
return obj;
}
}
3、统一记录日志#
ServiceLogAspect
@Component
@Aspect
public class ServiceLogAspect {
private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);
@Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
public void pointcut() {
}
@Before("pointcut()")
public void before(JoinPoint joinPoint) {// 参数:连接点
// 用户[1.2.3.4],在[xxx],访问了[com.nowcoder.community.service.xxx()].
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String ip = request.getRemoteHost();
String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String target = joinPoint.getSignature().getDeclaringTypeName() + "." +joinPoint.getSignature().getName();// 得到该连接点的类名和方法名
logger.info(String.format("用户[%s],在[%s],访问了[%s].", ip, now, target));
}
}
分类:
仿牛客网社区项目
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署