如果不理解面向切面编程,可以重点读被标记的文字。
在传统的面向对象编程中,我们将功能模块封装在类中,然后通过创建对象调用类中的方法来实现功能。然而,有些功能并不属于某个具体的类,它们涉及到多个类的共同关注点,比如日志记录、事务管理、权限控制等。如果将这些横切关注点直接嵌入到各个类的业务逻辑中,会导致代码重复、难以维护和扩展。
AOP通过将这些横切关注点从业务逻辑中抽离出来,形成一个独立的模块,称为切面(Aspect)。切面包含了需要横切的逻辑,比如日志记录、事务管理等。在AOP编程中,我们定义切点(Pointcut)表示在哪些地方应用切面,然后将切面与切点进行关联。当程序执行到切点时,切面中的逻辑就会被自动执行,从而实现了横切关注点的功能。
Spring AOP和AspectJ都是常用的AOP框架,它们提供了丰富的AOP功能,但在实现方式和使用上有所区别。
优势:功能强大,适合需要更细粒度控制和更复杂切面编程的场景;
缺点:需要专门去一门语言;
优势:更轻量级、对代码侵入性小。
补充:什么是代理?
package com.nowcoder.community.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AlphaAspect {
@Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
public void pointcut(){
}
@Before("pointcut()")
public void before(){
System.out.println("before");
}
@After("pointcut()")
public void after(){
System.out.println("after");
}
@AfterReturning("pointcut()")
public void afterReturning(){
System.out.println("afterReturning");
}
@AfterThrowing("pointcut()")
public void AfterThrowing(){
System.out.println("AfterThrowing");
}
@Around("pointcut()")
public Object AfterThrowing(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("around before");
Object object = joinPoint.proceed();
System.out.println("around after");
return object;
}
}
around before
before
2023-09-05 21:59:25,581 DEBUG [http-nio-8080-exec-1] c.n.c.d.U.selectById [BaseJdbcLogger.java:143] ==> Preparing: select id, username, password, salt, email, type, status, activation_code, header_url, create_time from user where id = ?
2023-09-05 21:59:25,581 DEBUG [http-nio-8080-exec-1] c.n.c.d.U.selectById [BaseJdbcLogger.java:143] ==> Parameters: 149(Integer)
2023-09-05 21:59:25,582 DEBUG [http-nio-8080-exec-1] c.n.c.d.U.selectById [BaseJdbcLogger.java:143] <== Total: 1
around after
after
afterReturning
package com.nowcoder.community.aspect;
import org.apache.coyote.Request;
import org.apache.tomcat.util.http.fileupload.servlet.ServletRequestContext;
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);
@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));
}
}
2023-09-05 22:19:20,066 INFO [http-nio-8080-exec-8] c.n.c.a.ServiceLogAspect [ServiceLogAspect.java:38] 用户[0:0:0:0:0:0:0:1]在[2023-09-05 22:19:20]访问了[com.nowcoder.community.service.MessageService.findLetters].
2023-09-05 22:19:20,067 DEBUG [http-nio-8080-exec-8] c.n.c.d.M.selectLetters [BaseJdbcLogger.java:143] ==> Preparing: select id, from_id, to_id, conversation_id, content, status, create_time from message where conversation_id = ? and status != 2 and from_id != 1 order by id desc limit ?, ?
2023-09-05 22:19:20,067 DEBUG [http-nio-8080-exec-8] c.n.c.d.M.selectLetters [BaseJdbcLogger.java:143] ==> Parameters: 146_181(String), 0(Integer), 5(Integer)
2023-09-05 22:19:20,068 DEBUG [http-nio-8080-exec-8] c.n.c.d.M.selectLetters [BaseJdbcLogger.java:143] <== Total: 1
2023-09-05 22:19:20,068 INFO [http-nio-8080-exec-8] c.n.c.a.ServiceLogAspect [ServiceLogAspect.java:38] 用户[0:0:0:0:0:0:0:1]在[2023-09-05 22:19:20]访问了[com.nowcoder.community.service.UserService.findUserById].
2023-09-05 22:19:20,068 DEBUG [http-nio-8080-exec-8] c.n.c.d.U.selectById [BaseJdbcLogger.java:143] ==> Preparing: select id, username, password, salt, email, type, status, activation_code, header_url, create_time from user where id = ?
2023-09-05 22:19:20,069 DEBUG [http-nio-8080-exec-8] c.n.c.d.U.selectById [BaseJdbcLogger.java:143] ==> Parameters: 181(Integer)
2023-09-05 22:19:20,070 DEBUG [http-nio-8080-exec-8] c.n.c.d.U.selectById [BaseJdbcLogger.java:143] <== Total: 1
2023-09-05 22:19:20,070 INFO [http-nio-8080-exec-8] c.n.c.a.ServiceLogAspect [ServiceLogAspect.java:38] 用户[0:0:0:0:0:0:0:1]在[2023-09-05 22:19:20]访问了[com.nowcoder.community.service.UserService.findUserById].
2023-09-05 22:19:20,071 DEBUG [http-nio-8080-exec-8] c.n.c.d.U.selectById [BaseJdbcLogger.java:143] ==> Preparing: select id, username, password, salt, email, type, status, activation_code, header_url, create_time from user where id = ?
2023-09-05 22:19:20,071 DEBUG [http-nio-8080-exec-8] c.n.c.d.U.selectById [BaseJdbcLogger.java:143] ==> Parameters: 146(Integer)
2023-09-05 22:19:20,072 DEBUG [http-nio-8080-exec-8] c.n.c.d.U.selectById [BaseJdbcLogger.java:143] <== Total: 1