手撕Spring底层系列之:IOC、AOP

人们眼中的天才之所以卓越非凡,并非天资超人一等而是付出了持续不断的努力。1万小时的锤炼是任何人从平凡变成超凡的必要条件。------------ 马尔科姆·格拉德威尔


目录

一、开篇:为什么必须理解Spring底层?

二、IOC容器:控制反转的本质解析

[2.1 核心机制:工厂模式 + 反射](#2.1 核心机制:工厂模式 + 反射)

[2.2 容器启动六步流程](#2.2 容器启动六步流程)

[2.3 循环依赖的破解之道](#2.3 循环依赖的破解之道)

三、AOP设计:动态代理的两种武器

[3.1 AOP 概念](#3.1 AOP 概念)

[3.2 AOP 实现思想](#3.2 AOP 实现思想)

[3.3 JDK动态代理 vs CGLIB字节码增强](#3.3 JDK动态代理 vs CGLIB字节码增强)

[3.4 应用场景](#3.4 应用场景)

四、总结


🌟 嗨,我是Xxtaoaooo!

本系列将用源码解剖+拆分核心轮子的方式,带你暴力破解Spring底层逻辑。

警告:阅读后可能导致看Spring源码时产生「庖丁解牛」般的快感!

话不多说,直接开干!


一、开篇:为什么必须理解Spring底层?

作为数年Java程序猿,我曾陷入"会用Spring却不懂Spring"的尴尬境地。当**@Autowired** 突然失效、事务注解不回滚时,盲目搜索解决方案的挫败感 让我决心撕开Spring的优雅封装。本系列文章正是我个人深入了解源码后提炼的核心设计精华,你将看到:

  • IoC容器如何用三级缓存破解循环依赖的魔鬼细节
  • AOP动态代理的选择逻辑(JDK vs CGLIB)
  • Bean生命周期中9个关键扩展点的实战应用
  • 如何手写简化版Spring核心模块

二、IOC容器:控制反转的本质解析

2.1 核心机制:工厂模式 + 反射

  • 工厂模式ApplicationContext作为中央工厂,替代传统的new对象方式
  • 反射机制:运行时动态解析类信息并实例化对象
java 复制代码
// 反射实现Bean实例化简化代码
public class SimpleBeanFactory {
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    public Object getBean(String beanName) throws Exception {
        Object bean = singletonObjects.get(beanName);
        if (bean != null) return bean;

        // 反射创建对象(简化版)
        Class<?> clazz = Class.forName(beanName);
        bean = clazz.getDeclaredConstructor().newInstance();
        singletonObjects.put(beanName, bean);
        return bean;
    }
}

2.2 容器启动六步流程

1. A[启动 refresh() 方法]:

    • 这是整个容器初始化的入口。无论是通过new ClassPathXmlApplicationContext(...)还是new AnnotationConfigApplicationContext(...),最终都会调用**refresh()**方法来启动容器。

2. B[创建并准备 BeanFactory]:

    • BeanFactory是Spring IoC容器的核心。在此阶段,会创建一个DefaultListableBeanFactory实例,并对其进行一些基础设置,比如设置类加载器。

3. C[扫描并加载 BeanDefinitions]:

    • Spring会扫描指定的配置源(如XML文件、注解**@Component**/@Service等),将找到的Bean解析成BeanDefinition对象。
    • BeanDefinition是Bean的"蓝图"或"配方",包含了Bean的类名、作用域、依赖关系、属性等元数据。这些BeanDefinition会被注册到BeanFactory中。

4. D[注册 BeanPostProcessors]:

    • BeanPostProcessor是Spring提供的一个强大扩展点,它允许在Bean实例化、配置和初始化前后进行自定义处理(例如,AOP就是通过它实现的)。
    • 这一步非常关键,它会找出所有实现了BeanPostProcessor接口的Bean,并将它们注册到BeanFactory中,以便在后续的Bean创建过程中使用。

5. E[初始化所有非懒加载的单例Bean]:

    • 这是容器初始化的核心工作。Spring会遍历BeanFactory中所有的BeanDefinition,并根据定义创建Bean实例(对于非懒加载的单例Bean)。
    • 这个过程包括了:实例化(调用构造函数)、填充属性(依赖注入)、执行初始化回调(如**@PostConstruct方法或afterPropertiesSet方法),以及应用BeanPostProcessor**。

6. F[发布上下文就绪事件]:

    • 当所有单例Bean都初始化完成后,Spring会发布一个ContextRefreshedEvent事件。
    • 这标志着容器已经完全准备就绪,可以对外提供服务。其他应用程序组件可以监听这个事件来执行一些启动后的逻辑(例如,启动一个定时任务)。

总结为:

  1. 配置解析:读取XML/注解的Bean定义
  2. BeanFactory构建 :初始化DefaultListableBeanFactory
  3. 后置处理器注册 :处理@Autowired等注解逻辑
  4. 预实例化单例:非懒加载Bean的初始化
  5. 依赖注入 :通过populateBean()填充属性
  6. 生命周期回调 :执行init-method@PostConstruct

2.3 循环依赖的破解之道

三级缓存是解决循环依赖的核心数据结构:

java 复制代码
public class DefaultSingletonBeanRegistry {
    // 一级缓存:完整Bean
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    // 二级缓存:早期引用(未填充属性)
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    // 三级缓存:Bean工厂(可生成代理对象)
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
}

单向对象注入的代码验证

XML 复制代码
<bean id="userDao" class="org.xt.dao.impl.UserDaoImpl"></bean>

<bean id="userService" class="org.xt.service.impl.UserServiceImpl">
  <property name="name" value="123"></property>
  <property name="userDao" ref="userDao"></property>
</bean>

容器中有userDao Bean对象的执行流程:

userDao创建 → userService创建

→ userService执行注入userDao的操作: setUserDao方法执行

XML 复制代码
<bean id="userService" class="org.xt.service.impl.UserServiceImpl">
  <property name="name" value="123"></property>
  <property name="userDao" ref="userDao"></property>
</bean>

<bean id="userDao" class="org.xt.dao.impl.UserDaoImpl"></bean>

容器中没有userDao Bean对象的执行流程:

userService创建 → userDao创建

→ userService执行注入userDao的操作: setUserDao方法执行

双向对象引用

循环引用(依赖):多个实体之间相互依赖并形成闭环的情况。

循环引用的解决方法:

Spring提供了三级缓存存储完整Bean实例和半成品Bean实例,用于解决循环引用问题。

三级缓存存储未被引用的对象,如果找到未被引用的对象,那么将从三级缓存删除,放入二级缓存,一级缓存是存完成的bean对象。

结合dao和service层循环引用结合三级缓存进行解析:


三、AOP设计:动态代理的两种武器

3.1 AOP 概念

什么是AOP?

AOP Aspect Oriented Programming 顾名思义就是面向切面编程,是对面向对象编程OOP的一个升华,面向对象编程是纵向对一个事物的抽象,一个对象包括静态属性信息,动态方法信息等。而AOP是横向的对不同事物的抽象,属性与属性、属性与方法、方法与方法、都可以组成一个切面,这种思维去设计编程的方式被称为面向切面编程。

3.2 AOP 实现思想

**其思想实现方案依靠动态代理技术,在运行期间,对目标对象的方法进行增强,代理对象同名方法内可以执行原有逻辑的同时嵌入执行其他增强逻辑或其他对象的方法。**动态代理技术主要依靠Java的反射机制来实现。

Java提供了两种动态代理实现的方式:基于接口的动态代理(JDK动态代理)和基于类的动态代理(CGLIB动态代理)。JDK动态代理只能代理实现了接口的类,而CGLIB动态代理可以代理任意的类,但其原理是通过生成目标类的子类来实现代理。

A对象是目标对象,B对象是增强对象

A对象内部的方法被称为目标方法

B对象内部的方法被称为增强方法

模拟AOP实现的基础代码

java 复制代码
public interface UserService {
    void show1();
    void show2();
}

//目标对象
public class UserServiceImpl implements UserService {
    @Override
    public void show1() {
        System.out.println("show1...");
    }

    @Override
    public void show2() {
        System.out.println("show2...");
    }
}

//增强类,内部提供增强方法
public class MyAdvice {
    public void beforeAdvice(){
        System.out.println("前置的增强...");
    }
    public void afterAdvice(){
        System.out.println("后置的增强...");
    }
}


public class MockAopBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {

    ApplicationContext applicationContext;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //  目的:对UserServiceImpl中的show1和show2方法进行增强,增强方法存在MyAdvice中
        //  问题1:筛选UserServiceImpl或者service.impl包下的所有方法都可以进行增强 解决方案:添加判断
        //  问题2:MyAdvice怎么获取到 解决方法:从spring容器中获取
        if(bean.getClass().getPackage().getName().equals("org.xt.service.impl")){
            //生成当前Bean的Proxy对象
            Object beanProxy = Proxy.newProxyInstance(
                bean.getClass().getClassLoader(),
                bean.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    //执行增强对象的Before方法
                    MyAdvice myAdvice = applicationContext.getBean(MyAdvice.class);
                    myAdvice.beforeAdvice();
                    //执行目标对象的目标方法
                    Object result = method.invoke(bean, args);
                    //执行增强对象的After方法
                    myAdvice.afterAdvice();
                    return result;
                });
            return beanProxy;
        }
        return bean;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

3.3 JDK动态代理 vs CGLIB字节码增强

|----------|---------------------|---------------------|
| 对比维度 | JDK动态代理 | CGLIB |
| 代理对象要求 | 必须实现接口 | 可代理任意类 |
| 性能 | 调用快,创建慢 | 创建快,调用稍慢 |
| 底层技术 | Java反射机制 | ASM字节码操作 |
| 方法拦截器 | InvocationHandler | MethodInterceptor |
| 适用场景 | 接口-based代理 | 类继承-based代理 |

代理创建核心源码解析

java 复制代码
// DefaultAopProxyFactory选择代理方式
public AopProxy createAopProxy(AdvisedSupport config) {
    if (config.isOptimize() || config.isProxyTargetClass()) {
        return new CglibAopProxy(config); // 使用CGLIB
    }
    return new JdkDynamicAopProxy(config); // 使用JDK代理
}

// JDK代理调用流程
public Object invoke(Object proxy, Method method, Object[] args) {
    // 1. 获取目标对象
    Object target = getTarget();
    // 2. 获取拦截器链
    List<Object> chain = getInterceptorsAndDynamicInterceptionAdvice(method);
    // 3. 执行拦截器链
    return new ReflectiveMethodInvocation(target, method, args, chain).proceed();
}

通知(Advice)执行时序

3.4 应用场景

IOC容器的典型应用

AOP在微服务中的实战

java 复制代码
// 分布式事务切面示例
@Aspect
public class GlobalTransactionAspect {
    @Around("@annotation(com.distributed.tx)")
    public Object handleTransaction(ProceedingJoinPoint joinPoint) {
        try {
            // 1. 创建全局事务ID
            String txId = UUID.randomUUID().toString();
            // 2. 通知参与者注册事务
            TransactionManager.register(txId);
            // 3. 执行目标方法
            Object result = joinPoint.proceed();
            // 4. 提交事务
            TransactionManager.commit(txId);
            return result;
        } catch (Throwable e) {
            // 5. 回滚事务
            TransactionManager.rollback(txId);
            throw new RuntimeException(e);
        }
    }
}

四、总结

优秀的框架设计,往往是把复杂留给自己,把简单留给使用者。

Spring的成功正是这一理念的完美实践------通过IoC容器标准化对象生命周期 ,通过AOP解耦横切关注点,最终实现开发效率的质的飞跃。

回顾本系列的核心收获:

  1. IoC的本质是管理权转移:将对象的控制权从程序员手中移交至容器,通过三级缓存等精妙设计解决工程难题
  2. AOP的根基是代理模式:动态代理技术使非侵入式功能扩展成为可能,JDK与CGLIB的互补成就了Spring的灵活性
  3. 手写轮子的终极意义:当完成手搓AOP实现时,那些曾令人困惑的面试题(如Bean生命周期、循环依赖)将自然内化为你的设计能力

致每一位不甘于CRUD的开发者:撕开框架底层的过程如同攀登技术高原,虽充满挑战,但站在设计思想的顶峰时,你看到的将不再是零散的API,而是一片可自由创造的新大陆。


🌟 嗨,我是Xxtaoaooo!

⚙️ 【点赞】让更多同行看见深度干货

🚀 【关注】持续获取行业前沿技术与经验

🧩 【评论】分享你的实战经验或技术困惑

作为一名技术实践者,我始终相信:

每一次技术探讨都是认知升级的契机,期待在评论区与你碰撞灵感火花🔥

相关推荐
LaoZhangAI2 分钟前
GPT-4o mini API限制完全指南:令牌配额、访问限制及优化策略【2025最新】
前端·后端
LaoZhangAI17 分钟前
FLUX.1 API图像尺寸设置全指南:优化生成效果与成本
前端·后端
rockmelodies20 分钟前
【JAVA安全】Java 集合体系详解
java·python·安全·集合
Z_W_H_21 分钟前
【SpringBoot】实战-开发接口-用户-注册
java·spring boot·spring
非ban必选22 分钟前
spring-ai-alibaba官方 Playground 示例之联网搜索代码解析2
人工智能·python·spring
Kookoos25 分钟前
ABP VNext + EF Core 二级缓存:提升查询性能
后端·.net·二级缓存·ef core·abp vnext
生活百般滋味,人生需要笑对。 --佚名27 分钟前
springboot如何redis锁
java·spring boot·redis
月初,31 分钟前
SpringBoot集成Minio存储文件,开发图片上传等接口
java·spring boot·后端
oioihoii34 分钟前
C++11迭代器改进:深入理解std::begin、std::end、std::next与std::prev
java·开发语言·c++
Huckings38 分钟前
Android车载系统时间同步方案具体实现
android·java