Spring AOP原理

本文深入探讨AOP(面向切面编程)的概念,详解Spring AOP的工作原理及其实现步骤,包括动态代理机制、通知注解的运用、切面类与业务逻辑类的整合,以及@EnableAspectJAutoProxy的开启方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AOP:【动态代理】
        指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;

1、导入aop模块;Spring AOP:(spring-aspects)
2、定义一个业务逻辑类(MathCalculator);在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常,xxx)
3、定义一个日志切面类(LogAspects):切面类里面的方法需要动态感知MathCalculator.div运行到哪里然后执行;
        通知方法:
            前置通知(@Before):logStart:在目标方法(div)运行之前运行
            后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
            返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
            异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
            环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())
4、给切面类的目标方法标注何时何地运行(通知注解);
5、将切面类和业务逻辑类(目标方法所在类)都加入到容器中;
6、必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect)
[7]、给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】
        在Spring中很多的 @EnableXXX;

三步:
    1)、将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)
    2)、在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
 3)、开启基于注解的aop模式;@EnableAspectJAutoProxy
 
AOP原理:【看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?】
        @EnableAspectJAutoProxy;
1、@EnableAspectJAutoProxy是什么?
        @Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar
            利用AspectJAutoProxyRegistrar自定义给容器中注册bean;BeanDefinetion
            internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator

        给容器中注册一个AnnotationAwareAspectJAutoProxyCreator;

2、 AnnotationAwareAspectJAutoProxyCreator:
        AnnotationAwareAspectJAutoProxyCreator
            ->AspectJAwareAdvisorAutoProxyCreator
                ->AbstractAdvisorAutoProxyCreator
                    ->AbstractAutoProxyCreator
                            implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
                        关注后置处理器(在bean初始化完成前后做事情)、自动装配BeanFactory

AbstractAutoProxyCreator.setBeanFactory()
AbstractAutoProxyCreator.有后置处理器的逻辑;

AbstractAdvisorAutoProxyCreator.setBeanFactory()-》initBeanFactory()

AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()


流程:
        1)、传入配置类,创建ioc容器
        2)、注册配置类,调用refresh()刷新容器;
        3)、registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建;
            1)、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
            2)、给容器中加别的BeanPostProcessor
            3)、优先注册实现了PriorityOrdered接口的BeanPostProcessor;
            4)、再给容器中注册实现了Ordered接口的BeanPostProcessor;
            5)、注册没实现优先级接口的BeanPostProcessor;
            6)、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;
                创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
                1)、创建Bean的实例
                2)、populateBean;给bean的各种属性赋值
                3)、initializeBean:初始化bean;
                        1)、invokeAwareMethods():处理Aware接口的方法回调
                        2)、applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization()
                        3)、invokeInitMethods();执行自定义的初始化方法
                        4)、applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization();
                4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;--》aspectJAdvisorsBuilder
            7)、把BeanPostProcessor注册到BeanFactory中;
                beanFactory.addBeanPostProcessor(postProcessor);
=======以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程========

            AnnotationAwareAspectJAutoProxyCreator => InstantiationAwareBeanPostProcessor
        4)、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;创建剩下的单实例bean
            1)、遍历获取容器中所有的Bean,依次创建对象getBean(beanName);
                getBean->doGetBean()->getSingleton()->
            2)、创建bean
                【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()】
                1)、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;
                    只要创建好的Bean都会被缓存起来
                2)、createBean();创建bean;
                    AnnotationAwareAspectJAutoProxyCreator 会在任何bean创建之前先尝试返回bean的实例
                    【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】
                    【BeanPostProcessor是在Bean对象创建完成之后初始化前后调用的】
                    1)、resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation
                        希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续
                        1)、后置处理器先尝试返回对象;
                            bean = applyBeanPostProcessorsBeforeInstantiation():
                                拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor;
                                就执行postProcessBeforeInstantiation
                            if (bean != null) {
                            bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                        }

                    2)、doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例;和3.6流程一样;
                    3)、
            
        
AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】    的作用:
1)、每一个bean创建之前,调用postProcessBeforeInstantiation();
        关心MathCalculator和LogAspect的创建
        1)、判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
        2)、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,
            或者是否是切面(@Aspect)
        3)、是否需要跳过
            1)、获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】
                每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;
                判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true
            2)、永远返回false

