首页 > 其他分享 >深入理解Spring容器:从基础到原理(四十一)

深入理解Spring容器:从基础到原理(四十一)

时间:2024-12-23 21:58:42浏览次数:5  
标签:容器 Spring 增强器 切点 四十一 切面 注解 方法

深入理解Spring容器:从基础到原理(四十一)

一、引言

在我们对Spring容器中AOP实现的持续探索中,已经详细剖析了AnnotationAwareAspectJAutoProxyCreator在获取增强器过程中的buildAspectJAdvisors方法,了解了如何从Spring容器中扫描并提取AspectJ注解类的增强器。此刻,我们将深入研究增强器的具体实现,包括普通增强器的获取逻辑以及不同类型增强器(如前置增强器、后置增强器)的内部实现细节。理解这些内容对于全面掌握Spring AOP如何将切面逻辑准确地织入目标对象的方法调用中至关重要。通过深入解析增强器的实现,我们将能够更好地运用Spring AOP来实现精确、高效的横切关注点增强,提升应用程序的开发效率和质量。

二、普通增强器的获取:深入细节

(一)getAdvisor方法的核心功能

  1. AnnotationAwareAspectJAutoProxyCreator获取增强器的过程中,getAdvisor方法承担着从AspectJ注解方法中获取切点信息并生成相应增强器的关键任务。它是构建增强器的核心逻辑所在,通过解析方法上的注解,获取切点表达式等信息,然后根据这些信息创建合适的增强器实例,为后续将切面逻辑织入目标对象的方法调用做好准备。

  2. 例如,在一个包含多个切面和目标对象的Spring应用中,对于每个被@Aspect注解标记的类中的方法,getAdvisor方法会被调用来分析方法上的注解,如@Before@After等,确定切点信息,进而创建对应的增强器。假设我们有一个LoggingAspect切面类,其中有一个@Before注解的方法,getAdvisor方法会解析该注解,获取切点表达式,创建一个前置增强器,用于在目标对象方法执行前执行日志记录逻辑。

    (二)切点信息的获取步骤

    1. 查找注解并封装
  3. 首先,通过AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod方法查找给定方法(candidateAdviceMethod)上的AspectJ注解。该方法会在一组敏感的注解类(包括Before.classAround.classAfter.classAfterReturning.classAfterThrowing.classPointcut.class)中查找,并返回找到的第一个注解(使用AspectJAnnotation封装)。如果没有找到任何注解,则返回null

  4. 例如,对于一个@Before注解的方法,findAspectJAnnotationOnMethod会在该方法上找到Before注解,并将其封装为AspectJAnnotation对象。这个对象包含了注解的详细信息,如注解类型、注解属性等,为后续获取切点表达式提供了基础。

    2. 创建AspectJExpressionPointcut实例并设置表达式
  5. 如果找到了AspectJ注解,会创建一个AspectJExpressionPointcut实例(ajexp),并将其与AspectJ注解类(candidateAspectClass)和空的字符串数组、类数组关联起来。然后,通过AspectJAnnotation.getPointcutExpression方法获取注解中的切点表达式,并使用ajexp.setExpression方法将其设置到AspectJExpressionPointcut实例中。

  6. 假设我们有一个@Pointcut("execution(* com.example.ServiceImpl.*(..))")注解,getPointcut方法会创建一个AspectJExpressionPointcut实例,并将切点表达式"execution(* com.example.ServiceImpl.*(..))"设置到该实例中。这个AspectJExpressionPointcut实例将用于后续判断目标对象的方法是否匹配该切点,从而决定是否应用切面逻辑。

    (三)根据切点信息生成增强器

  7. 在获取了切点信息后,会创建一个InstantiationModelAwarePointcutAdvisorImpl实例来封装增强器相关信息。这个实例包含了切面工厂(af)、切点表达式(ajexp)、切面实例工厂(aif)、方法(method)、声明顺序(declarationOrderInAspect)和切面名称(aspectName)等重要信息。这些信息将在增强器的执行过程中发挥关键作用,用于确定增强器的行为和应用时机。

  8. 例如,对于一个ValidationAspect切面类中的方法,getAdvisor方法会根据该方法上的注解和相关信息创建一个InstantiationModelAwarePointcutAdvisorImpl实例,其中包含了用于验证参数的逻辑、切点表达式以及切面实例工厂等信息。这个增强器将在目标对象的方法执行前,根据切点表达式判断是否需要进行参数验证,如果需要,则执行验证逻辑。

    (四)增强器的初始化与不同类型增强的处理

  9. InstantiationModelAwarePointcutAdvisorImpl的初始化过程中,还会根据切面实例工厂(aif)的实例化策略(是否延迟初始化)对增强器进行进一步的处理。如果是延迟初始化(通过aif.getAspectMetadata().isLazilyInstantiated()判断),会创建一个特殊的PerTargetInstantiationModelPointcut实例作为切点,并标记增强器为延迟初始化(lazy = true);如果不是延迟初始化,则立即实例化增强器(通过instantiateAdvice方法),并将其标记为非延迟初始化(lazy = false)。

  10. instantiateAdvice方法中,会根据注解类型(通过aspectJAnnotation.getAnnotationType()获取)创建不同类型的增强器。例如,对于@Before注解,会创建AspectJMethodBeforeAdvice类型的增强器;对于@After注解,会创建AspectJAfterAdvice类型的增强器;对于@Around注解,会创建AspectJAroundAdvice类型的增强器等。这使得Spring AOP能够根据不同的注解类型,准确地实现相应的增强逻辑,如前置增强、后置增强、环绕增强等。

    (五)代码示例展示普通增强器获取过程

  11. 假设我们有一个简单的Spring应用,包含一个UserService接口和其实现类UserServiceImpl,以及一个SecurityAspect切面类用于权限验证。我们希望通过AOP在UserServiceImpl的方法执行前进行权限验证。

  12. 首先,定义UserService接口:

    public interface UserService {
    void addUser(String username);
    void updateUser(String username);
    }
    
  13. 然后,实现UserServiceImpl类:

    import org.springframework.stereotype.Service;
    @Service
    public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username) {
    System.out.println("Adding user: " + username);
    }
    @Override
    public void updateUser(String username) {
    System.out.println("Updating user: " + username);
    }
    }
    
  14. 接着,定义SecurityAspect切面类:

    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Component;
    @Aspect
    @Component
    public class SecurityAspect {
    @Before("execution(* com.example.UserServiceImpl.*(..))")
    public void checkPermissions() {
    System.out.println("Checking permissions");
    }
    }
    
  15. 在Spring配置文件中,配置AOP相关信息(这里假设使用XML配置与注解配置混合的方式,仅展示关键部分):

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframeworkframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframeworkframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframeworkframework.org/schema/beans http://www.springframeworkframework.org/schema/beans/Spring-beans.xsd
    http://www.springframeworkframework.org/schema/aop http://www.springframeworkframework.org/schema/aop/Spring-aop.xsd">
    <bean id="userService" class="com.example.UserServiceImpl" />
    <bean id="securityAspect" class="com.example.SecurityAspect" />
    <aop:aspectj-autoproxy />
    </beans>
    
  16. 当Spring容器启动并执行到getAdvisor方法时(在buildAspectJAdvisors方法中会调用getAdvisor方法来获取SecurityAspect切面类中方法的增强器):

    • 首先,在getAdvisor方法中,对于SecurityAspect类中的checkPermissions方法,会通过findAspectJAnnotationOnMethod方法查找@Before注解,并将其封装为AspectJAnnotation对象。

    • 然后,创建AspectJExpressionPointcut实例,获取@Before注解中的切点表达式"execution(* com.example.UserServiceImpl.*(..))",并设置到AspectJExpressionPointcut实例中。

    • 接着,创建InstantiationModelAwarePointcutAdvisorImpl实例,将相关信息(如切面工厂、切点表达式、切面实例工厂、checkPermissions方法、声明顺序和切面名称等)封装进去。由于默认情况下不是延迟初始化,会调用instantiateAdvice方法。

    • instantiateAdvice方法中,根据@Before注解类型,创建AspectJMethodBeforeAdvice类型的增强器。这个增强器包含了checkPermissions方法的逻辑,以及用于判断目标对象方法是否匹配切点的AspectJExpressionPointcut实例等信息。

    • 最后,这个增强器将被添加到增强器列表中,当为UserServiceImpl创建AOP代理时,会将该增强器应用到代理对象中。在调用UserServiceImpladdUserupdateUser方法时,代理对象会首先执行SecurityAspect中的checkPermissions方法进行权限验证,如果验证通过,才会继续执行addUserupdateUser方法的实际业务逻辑。这展示了getAdvisor方法如何从AspectJ注解方法中获取切点信息并生成相应的增强器,实现AOP的前置增强功能。

      三、不同类型增强器的内部实现:深入剖析

      (一)MethodBeforeAdviceInterceptor:前置增强器的实现

  17. MethodBeforeAdviceInterceptor是实现前置增强逻辑的关键类,它实现了MethodInterceptor接口,这使得它能够在目标对象方法调用的拦截链中发挥作用。在其构造函数中,接受一个MethodBeforeAdvice类型的参数(通常是AspectJMethodBeforeAdvice实例),并将其保存为属性。这个MethodBeforeAdvice包含了前置增强的具体逻辑,即切面类中@Before注解标记的方法的逻辑。

  18. MethodBeforeAdviceInterceptorinvoke方法被调用时(在代理对象方法调用时触发),它首先会调用advice.before方法(其中adviceAspectJMethodBeforeAdvice实例)。在before方法中,会通过invokeAdviceMethod方法调用增强方法,而invokeAdviceMethod方法最终会调用invokeAdviceMethodWithGivenArgs方法来执行实际的增强逻辑。在invokeAdviceMethodWithGivenArgs方法中,会获取切面实例(通过aspectInstanceFactory.getAspectInstance()),并使用反射机制调用aspectJAdviceMethod(即@Before注解标记的方法),将切面实例和方法参数(如果有)传递进去。这样,就实现了在目标对象方法执行前执行切面的前置增强逻辑。

  19. 例如,在上述SecurityAspect的例子中,当调用UserServiceImpladdUser方法时,代理对象会调用MethodBeforeAdviceInterceptorinvoke方法。在invoke方法中,先执行SecurityAspect中的checkPermissions方法进行权限验证,只有在权限验证通过后,才会继续调用addUser方法的实际业务逻辑。这确保了在执行关键业务操作前,先进行必要的权限检查,增强了系统的安全性。

    (二)AspectJAfterAdvice:后置增强器的实现

  20. AspectJAfterAdvice用于实现后置增强逻辑,它直接实现了MethodInterceptorAfterAdvice接口。在其构造函数中,接受AspectJ方法(aspectJBeforeAdviceMethod)、切点表达式(pointcut)和切面实例工厂(aif)作为参数,并调用父类AbstractAspectJAdvice的构造函数进行初始化。

  21. AspectJAfterAdviceinvoke方法被调用时,它首先会调用mi.proceed()方法来执行目标对象的方法,这确保了目标对象的业务逻辑先执行。然后,在finally块中,会调用invokeAdviceMethod方法来执行后置增强逻辑。这与前置增强器的执行顺序不同,前置增强器是在目标对象方法执行前执行增强逻辑,而后置增强器是在目标对象方法执行后执行。这种设计确保了切面逻辑在目标对象方法执行的前后都能得到正确的应用,实现了对目标对象方法的全面增强。

  22. 例如,假设我们在UserService中添加一个@After注解的方法到SecurityAspect中,用于记录方法执行后的一些信息。当调用UserServiceImpladdUser方法时,代理对象会先执行addUser方法的业务逻辑,然后在finally块中执行SecurityAspect中的后置增强方法,记录方法执行后的相关信息,如执行时间、执行结果等。这展示了AspectJAfterAdvice如何实现后置增强逻辑,在目标对象方法执行后进行额外的处理。

    (三)增强器的多样性与灵活性

  23. Spring AOP通过提供多种类型的增强器(如前置增强器、后置增强器、环绕增强器等),实现了对目标对象方法增强的多样性和灵活性。开发者可以根据不同的需求,选择合适的注解(如@Before@After@Around等)来定义切面方法,Spring AOP会根据注解类型创建相应的增强器,并将其织入到目标对象的方法调用中。这种机制使得在不修改目标对象源代码的情况下,能够轻松地为目标对象添加各种横切关注点的增强逻辑,如日志记录、事务管理、权限验证、性能监控等。

  24. 例如,在一个复杂的企业级应用中,可以使用@Before注解实现参数验证和权限检查,使用@After注解记录方法执行结果和日志,使用@Around注解实现事务管理等。通过灵活组合这些不同类型的增强器,可以构建出功能强大、易于维护的应用程序,提高代码的复用性和可扩展性。同时,这种基于注解的方式使得AOP的使用更加简洁和直观,降低了开发难度,提高了开发效率。

    四、总结与展望

    通过对普通增强器获取逻辑以及不同类型增强器内部实现的深入剖析,我们全面了解了Spring AOP如何根据AspectJ注解创建和执行增强器,将切面逻辑准确地织入目标对象的方法调用中。从getAdvisor方法的切点信息获取和增强器生成,到MethodBeforeAdviceInterceptorAspectJAfterAdvice等增强器的具体实现,每个环节都展示了Spring AOP在增强器实现方面的精细设计和强大功能。在后续的学习中,我们将继续探索Spring AOP的更多高级特性,如增强器的优先级控制、切面的执行顺序、AOP与其他Spring组件的集成细节以及在实际应用中的性能优化等。这些内容将进一步拓展我们对Spring AOP的理解和应用能力,使我们能够构建更加健壮、高效和灵活的Java应用程序。希望本文能够帮助码龄1 - 5年的程序员更好地理解Spring AOP的这些重要实现过程,为开发高质量的应用提供有力的技术支持。让我们共同期待下一次的深入探索之旅,继续挖掘Spring框架的无尽潜力。

