1、AOP
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));
}
}