一文读懂Spring中的AOP机制

简介: 一文读懂Spring中的AOP机制

一、前言

上一篇我们说了注解的底层原理,请戳:一文读懂注解的底层原理

这一篇我们来说一下 Spring 中的 AOP 机制,为啥说完注解的原理然后又要说 AOP 机制呢?

不妨看一下前面的这一篇,请戳:一文读懂Annotation,这一篇我们实现了如何自定义注解的案例。

1、标记日志打印的自定义注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PrintLog {
}

2、定义一个切面,在切面中对使用了 @PrintLog 自定义注解的方法进行环绕增强通知

@Component
@Aspect
@Slf4j
public class PrintLogAspect {
    @Around(value = "@annotation(com.riemann.core.annotation.PrintLog)")
    public Object handlerPrintLog(ProceedingJoinPoint joinPoint) throws Throwable {
        String clazzName = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        Map<String, Object> nameAndArgs = getFieldsName(this.getClass(), clazzName, methodName, args);
        log.info("Enter class[{}] method[{}] params[{}]", clazzName, methodName, nameAndArgs);
        Object object = null;
        try {
            object = joinPoint.proceed();
        } catch (Throwable throwable) {
            log.error("Process class[{}] method[{}] error", clazzName, methodName, throwable);
        }
        log.info("End class[{}] method[{}]", clazzName, methodName);
        return object;
    }
    private Map<String, Object> getFieldsName(Class clazz, String clazzName, String methodName, Object[] args) throws NotFoundException {
        Map<String, Object > map = new HashMap<>();
        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(clazz);
        pool.insertClassPath(classPath);
        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            // exception
        }
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < cm.getParameterTypes().length; i++) {
            map.put( attr.variableName(i + pos), args[i]);
        }
        return map;
    }
}

3、最后,在 Controller 中的方法上使用 @PrintLog 自定义注解即可;当某个方法上使用了自定义注解,那么这个方法就相当于一个切点,那么就会对这个方法做环绕(方法执行前和方法执行后)增强处理。

@RestController
public class Controller {
  @PrintLog
  @GetMapping(value = "/user/findUserNameById/{id}", produces = "application/json;charset=utf-8")
  public String findUserNameById(@PathVariable("id") int id) {
      // 模拟根据id查询用户名
      String userName = "公众号【老周聊架构】";
      return userName;
  }
}

了解完自定义注解的底层机制以后,我们来想一下,为啥在 Controller 类里的方法上添加 @PrintLog 就可以做到在这个方法前后打印相应的日志呢?这就是我们熟悉的 AOP 底层帮我们做完了这个事情。很多人知道是 AOP 完成的,但对于怎么完成的很多人还是不是很清楚。本文就来分析分析它背后的机制。

我们来看下 PrintLogAspect 这个增强类,它有 @Component、@Aspect、@Around 几个核心注解。

下面我们就可以来想一想,这几个核心注解是如何被 Spring 读取的,Spring 又是如何应用这些注解生成代理类,又是如何让它起到增强的作用。

二、Spring中的AOP机制

这里用一张时序图说一下整体机制

spring 在整个 Bean 初始化完成后,会执行后置处理器方法,调用各个 BeanPostProcessor,在各个 BeanPostProcessor 里,有一个 AnnotationAwareAspectJAutoProxyCreator,spring 会在该类的 postProcessBeforeInitialization 里进行 Advisor 的初始化。

可以这样理解,spring 在创建一个类之前,会看下有没有配置 AOP,如果有的话,会把配置给转换成一个个 advisor,然后缓存起来(这样后面需要生成代理类时候,就可以直接使用了)。

findCandidateAdvisors 的方式有两种,一种是上图第 7 步的 findAdvisorBeans 还有一种是第 8 步的 buildAspectJAdvisors。

1、findAdvisorBeans 方式

public List<Advisor> findAdvisorBeans() {
    String[] advisorNames = this.cachedAdvisorBeanNames;
    if (advisorNames == null) {
        advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);
        this.cachedAdvisorBeanNames = advisorNames;
    }
    if (advisorNames.length == 0) {
        return new ArrayList();
    } else {
        List<Advisor> advisors = new ArrayList();
        String[] var3 = advisorNames;
        int var4 = advisorNames.length;
        for(int var5 = 0; var5 < var4; ++var5) {
            String name = var3[var5];
            if (this.isEligibleBean(name)) {
                if (this.beanFactory.isCurrentlyInCreation(name)) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Skipping currently created advisor '" + name + "'");
                    }
                } else {
                    try {
                        // 核心方法
                        advisors.add(this.beanFactory.getBean(name, Advisor.class));
                    } catch (BeanCreationException var11) {
                        Throwable rootCause = var11.getMostSpecificCause();
                        if (rootCause instanceof BeanCurrentlyInCreationException) {
                            BeanCreationException bce = (BeanCreationException)rootCause;
                            String bceBeanName = bce.getBeanName();
                            if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
                                if (logger.isTraceEnabled()) {
                                    logger.trace("Skipping advisor '" + name + "' with dependency on currently created bean: " + var11.getMessage());
                                }
                                continue;
                            }
                        }
                        throw var11;
                    }
                }
            }
        }
        return advisors;
    }
}