2)、创建对象
postProcessAfterInitialization;
        return wrapIfNecessary(bean, beanName, cacheKey);//包装如果需要的情况下
        1)、getAdvicesAndAdvisorsForBean->findEligibleAdvisors获取当前bean的所有增强器(通知方法)  Object[]  specificInterceptors
            1、findCandidateAdvisors找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
            2、findAdvisorsThatCanApply获取到能在bean使用的增强器。
            3、sortAdvisors给增强器排序
        2)、this.advisedBeans.put(cacheKey, Boolean.TRUE);保存当前bean在advisedBeans中;
        3)、如果当前bean需要增强,创建当前bean的代理对象createProxy();
            1)、获取所有增强器(通知方法Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);)
            2)、保存到proxyFactory   proxyFactory.addAdvisors(advisors);
            3)、创建代理对象:Spring自动决定  proxyFactory.getProxy(getProxyClassLoader());
                JdkDynamicAopProxy(config);jdk动态代理;
                ObjenesisCglibAopProxy(config);cglib的动态代理;
        4)、给容器中返回当前组件使用cglib增强了的代理对象;
        5)、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;
        
    
    3)、目标方法执行    ;
        容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx);
        1)、CglibAopProxy.intercept();拦截目标方法的执行
        2)、根据ProxyFactory对象获取将要执行的目标方法拦截器链;
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
            1)、List<Object> interceptorList保存所有拦截器 5
                一个默认的ExposeInvocationInterceptor 和 4个增强器;
            2)、遍历所有的增强器,将其转为Interceptor;
                registry.getInterceptors(advisor);
            3)、将增强器转为List<MethodInterceptor>;
                如果是MethodInterceptor,直接加入到集合中
                如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;
                转换完成返回MethodInterceptor数组;

        3)、如果没有拦截器链,直接执行目标方法;
            拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)

              
        4)、如果有拦截器链,把需要执行的目标对象,目标方法,
            拦截器链等信息传入创建一个 CglibMethodInvocation 对象new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)
            并调用 CglibMethodInvocation.proceed()方法
        5)、拦截器链的触发过程;
            1)、如果没有拦截器,直接执行目标方法;或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)也执行目标方法;
            2)、链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;
                拦截器链的机制,保证通知方法与目标方法的执行顺序;
             
    总结:
        1)、  @EnableAspectJAutoProxy 开启AOP功能
        2)、 @EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
        3)、AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;
        4)、容器的创建流程:
            1)、registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象
            2)、finishBeanFactoryInitialization()初始化剩下的单实例bean
                1)、创建业务逻辑组件和切面组件
                2)、AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
                3)、组件创建完之后,判断组件是否需要增强
                    是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib);
        5)、执行目标方法:
            1)、代理对象执行目标方法
            2)、CglibAopProxy.intercept();
                1)、得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
                2)、利用拦截器的链式机制,依次进入每一个拦截器进行执行;
                3)、效果:
                    正常执行:前置通知-》目标方法-》后置通知-》返回通知
                    出现异常:前置通知-》目标方法-》后置通知-》异常通知
--------------------- 
作者:天高任鸟飞-海阔凭鱼跃 
来源:优快云 
原文:https://blog.youkuaiyun.com/asdasdasd123123123/article/details/81990519 
版权声明:本文为博主原创文章,转载请附上博文链接!

