统一日志处理----AOP/面向切面编程

AOP

Aspect Oriented Programing:面向切面编程
AOP是对OOP的补充,进一步提高编程的效率
AOP的常见使用场景有:权限检查、记录日志、事务管理
如下图所示结构,每个模块都含有相同的系统需求,而这些需求和模块本身的功能无关。我们可以单独定义一个组件,然后将这些系统需求封装到这个组件中去,这个组件和业务组件没有任何关系,这个组件就横向扩展了业务组件的需求。
拦截器也是一种AOP思想的实现
在这里插入图片描述

AOP的术语

  • target:业务组件,要处理的目标对象
  • aspect:方面组件,将系统需求单独封装到一个组件中,这个组件就是aspect
  • join point:目标对象上允许织入aspect的位置
  • pointcut:声明将代码织入哪些对象的哪些位置
  • advice:通知,解决的是方面组件具体织入的逻辑
    在这里插入图片描述

AOP的实现

AOP有两种实现方式:静态代理和动态代理。

  • 静态代理
    静态代理:代理类在编译阶段生成,在编译阶段将通知织入Java字节码中,也称编译时增强。AspectJ使用的是静态代理。缺点:代理对象需要与目标对象实现一样的接口,并且实现接口的方法,会有冗余代码。同时,一旦接口增加方法,目标对象与代理对象都要维护。

  • 动态代理
    动态代理:代理类在程序运行时创建,AOP框架不会去修改字节码,而是在内存中临时生成一个代理对象,在运行期间对业务方法进行增强,不会生成新类。

  • AspectJ

    • 语言级的实现,扩展了java语言,定义了AOP语法
    • 在编译期织入代码,有一个专门的编译器,用来生成遵守java字节码规范的class文件
  • Spring AOP

    • 使用纯java实现,不需要专门的编译过程,不需要特殊的类装载器
    • 在运行时通过代理的方式织入代码,只支持方法类型的连接点
    • 支持对AspectJ的集成

Spring AOP

  • JDK动态代理
    • java提供的动态代理技术,可以在运行时创建接口的代理实例
    • Spring AOP默认采用此种方式,在接口的代理实例中织入代码
  • CGLib动态代理
    • 采用底层的字节码技术,在运行时创建子类代理实例
    • 当目标对象不存在接口时,Spring AOP必须采用此种方式,在子类实例中织入代码

通知类型

Spring切面可以应用5种类型的通知:

  • 前置通知(Before):在目标方法被调用之前调用通知功能;
  • 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
  • 返回通知(After-returning ):在目标方法成功执行之后调用通知;
  • 异常通知(After-throwing):在目标方法抛出异常后调用通知;
  • 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的逻辑。

代码示例

package com.nowcoder.mycommunity.controller.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class AlphaAspect {

    // the first * means every return type
    // the second * means all classes in the com.nowcoder.mycommunity.service package
    // the third * means all functions in those classes
    // (..) means all parameter type
    // pointcut
    @Pointcut("execution(* com.nowcoder.mycommunity.service.*.*(..))")
    public void pointcut(){

    }

    // do this function before 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("afterReturn");
    }

    @AfterThrowing("pointcut()")
    public void afterThrowing(){
        System.out.println("afterThrowing");
    }

    // not only execute this function before pointcut, but after it
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("around before");
        Object object = joinPoint.proceed();
        System.out.println("around after");
        return object;
    }
}

统一处理日志

package com.nowcoder.mycommunity.controller.aspect;

import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
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 java.text.SimpleDateFormat;
import java.util.Date;

@Component
@Aspect
public class ServiceLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);

	// 将所有service组件加入到pointcut中
    @Pointcut("execution(* com.nowcoder.mycommunity.service.*.*(..))")
    public void pointcut(){

    }

    /**
     * joinpoint means the program that is woven into
     * @param joinPoint
     */
    @Before("pointcut()")
    public void before(JoinPoint joinPoint){
        // user[1.2.3.4] in[xxx] accessed the [com.nowcoder.mycommunity.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("user[%s] in[%s] accessed[%s].", ip, now, target));
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值