其实去掉那些读缓存的代码,就一句话:


advisors.add(this.beanFactory.getBean(name, Advisor.class));

找到实现了 Advisor 接口的类,并返回。

2、buildAspectJAdvisors 方式

public List<Advisor> buildAspectJAdvisors() {
    List<String> aspectNames = this.aspectBeanNames;
    if (aspectNames == null) {
        synchronized(this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                List<Advisor> advisors = new ArrayList();
                List<String> aspectNames = new ArrayList();
                // 找到所有的类(因为是Object所以基本上就是所有被spring管理的类)
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
                String[] var18 = beanNames;
                int var19 = beanNames.length;
                for(int var7 = 0; var7 < var19; ++var7) {
                    String beanName = var18[var7];
                    // 是否是Aspect(比如含有@Aspect注解)
                    if (this.isEligibleBean(beanName)) {
                        Class<?> beanType = this.beanFactory.getType(beanName);
                        if (beanType != null && this.advisorFactory.isAspect(beanType)) {
                            aspectNames.add(beanName);
                            AspectMetadata amd = new AspectMetadata(beanType, beanName);
                            if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                                MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                                // 生成Advisor
                                List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                                if (this.beanFactory.isSingleton(beanName)) {
                                    this.advisorsCache.put(beanName, classAdvisors);
                                } else {
                                    this.aspectFactoryCache.put(beanName, factory);
                                }
                                advisors.addAll(classAdvisors);
                            } else {
                                if (this.beanFactory.isSingleton(beanName)) {
                                    throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect instantiation model is not singleton");
                                }
                                MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                                this.aspectFactoryCache.put(beanName, factory);
                                advisors.addAll(this.advisorFactory.getAdvisors(factory));
                            }
                        }
                    }
                }
                this.aspectBeanNames = aspectNames;
                return advisors;
            }
        }
    }
    if (aspectNames.isEmpty()) {
        return Collections.emptyList();
    } else {
        List<Advisor> advisors = new ArrayList();
        Iterator var3 = aspectNames.iterator();
        while(var3.hasNext()) {
            String aspectName = (String)var3.next();
            List<Advisor> cachedAdvisors = (List)this.advisorsCache.get(aspectName);
            if (cachedAdvisors != null) {
                advisors.addAll(cachedAdvisors);
            } else {
                MetadataAwareAspectInstanceFactory factory = (MetadataAwareAspectInstanceFactory)this.aspectFactoryCache.get(aspectName);
                advisors.addAll(this.advisorFactory.getAdvisors(factory));
            }
        }
        return advisors;
    }
}

我们的 PrintLogAspect 例子里,并没有实现任何接口,只是使用了一个 @Aspect 注解。因此使用 buildAspectJAdvisors 方式,spring 会通过我们的 AspectJ 注解(比如@Around、@Pointcut、@Before、@After) 动态的生成各个 Advisor。

小结如下:

  • 找到所有被 spring 管理的类(父类是 Object 的类)
  • 如果类含有 @Aspect 注解,调用 advisorFactory.getAdvisors 方法生成对应的 advisor
  • 返回advisors


我们继续来看下最核心的,Advisor 的创建。

3、Advisor 的创建


org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisors
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    this.validate(aspectClass);
    MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
    List<Advisor> advisors = new ArrayList();
    Iterator var6 = this.getAdvisorMethods(aspectClass).iterator();
    // 遍历所有没有 @Pointcut 注解的方法
    while(var6.hasNext()) {
        Method method = (Method)var6.next();
        Advisor advisor = this.getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }
    if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        Advisor instantiationAdvisor = new ReflectiveAspectJAdvisorFactory.SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
        advisors.add(0, instantiationAdvisor);
    }
    Field[] var12 = aspectClass.getDeclaredFields();
    int var13 = var12.length;
    for(int var14 = 0; var14 < var13; ++var14) {
        Field field = var12[var14];
        Advisor advisor = this.getDeclareParentsAdvisor(field);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }
    return advisors;
}

最核心的,就是遍历所有没有 Pointcut 注解的方法,调用 getAdvisor 生成对应的 Advisor。