标签:容器,Spring,增强器,切点,四十一,切面,注解,方法
From: https://blog.csdn.net/wj_rdk/article/details/144678669

相关文章

  • 深入理解Spring容器:从基础到原理(四十)
    深入理解Spring容器:从基础到原理(四十)一、引言在我们对Spring容器中AOP实现的深入探索中,已经详细剖析了AnnotationAwareAspectJAutoProxyCreator在创建AOP代理过程中的基本流程,包括其在postProcessAfterInitialization方法中的代理创建逻辑以及获取增强器的初步步骤。此刻,......
  • 深入理解Spring容器:从基础到原理(三十九)
    深入理解Spring容器:从基础到原理(三十九)一、引言在我们对Spring容器功能扩展以及AOP实现的持续探索中,已经深入剖析了动态AOP自定义标签的解析过程,了解了Spring如何通过配置启用AOP并注册关键组件。此刻,我们将聚焦于AOP实现的核心环节——创建AOP代理。AnnotationAwareAspec......
  • 解决Spring Boot jar包启动日志输出中文乱码
    解决SpringBootjar包启动日志输出中文乱码|Id|Title|DateAdded|SourceUrl|PostType|Body|BlogId|Description|DateUpdated|IsMarkdown|EntryName|CreatedTime|IsActive|AutoDesc|AccessPermission||-------------|-------------|------------......
  • 解决 高版本SpringBoot整合Swagger 启动报错Failed to start bean ‘documentationPlu
    解决高版本SpringBoot整合Swagger启动报错Failedtostartbean‘documentationPluginsBootstrapper‘问题|Id|Title|DateAdded|SourceUrl|PostType|Body|BlogId|Description|DateUpdated|IsMarkdown|EntryName|CreatedTime|IsActive|AutoDesc|A......
  • 基于SpringBoot在线音乐系统平台功能实现九
    一、前言介绍:1.1项目摘要随着互联网技术的迅猛发展和普及,人们对音乐的获取和欣赏方式发生了巨大改变。传统的音乐播放方式,如CD、磁带或本地下载的音乐文件,已经不能满足用户日益增长的需求。用户更希望通过网络直接获取各种类型的音乐,并享受随时随地的音乐播放服务。现代......
  • 基于SpringBoot在线音乐系统平台功能实现十
    一、前言介绍:1.1项目摘要随着互联网技术的迅猛发展和普及,人们对音乐的获取和欣赏方式发生了巨大改变。传统的音乐播放方式,如CD、磁带或本地下载的音乐文件,已经不能满足用户日益增长的需求。用户更希望通过网络直接获取各种类型的音乐,并享受随时随地的音乐播放服务。现代......
  • springboot毕设 足球管理系统的设计与实现 程序+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景足球,作为全球最受欢迎的体育运动之一,不仅承载着无数人的热情与梦想,也促进了体育产业的蓬勃发展。随着信息技术的不断进步,足球运动的管理逐渐从传统的......
  • springboot毕设 智慧点餐系统 程序+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信息技术的飞速发展和人们生活节奏的加快,餐饮行业正面临着前所未有的变革。传统的点餐方式不仅效率低下,还难以满足消费者日益增长的个性化需求。......
  • springboot毕设 智慧迎新系统 程序+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信息技术的飞速发展和高校信息化建设的不断深入,智慧校园的概念逐渐深入人心。智慧迎新系统作为智慧校园的重要组成部分,旨在通过信息化手段优化新......
  • springboot毕设乐养养老院管理系统程序+论文+部署
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、研究背景随着社会老龄化程度的不断加深,养老问题成为社会关注的焦点。传统的养老院管理方式面临诸多挑战,难以满足日益增长的养老服务需求。在这样的大背景......