【SpringAop实现切片注解】

文章目录

  • AOP的目的
  • AOP代码实现
    • 导包:
    • aop实现
    • 注解实现
    • 异步监听

AOP的目的

Spring AOP(Aspect-Oriented Programming)是 Spring 框架中的一项功能,旨在通过切面(Aspect)将横切关注点(Cross-Cutting Concerns)与业务逻辑解耦,从而使代码更加模块化和易维护。以下是 Spring AOP 的主要目的和应用场景:
【SpringAop实现切片注解】_第1张图片

  1. 解耦横切关注点
    横切关注点是指多个模块中都会出现的功能,但不属于核心业务逻辑的一部分。例如:
    日志记录
    安全认证与授权
    性能监控
    事务管理
    缓存 通过 AOP,可以将这些横切关注点从业务代码中分离出来,增强代码的关注点分离。

Spring AOP(面向切面编程)的目的
Spring AOP(Aspect-Oriented Programming)是 Spring 框架中的一项功能,旨在通过切面(Aspect)将横切关注点(Cross-Cutting Concerns)与业务逻辑解耦,从而使代码更加模块化和易维护。以下是 Spring AOP 的主要目的和应用场景:

  1. 解耦横切关注点
    横切关注点是指多个模块中都会出现的功能,但不属于核心业务逻辑的一部分。例如:

日志记录
安全认证与授权
性能监控
事务管理
缓存 通过 AOP,可以将这些横切关注点从业务代码中分离出来,增强代码的关注点分离。
2. 动态增强功能
AOP 允许在运行时动态地增强目标对象的功能,而无需修改目标对象的源码。这种增强可以是方法的前置处理、后置处理、异常处理等。
例如:
前置增强:方法执行前记录日志。
后置增强:方法执行完成后清理资源。
异常增强:捕获异常并记录详细信息。
环绕增强:在方法执行前后动态添加逻辑。
3. 提高代码可维护性
通过 AOP,可以将重复的代码提取到一个单独的切面中,避免在多个地方编写相同的逻辑,从而减少代码冗余,提高代码可读性和可维护性。
4. 实现透明化操作
AOP 的增强功能对业务逻辑代码是透明的,开发者无需感知这些功能如何实现。例如:
日志记录的切面会自动记录方法的执行情况,开发者只需专注于业务逻辑的实现。
权限检查逻辑可以通过切面动态嵌入,而不用在每个业务方法中显式调用。
5. 提供灵活的配置
AOP 支持通过配置实现切面的动态应用,例如:
通过注解(如 @Aspect 和 @Pointcut)精确指定切入点。
通过 XML 配置或 Java 配置文件动态启用或禁用切面。 这使得 AOP 非常灵活,可以根据需求动态调整功能。
6. 典型应用场景
以下是 Spring AOP 的常见应用场景:
日志管理:记录方法的调用、参数、返回值及执行时间。
安全控制:检查用户是否有权限调用某些服务或接口。
事务管理:在方法调用前开启事务,调用后提交或回滚事务。
性能监控:统计方法执行的时间。
异常处理:统一捕获并记录系统中出现的异常。
Spring AOP 工作原理
Spring AOP 主要通过代理模式实现:
基于 JDK 动态代理:对接口进行代理。
基于 CGLIB 动态代理:对没有实现接口的类进行代理。
在代理类中,通过拦截方法调用,在方法执行前后动态添加切面逻辑。

AOP代码实现

导包:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

aop实现