@Nullable
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) {
    this.validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
    AspectJExpressionPointcut expressionPointcut = this.getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
    return expressionPointcut == null ? null : new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

也就是,生成的 Advisor 的实现类,其实是 InstantiationModelAwarePointcutAdvisorImpl。

4、不同类型的通知

使用过 AOP 的都知道,不同的注解,比如 @Before、@After、@Around 都是不一样的。InstantiationModelAwarePointcutAdvisorImpl 这个类,实际上,是对底层 Advisor 的包装,它记录了所对应 @AspectJ 的类、配置的方法、对应的切入点、以及最重要的通知,这个通知会在 InstantiationModelAwarePointcutAdvisorImpl 的构造函数中被初始化。

private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
    Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
    return advice != null ? advice : EMPTY_ADVICE;
}

org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvice

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
    Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    this.validate(candidateAspectClass);
    AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    } else if (!this.isAspect(candidateAspectClass)) {
        throw new AopConfigException("Advice must be declared inside an aspect type: Offending method '" + candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]");
    } else {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Found AspectJ method: " + candidateAdviceMethod);
        }
        Object springAdvice;
        switch(aspectJAnnotation.getAnnotationType()) {
        case AtPointcut:
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
            }
            return null;
        case AtAround:
            springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtBefore:
            springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtAfter:
            springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtAfterReturning:
            springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterReturning afterReturningAnnotation = (AfterReturning)aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                ((AbstractAspectJAdvice)springAdvice).setReturningName(afterReturningAnnotation.returning());
            }
            break;
        case AtAfterThrowing:
            springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterThrowing afterThrowingAnnotation = (AfterThrowing)aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                ((AbstractAspectJAdvice)springAdvice).setThrowingName(afterThrowingAnnotation.throwing());
            }
            break;
        default:
            throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);
        }
        ((AbstractAspectJAdvice)springAdvice).setAspectName(aspectName);
        ((AbstractAspectJAdvice)springAdvice).setDeclarationOrder(declarationOrder);
        String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
        if (argNames != null) {
            ((AbstractAspectJAdvice)springAdvice).setArgumentNamesFromStringArray(argNames);
        }
        ((AbstractAspectJAdvice)springAdvice).calculateArgumentBindings();
        return (Advice)springAdvice;
    }
}

spring 会根据不同的注解的类型,生成对应的 Advice。

spring 会在真正创建一个类之前,根据我们带有 @Aspect 类的配置生成对应的 Advise 对象,这些对象会被缓存起来。在这之后,就是在 spring 创建完 bean 后,根据这个 bean 生成对应的代理对象,并替换掉(也就是说,实际调用方法时候调用的对象变为这个生成的代理对象) 代理对象的创建,代理对象的创建我们之前分析过了,下面补充一点生成代理的方法。

5、代理方法

虽然之前分析过,但这里老周还是提一下代理对象的创建。

AopProxy 接口类提供了 getProxy 方法来获取代理对象,其中有三个实现如下图。

public interface AopProxy {
    Object getProxy();
    Object getProxy(@Nullable ClassLoader var1);
}

这里以 JDK 动态代理来分析

public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
    }
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    this.findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

看到 Proxy.newProxyInstance 就非常熟悉了,JDK 的动态代理。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;
    TargetSource targetSource = this.advised.targetSource;
    Object target = null;
    Object retVal;
    try {
        // equals 方法
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            Boolean var18 = this.equals(args[0]);
            return var18;
        }
        // hashCode方法
        if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            Integer var17 = this.hashCode();
            return var17;
        }
        // 如果是 DecoratingProxy类
        if (method.getDeclaringClass() == DecoratingProxy.class) {
            Class var16 = AopProxyUtils.ultimateTargetClass(this.advised);
            return var16;
        }
        // 实现了Advised接口
        if (this.advised.opaque || !method.getDeclaringClass().isInterface() || !method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            if (this.advised.exposeProxy) {
                // ThreadLocal里记录下当前被代理的对象
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }
            target = targetSource.getTarget();
            Class<?> targetClass = target != null ? target.getClass() : null;
            // 核心方法,获取当前方法的拦截器
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
            if (chain.isEmpty()) {
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
            } else {
                MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                // 调用这些拦截器及方法
                retVal = invocation.proceed();
            }
            Class<?> returnType = method.getReturnType();
            if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                retVal = proxy;
            } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
                throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);
            }
            Object var12 = retVal;
            return var12;
        }
        retVal = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
    } finally {
        if (target != null && !targetSource.isStatic()) {
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            AopContext.setCurrentProxy(oldProxy);
        }
    }
    return retVal;
}

小结如下:

  • hashCode、equals方法单独处理
  • 根据当前方法等,生成所需的方法拦截器
  • 调用方法及拦截器