<think>好的,用户想了解Spring AOP原理,特别是代理模式、动态代理和静态代理。首先,我需要回顾一下代理模式的基本概念,然后区分静态代理和动态代理的不同,再联系到Spring AOP是如何应用这些技术的。 首先,代理模式的定义。根据引用[1],代理模式允许在不修改被代理对象的情况下,通过代理类进行功能增强。这里的关键是代理类分为静态和动态两种。静态代理需要手动创建代理类,而动态代理在运行时生成。用户可能想知道这两种代理在Spring AOP中的应用场景。 接下来,静态代理的细节。引用[1]提到静态代理的类在运行前已经存在,通常由程序员手动编写或工具生成。例如,假设有一个业务接口Subject和实现类RealSubject,静态代理类Proxy会实现Subject并持有RealSubject的引用,在方法调用前后添加逻辑。这种情况的缺点是每个被代理类都需要一个对应的代理类,导致代码冗余。 然后是动态代理,Spring AOP主要使用JDK动态代理和CGLIB。引用[2]指出JDK代理通过Proxy类生成代理对象,而CGLIB通过Enhancer创建。JDK动态代理要求目标类实现接口,利用InvocationHandler的invoke方法进行增强。CGLIB则通过继承目标类,重写方法来实现代理,适用于没有接口的情况。需要解释这两种方式的区别以及Spring如何选择使用哪一种。 还要结合Spring AOP的源码,如引用[2]提到的AOP源码剖析部分。Spring在运行时决定使用哪种代理方式,如果目标类实现了接口,默认使用JDK代理,否则使用CGLIB。这涉及到AOP的配置和底层机制。 用户可能还关心AOP的应用场景,比如事务管理、日志记录等,这些通过代理模式实现,无需修改原有代码。需要举例说明,比如在方法执行前后加入事务控制,动态代理如何拦截方法调用并添加额外操作。 另外,需要确保回答结构清晰,分点说明原理、代理类型、实现方式以及Spring的选择策略。同时,生成的相关问题要围绕用户可能进一步询问的点,比如性能比较、配置方法或实际应用例子。 最后,检查引用是否正确应用,如引用[1]关于代理模式的分类,引用[2]关于Spring AOP的具体实现,引用[3]关于代理模式的角色。确保每个技术点都有对应的引用支持,增加回答的可信度。</think>### Spring AOP原理与代理模式解析 #### 一、代理模式的核心作用 代理模式通过代理对象控制对真实对象的访问,在**不修改原始代码**的前提下实现功能增强。其核心角色包括: - **Subject**:定义业务接口(可以是接口或抽象类)[^3] - **RealSubject**:具体业务实现类(被代理对象) - **Proxy**:代理类,负责增强和控制对RealSubject的访问 #### 二、静态代理的实现方式 静态代理需**手动编写代理类**,代理关系在编译时确定。例如: ```java // 业务接口 interface UserService { void save(); } // 真实对象 class UserServiceImpl implements UserService { public void save() { /* 业务逻辑 */ } } // 静态代理类 class UserServiceProxy implements UserService { private UserService target; public UserServiceProxy(UserService target) { this.target = target; } public void save() { System.out.println("前置增强"); target.save(); // 调用真实对象方法 System.out.println("后置增强"); } } ``` **缺点**:每个业务类需对应一个代理类,代码冗余度高[^1]。 #### 三、动态代理的实现机制 Spring AOP主要采用两种动态代理技术: 1. **JDK动态代理**(基于接口) - 要求目标类必须实现至少一个接口 - 通过`java.lang.reflect.Proxy`生成代理对象 - 核心类`InvocationHandler`实现方法拦截: ```java class JdkProxyHandler implements InvocationHandler { private Object target; public Object getProxy(Object target) { this.target = target; return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) { System.out.println("前置增强"); Object result = method.invoke(target, args); System.out.println("后置增强"); return result; } } ``` 2. **CGLIB代理**(基于继承) - 通过生成目标类的子类实现代理 - 使用`MethodInterceptor`接口: ```java class CglibInterceptor implements MethodInterceptor { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) { System.out.println("前置增强"); Object result = proxy.invokeSuper(obj, args); System.out.println("后置增强"); return result; } } // 通过Enhancer创建代理 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(TargetClass.class); enhancer.setCallback(new CglibInterceptor()); TargetClass proxy = (TargetClass)enhancer.create(); ``` - **优势**:可代理无接口的类[^2] #### 四、Spring AOP的代理选择策略 | 条件 | 使用的代理方式 | |--------------------------|----------------| | 目标类实现了至少一个接口 | JDK动态代理 | | 目标类未实现任何接口 | CGLIB代理 | **底层实现**: - 通过`DefaultAopProxyFactory`类判断代理方式 - Spring Boot 2.x后默认强制使用CGLIB(可通过`spring.aop.proxy-target-class=true`配置) #### 五、AOP核心流程 1. 解析切面定义(@Aspect) 2. 匹配连接点(Pointcut) 3. 生成代理对象 4. 编织增强逻辑(Advice) 5. 通过责任链模式执行拦截器链 #### 六、性能对比 - **JDK动态代理**:生成速度快,执行效率稍低 - **CGLIB**:生成速度慢,执行效率高(现代JVM差距已不明显)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值