@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class LogAspect {
    //时间发布器
    private final ApplicationEventPublisher applicationEventPublisher;
    @Pointcut("execution(* com.*.controller..*.*(..))")
    public void eventPoint(){}

    /**
     * 请求时间之前
     */
    @Before("eventPoint()")
    public void doBefore(JoinPoint joinPoint) {
        log.info("日志记录之前");
        RequestHolder.set(System.currentTimeMillis());
    }
    //环绕通知
    @Around("eventPoint()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object proceed = joinPoint.proceed();
        log.info("环绕增强器:{}",proceed);
        return proceed;
    }
	//请求之后
    @AfterReturning(value = "eventPoint()", returning = "keys")
    public void doAfterReturning(JoinPoint joinPoint, Object keys) {
        try{
            log.info("日志记录之后");
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//            if(requestAttributes == null){
//                log.error("日志事件不进行发布,没有请求链路");
//                return;
//            }
            Signature signature = joinPoint.getSignature();
            MethodSignature methodSignature = (MethodSignature) signature;
            Method method = methodSignature.getMethod();
            if (method.isAnnotationPresent(Log.class)) {
                Log log = method.getAnnotation(Log.class);
//                webLog.setDescription(log.summary());
                Long requestStartTime = RequestHolder.get();
                applicationEventPublisher.publishEvent(new LogInfoEvent(this,requestStartTime,log,joinPoint,keys,requestAttributes));
            }
        }catch (Exception exception){
            log.error("日志事件无法发布,出现错误:",exception);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        } finally{
            RequestHolder.remove();
        }
    }
	//请求报错
    @AfterThrowing(value = "eventPoint()",throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e){
        RequestHolder.remove();

    }
}
@Pointcut("execution(* com.*.controller..*.*(..))"
含义:
execution 表达式: 定义切入点,指定方法的执行匹配规则。
* (通配符):
	匹配任意的返回值类型。
	匹配任意的包名或类名的一部分。
	com.*.controller:
	匹配 com 包下任意一级子包的 controller 包。
	比如:com.example.controller、com.test.controller。
.. (双点):
	匹配任意子包及以下的层级。
	匹配方法的任意参数。
*.*(..):
	匹配任意类 (*) 的任意方法 (*)
	方法参数为任意类型和任意数量 ((..))

注解实现

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    /**
     * 操作模块
     *
     * @return
     */
    String modul() default "";

    /**
     * 操作类型
     *
     * @return
     */
    String type() default "";

    /**
     * 操作说明
     *
     * @return
     */
    String desc() default "";
}

异步监听

我们这个切片处理采用了异步监听来处理,减少了操作响应时间。
异步时间发布

/**
 * 日志详情事件类
 * @Author D
 */
@Getter
@Setter
public class LogInfoEvent extends ApplicationEvent {
    /**
     * 操作人实体
     */
    private Object sysUser;
    /**
     * 织入点
     */
    private JoinPoint joinPoint;
    /**
     * 返回值
     */
    private Object keys;
    /**
     * 上下文
     */
    private HttpServletRequest request;

    /**
     * 请求开始时间
     */
    private Long requestStartTime;

    /**
     * 异步上下文
     */
    private AsyncContext asyncContext;

    public LogInfoEvent(Object source, Long requestStartTime, Object sysUser, JoinPoint joinPoint, Object keys, RequestAttributes requestAttributes) {
        super(source);
        this.requestStartTime = requestStartTime;
        this.sysUser = sysUser;
        this.joinPoint = joinPoint;
        this.keys = keys;
        HttpServletRequest httpServletRequest = (HttpServletRequest)requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        assert httpServletRequest != null;
        this.request = httpServletRequest;
        this.asyncContext = httpServletRequest.startAsync();
    }
}

异步事件监听

@Slf4j
@Component
public class LogInfoEventListener {

    @Async(ThreadPoolConfig.ASSET_MANAGE_EXECUTOR_ASYNC)
    @EventListener(classes = LogInfoEvent.class)
    public void sendLogInfo(LogInfoEvent event) {
        JoinPoint joinPoint = event.getJoinPoint();
        Object sysUser = event.getSysUser();
        Long requestStartTime = event.getRequestStartTime();
        String methodName = "";
        //开启该请求被标记为异步处理
        AsyncContext asyncContext = event.getAsyncContext();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = event.getRequest();
        try {
            log.info("正常日志事件开始");
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 请求的方法名
            methodName = className + "." + method.getName();
            Log annotation = method.getAnnotation(Log.class);

            // 如果没有配置注解,并不入库备注信息获取操作
            log.info("正常日志事件入库完成");
        } catch (Exception e) {
            log.error("正常日志事件解析出现错误,访问的方法为:{},详细信息为:{}",methodName,e);
        }finally {
            asyncContext.complete();
        }
    }
}

你可能感兴趣的:(java,android,数据库)