6、ReflectiveMethodInvocation.proceed()

public Object proceed() throws Throwable {
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return this.invokeJoinpoint(); // ①
    } else {
        // ②
        Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
            Class<?> targetClass = this.targetClass != null ? this.targetClass : this.method.getDeclaringClass();
            return dm.methodMatcher.matches(this.method, targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed();
        } else {
             // ③
            return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
        }
    }
}

6.1 代码①

这个地方的代码就是目标对象的实际执行的地方,也就是 findUserNameById 的实际执行的调用的地方。

6.2 代码②

这一块是一个递归调用

6.3 代码③

这一块就是我们的 @Before、@Around、@After 等注解注释的方法的执行调用的地方,这里 @Before、@Around、@After 等注解的方法都会被封装到不同的 MethodInterceptor 子类对象中去,也就是说 MethodInterceptor 子类对象里面会记录这些注解对应的方法的元数据信息,当调用 MethodInterceptor#invoke 的时候会根据这些元数据信息通过反射的方式调用实际对应的方法,也就是我们上面创建的 PrintLogAspect 这个类的被 @Around 标注的方法。

小结如下:

  • 会根据我们之前生成的各个 Advisor 对应的切入点,判断下当前的方法是否满足该切入点。如果满足,将其适配为 MethodInterceptor 接口并返回。
  • 核心调用逻辑,就是取出一个个拦截器,先判断下方法是否满足拦截器条件,如果满足就调用。


三、总结

  • spring 在创建一个类之前,会看下有没有配置 AOP(可能是xml、可能是注解),如果有的话,会把配置给转换成一个个 advisor,然后缓存起来(这样后面需要生成代理类时候,就可以直接使用了)。
  • 如果有继续看它的 PointCut 对应的规则,只要在创建 bean 的时候符合这个 PointCut 规则的,就用动态代理(JDK Proxy、CGLib)的方式创建代理对象作为 bean 放到容器中。
  • 当我们从 bean 容器中获取代理对象 bean 并调用它的方法的时候,因为这个bean是通过代理的方式创建的,所以必然会走org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept() 方法,而这个方法也必然会执行org.springframework.aop.framework.ReflectiveMethodInvocation#proceed() 这个方法,而这个方法就会根据上面说的执行过程依次执行不同的 MethodInterceptor 子类对象的 invoke() 方法,这个方法会根据元数据信息通过反射的方式调用代理对象对应的真正的对象的方法,例如我上面创建的 PrintLogAspect 这个类的被 @Around 标注的方法。




欢迎大家关注我的公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。


喜欢的话,点赞、再看、分享三连。

相关文章
|
2月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
403 0
|
5月前
|
人工智能 JSON 安全
Spring Boot实现无感刷新Token机制
本文深入解析在Spring Boot项目中实现JWT无感刷新Token的机制,涵盖双Token策略、Refresh Token安全性及具体示例代码,帮助开发者提升用户体验与系统安全性。
568 5
|
1月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
6月前
|
监控 安全 Java
Spring AOP实现原理
本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。
1024 13
|
3月前
|
人工智能 监控 安全
Spring AOP切面编程颠覆传统!3大核心注解+5种通知类型,让业务代码纯净如初
本文介绍了AOP(面向切面编程)的基本概念、优势及其在Spring Boot中的使用。AOP作为OOP的补充,通过将横切关注点(如日志、安全、事务等)与业务逻辑分离,实现代码解耦,提升模块化程度、可维护性和灵活性。文章详细讲解了Spring AOP的核心概念,包括切面、切点、通知等,并提供了在Spring Boot中实现AOP的具体步骤和代码示例。此外,还列举了AOP在日志记录、性能监控、事务管理和安全控制等场景中的实际应用。通过本文,开发者可以快速掌握AOP编程思想及其实践技巧。
|
3月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。
|
3月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
4月前
|
JSON 前端开发 Java
Spring MVC 核心组件与请求处理机制详解
本文解析了 Spring MVC 的核心组件及请求流程,核心组件包括 DispatcherServlet(中央调度)、HandlerMapping(URL 匹配处理器)、HandlerAdapter(执行处理器)、Handler(业务方法)、ViewResolver(视图解析),其中仅 Handler 需开发者实现。 详细描述了请求执行的 7 步流程:请求到达 DispatcherServlet 后,经映射器、适配器找到并执行处理器,再通过视图解析器渲染视图(前后端分离下视图解析可省略)。 介绍了拦截器的使用(实现 HandlerInterceptor 接口 + 配置类)及与过滤器的区别
380 0
|
9月前
|
XML Java Maven
Spring 手动实现Spring底层机制
Spring 第六节 手动实现Spring底层机制 万字详解!
374 31
|
9月前
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
633 25