ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

IoC底层原理

IoC底层原理

Spring是如何创建和管理对象的

先读取配置信息(注解或XML),知道"需要管理哪些类"

// 你写的代码:@ServicepublicclassUserService{@AutowiredprivateUserDaouserDao;}@RepositorypublicclassUserDao{}
Spring 读到这些注解后,心里就知道了: UserService 这个类需要我管理 UserDao 这个类也需要我管理 UserService 里有一个 userDao 字段,需要我注入 UserDao 的实例

把配置信息封装成 BeanDefinition

  • 不是直接new对象,先要把Bean对象的信息记录一下
/** * BeanDefinition 就像一张"对象的图纸" * 它记录了:用哪个类创建、怎么初始化、依赖谁、作用域是什么 * * Spring 拿到这个图纸后,才按图纸去创建真正的对象 */// Spring 内部大概做了这样的事(简化版):// 为 UserService 创建一张图纸BeanDefinitionuserServiceBD=newBeanDefinition();userServiceBD.setBeanClass(UserService.class);// 用哪个类userServiceBD.setScope("singleton");// 单例userServiceBD.addDependentBean("userDao");// 依赖 userDao// 为 UserDao 创建一张图纸BeanDefinitionuserDaoBD=newBeanDefinition();userDaoBD.setBeanClass(UserDao.class);userDaoBD.setScope("singleton");

根据 BeanDefinition 创建对象(实例化)

  • 根据图纸用反射创建对象
/** * 反射 = 在运行时动态地创建对象、调用方法、访问字段 * * 你写 new UserService() 是在编译时就决定了创建哪个类 * 反射是在运行时,拿到类的名字(Class对象),再去创建实例 */// Spring 用反射创建对象的大致过程(简化):Class<?>clazz=Class.forName("com.example.service.UserService");// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// 通过类的全限定名,拿到 Class 对象// 这就是"反射"的第一步Objectinstance=clazz.getDeclaredConstructor().newInstance();// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^// 通过 Class 对象,调用无参构造器创建实例// 这相当于 new UserService()// 但它是动态的,不用写死在代码里// 为什么用反射而不用 new?// 因为 Spring 在写代码的时候根本不知道要管理哪些类// 它是在运行时读取你的配置才知道的// 所以不可能写死 new XXX(),只能用反射动态创建

填充依赖(依赖注入)

  • 对象创建好啦但是@Autowired字段是空的,还需要依赖注入
// Spring 内部大致做了这样的事(简化版):// 1. 创建 UserService 实例UserServiceuserService=newUserService();// 实际是反射创建的// 2. 找到 UserService 中被 @Autowired 标注的字段FielduserDaoField=UserService.class.getDeclaredField("userDao");// ↑ 反射获取字段userDaoField.setAccessible(true);// ↑ 因为 private 字段不能直接访问,需要设置可访问// 3. 从容器中取出已经创建好的 UserDao 实例ObjectuserDao=container.getBean("userDao");// ↑ 从 Spring 容器里拿// 4. 把 UserDao 注入到 UserService 的 userDao 字段中userDaoField.set(userService,userDao);// ↑ 相当于 userService.userDao = userDao// 但用反射绕过了 private 限制

两个重要的接口BeanFactory 和 ApplicationContext

BeanFactory(最底层的容器接口) │ │ 它是最基本的容器,只有"创建 Bean + 获取 Bean"的能力 │ ▼ ApplicationContext(更高级的容器接口) │ │ 它继承了 BeanFactory,并增加了更多能力: │ - 国际化(i18n) │ - 事件发布(ApplicationEvent) │ - 资源加载 │ - AOP 支持 │ ▼ 我们平时用的容器(都是 ApplicationContext 的实现): │ ├── ClassPathXmlApplicationContext → 读 XML 配置(老项目) ├── AnnotationConfigApplicationContext → 读注解配置(现代项目) └── FileSystemXmlApplicationContext → 读文件系统中的 XML 面试回答: BeanFactory 是最基础的容器,采用懒加载策略(用到时才创建Bean) ApplicationContext 是 BeanFactory 的子接口,功能更丰富, 采用预加载策略(启动时就创建所有单例Bean) 实际开发中基本都用 ApplicationContext
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进