一文搞懂Spring IOC(万字长文深度解析源码)

06-01 1447阅读

Spring IOC超详细源码解析

  • 一. 介绍
  • 二. 使用IOC
  • 三. 工作原理和工作流程
    • IOC的工作原理
    • 主要做了些什么?
    • 是怎么做到的?
      • 1、加载解析Bean
      • 2、注册Bean
      • 3、实例化Bean(从图纸变成实物)
      • 4、依赖注入
      • 5、初始化Bean
      • 6、使用Bean
      • 7、销毁Bean
      • 动手debug尝试
      • 四. 单例池管理(缓存已创建的Bean)
      • 五. 扩展机制(BeanPostProcessor)

        一. 介绍

        什么是IOC

        IOC是Spring的核心之一。全称是 Inversion of Control,中文翻译就是“控制反转”。是面向对象编程的一种设计原则,用来减少代码之间的耦合。

        IOC为何出现?又为何叫”控制反转“?

        在没有spring IOC之前,每次Java对象的创建、配置和管理都得程序员自己手动一个个去解决,在工作中往往Java对象往往成百上千,一个一个创建处理,显然不现实。为了方便对象管理,减少程序员工作量,就有了Spring IOC。

        将原本应用代码对对象创建和管理的控制权都交给Spring容器,减少代码的耦合,便于维护和扩展。

        IOC通过依赖注入(DI)来管理组成应用程序的组件,这些组件也就是Java对象,被称为Spring Beans。

        所以IOC也可以理解为一个对象的自动装配的大容器。

        二. 使用IOC

        基于xml或者基于注解。其中基于注解可以是配置类+注解,也可以是扫包+注解。以下为简单示例。

        User.class

        @Data  //lombok注解
        public class User {
            private String username;
            private String sex;
        }
        

        application.xml(基于xml方式时需要创建)

        
            
                
                
            
        
        

        BeanConfiguration.java(基于注解+配置类时需要创建)

        @Configuration //告诉spring这是一个配置类
        public class BeanConfiguration {
            @Bean(value="user")  //丢到spring容器中,给spring管理。这样Spring就知道这是一个bean,会创建这个对象
            //如果不加value值,默认bean的名字就是方法名,加value属性可以自定义bean的名字
            public User user(){
                User user = new User();
                user.setUsername("张三");
                user.setSex("男");
                return user;
            }
        }
        

        基于注解+扫包方式时,只需要修改User.java文件

        @Data
        @Component
        public class User {
            @Value("张三")  //注入属性值
            private String username;
            @Value("男")
            private String sex;
        }
        

        test.java

        public class test {
            public static void main(String[] args) {
            //1、基于xml文件
            	//加载和解析xml配置
                ClassPathXmlApplicationContext context1 = new ClassPathXmlApplicationContext("application.xml");
                //获取bean
                System.out.println(context1.getBean("user"));
                
           //2、基于注解+配置类
           		//加载和解析注解配置类
           		AnnotationConfigApplicationContext context2 = new AnnotationConfigApplicationContext(BeanConfiguration.class);
                System.out.println(context2.getBean("user"));
                
        		//当配置类很多的时候,可以放在一个包下,通过指定包来加载,避免一个一个去手动加载
        		AnnotationConfigApplicationContext context3 = new AnnotationConfigApplicationContext("com.esther.config");
                System.out.println(context3.getBean("user"));
          //3、基于注解+扫包
          		//最简单的方式,不再需要依赖于xml文件或者配置类,而是直接将bean的创建类交给目标类,让目标类来创建。
          		//只需要在实体类上加上注解@Component,告诉Spring这个类要被注入到ioc,让ioc去创建这个类对象。如果没有component注解,下面输出就报报错找不到bean。
          		AnnotationConfigApplicationContext context4 = new AnnotationConfigApplicationContext("com.esther.pojo");
                System.out.println(context4.getBean(User.class));
            }
        }
        

        扩展:依赖注入

        Company.class

        @Data
        public class Company {
        	@Value("001")
            private int code;
            @Autowired
            private User employee;  //依赖另一个对象
        }
        

        ioc创建完user和company对象后,会自动把user对象装配到company对象中,这就是依赖注入

        @Autowired是通过类型进行注入,如果需要通过名称注入使用@Qualifier(“xxx”),此时需要在类的component注解上加上名称,如@Component(“xxx”)

        三. 工作原理和工作流程

        在了解完Spring IOC是什么,为什么要出现以及基本的使用后,那就该疑问了,IOC到底是怎么实现对象的自动装配的呢?

        Spring提供了两个主要的IOC容器:BeanFactory和ApplicationContext。

        BeanFactory翻译就是对象工厂,是Spring IOC容其的最底层实现,负责管理Bean的生命周期。(Bean即Java对象)

        ApplicationContext是BeanFactory的子接口,不仅包含了BeanFactory的功能,还提供了更多高级的功能。比如:支持国际化、事件机制、资源加载以及AOP

        IOC的工作原理

        依赖注入(Dependency Injection)是IOC的核心实现方式

        IOC容器通过反射机制来创建和管理Bean实例。容器会维护一个Bean的缓存池,当需要使用Bean时,可以直接从缓存池中获取,而不需要每次都重新创建。

        IOC容器通过配置文件或者注解来解析Bean的依赖关系。在配置文件中,可以通过标签来定义Bean和其依赖;在注解中,可以通过@Autowired、@Resource等注解来指定依赖关系。

        主要做了些什么?

        • 加载配置

          可以加载基于XML的配置文件、基于Java注解的配置类等,获取Bean的定义信息。

        • 实例化Bean

          根据配置信息,通过反射机制创建Bean的实例。

        • 依赖注入

          在创建Bean实例时,为Bean实例注入依赖的其他Bean或者基本类型的属性值。

        • 生命周期管理

          控制Bean的创建和销毁过程,包括初始化和销毁方法的调用。

          是怎么做到的?

          1、加载解析Bean

          想要管理Bean,首先需要把Bean加载进来。

          • IOC容器启动时首先会加载配置元数据(如XML文件、Java注解或者Java配置类定义数据)
          • 加载过程主要由 ApplicationContext 接口的实现类完成。如ClassPathXmlApplicationContext (基于xml文件)或 AnnotationConfigApplicationContext(基于注解)。

            ApplicationContext的所有实现类:

            一文搞懂Spring IOC(万字长文深度解析源码)

            关键源码(refresh方法)

            (Spring 5.x版本)
            public class ClassPathXmlApplicationContext extends AbstractApplicationContext {
                public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh) {
                    super(configLocations, refresh);
                    if (refresh) {
                        refresh();
                    }
                }
                @Override
                protected void refresh() {
                    synchronized (this.startupShutdownMonitor) {
                        // 准备刷新上下文
                        prepareRefresh();
                        // 加载配置文件
                        loadBeanDefinitions();
                        // 完成刷新
                        finishRefresh();
                    }
                }
                private void loadBeanDefinitions() {
                    if (getConfigLocations() != null) {
                        for (String location : getConfigLocations()) {
                            loadBeanDefinition(location);
                        }
                    }
                }
                private void loadBeanDefinition(String location) {
                    // 加载 XML 文件
                    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
            		//此方法具体见下一代码块
                   reader.loadBeanDefinitions(location);
                }
            }
            
            public class XmlBeanDefinitionReader {
                public void loadBeanDefinitions(String location) {
                    // 加载 XML 文件
                    Document doc = loadDocument(location);
                    // 解析 XML 文件
                    parseBeanDefinitions(doc);
                }
                private void parseBeanDefinitions(Document doc) {
                    // 获取根元素
                    Element root = doc.getDocumentElement();
                    // 解析每个 bean 元素
                    NodeList nodeList = root.getElementsByTagName("bean");
                    for (int i = 0; i  
            

            源码分析

            以ClassPathXmlApplicationContext为例

            • 当我们在代码中写下ApplicationContext context = new ClassPathXmlApplicationContext(“beans.xml”)

              容器就已经开始启动了。它在构造函数中调用了refresh() 方法,这个方法会加载并解析配置文件。

            • 在 loadBeanDefinition() 方法中 ,使用XmlBeanDefinitionReader来解析XML配置文件。
            • XmlBeanDefinitionReader会通过ClassPathResource加载 xml,使用DOM解析器将XML转成Document对象。然后遍历bean标签,创建BeanDefinition对象。

              BeanDefinition 是什么?

              它就像Bean的 “身份证”,保存了类名、作用域(单例/原型)等元数据。

              BeanDefinition包含了类的名称、依赖关系、初始化方法、销毁方法等元数据信息。

              拓展

              Spring 6.x版本用 ReentrantLock 替换了 synchronized,如下:

              private final ReentrantLock refreshLock = new ReentrantLock();
              public void refresh() {
                  refreshLock.lock();
                  try {
                      prepareRefresh();
                      // 后续步骤...
                  } finally {
                      refreshLock.unlock();
                  }
              }
              

              为什么要升级?

              主要是为了提升并发性和灵活性。 ReentrantLock 提供了更灵活的锁管理(如超时、中断),利用tryLock减少死锁风险,尤其在复杂并发场景下性能更好。

              2、注册Bean

              加载解析完配置时,IOC把配置信息转换成容器可以理解的数据结构(BeanDefinition对象)。然后就开始注册Bean了

              场景类比

              想象BeanDefinition是家具的设计图纸,而BeanFactory是工厂的仓库。这一步就是把所有图纸存进仓库。

              关键源码:

              public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
                  this.beanDefinitionMap.put(beanName, beanDefinition);
                  // ...其他处理(如清除缓存)
              }
              

              源码分析

              • 这里发挥作用的主要是DefaultListableBeanFactory类的registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法。
              • DefaultListableBeanFactory是BeanFactory接口的实现类

                拓展

                Spring 6.x中对存储BeanDefinition的数据结构做了性能优化(例如使用更高效的ConcurrentHashMap变体),在存在大量Bean时减少内存的占用。

                3、实例化Bean(从图纸变成实物)

                • 当应用程序需要使用某个bean时,Spring IOC容器会注册的bean的信息创建Bean的实例。

                  此场景问题

                  如何将BeanDefinition 变成真正的Java对象?比如 new User()

                  关键源码:

                  public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory {
                      public Object getBean(String name) throws BeansException {
                          // 获取 BeanDefinition
                          BeanDefinition beanDefinition = getBeanDefinition(name);
                          // 创建 Bean 实例
                          return createBean(beanDefinition);
                      }
                      private Object createBean(BeanDefinition beanDefinition) {
                          // 获取 Bean 的类名
                          String className = beanDefinition.getBeanClassName();
                          // 加载类
                          Class beanClass = loadClass(className);
                          // 实例化 Bean
                          Object bean = instantiateBean(beanClass);
                          // 注入依赖
                          injectDependencies(bean, beanDefinition);
                          // 初始化 Bean
                          initializeBean(bean, beanDefinition);
                          return bean;
                      }
                      private Object instantiateBean(Class beanClass) {
                          // 使用反射机制创建实例
                          try {
                              return beanClass.newInstance();
                          } catch (InstantiationException | IllegalAccessException e) {
                              throw new BeanCreationException("Failed to instantiate bean", e);
                          }
                      }
                  }
                  

                  源码分析

                  • 在createBean() 方法中,首先获取从前面注册的beanDefinitionMap中获取注册的BeanDefinition对象,拿到bean的定义信息。
                  • 然后通过反射机制创建Bean的实例
                  • 接着注入依赖并初始化bean

                    4、依赖注入

                    • Spring容器会根据bean的定义信息,将依赖的bean注入到目标bean中。(如前面实例的职工对象里面有一个属性是用户对象)
                    • 依赖注入可以通过构造函数、Setter方法或者字段注解(如@Autowired、@Resource)来实现。

                      关键源码:

                      private void injectDependencies(Object bean, BeanDefinition beanDefinition) {
                          // 获取 Bean 的类
                          Class beanClass = bean.getClass();
                          // 获取所有字段
                          Field[] fields = beanClass.getDeclaredFields();
                          for (Field field : fields) {
                              // 检查是否有 @Autowired 注解
                              if (field.isAnnotationPresent(Autowired.class)) {
                                  // 获取依赖的 Bean 名称
                                  String dependencyName = field.getName();
                                  // 获取依赖的 Bean 实例
                                  Object dependency = getBean(dependencyName);
                                  // 注入依赖
                                  try {
                                      field.setAccessible(true);
                                      field.set(bean, dependency);
                                  } catch (IllegalAccessException e) {
                                      throw new BeanCreationException("Failed to inject dependency", e);
                                  }
                              }
                          }
                      }
                      

                      源码分析

                      • DefaultListableBeanFactory类提供了 injectDependencies() 方法,用于注入依赖。
                      • 在injectDependencies() 方法中,遍历Bean的所有字段,检查是否有@Autowired 注解,如果有,则获取依赖的Bean实例,并通过反射机制注入到目标Bean中。

                        5、初始化Bean

                        Spring的IOC容器会调用Bean的初始化方法,完成Bean的初始化工作。初始化方法可以通过实现InitializingBean 接口、使用@PostConstruct 注解或者在XML配置中指定 init-method 来定义。

                        关键源码:

                        private void initializeBean(Object bean, BeanDefinition beanDefinition) {
                            // 检查是否实现了 InitializingBean 接口
                            if (bean instanceof InitializingBean) {
                                try {
                                    ((InitializingBean) bean).afterPropertiesSet();
                                } catch (Exception e) {
                                    throw new BeanCreationException("Failed to initialize bean", e);
                                }
                            }
                            // 检查是否有 @PostConstruct 注解
                            Method[] methods = bean.getClass().getDeclaredMethods();
                            for (Method method : methods) {
                                if (method.isAnnotationPresent(PostConstruct.class)) {
                                    try {
                                        method.setAccessible(true);
                                        method.invoke(bean);
                                    } catch (Exception e) {
                                        throw new BeanCreationException("Failed to initialize bean", e);
                                    }
                                }
                            }
                            // 调用 XML 配置中的 init-method
                            String initMethod = beanDefinition.getInitMethod();
                            if (initMethod != null) {
                                try {
                                    Method method = bean.getClass().getMethod(initMethod);
                                    method.invoke(bean);
                                } catch (Exception e) {
                                    throw new BeanCreationException("Failed to initialize bean", e);
                                }
                            }
                        }
                        

                        源码分析

                        • 在initializingBean() 方法中,首先检查Bean是否实现了InitializingBean 接口,如果是,则调用afterpropertiesSet() 方法。
                        • 接着,检查是否有@PostConstruct 注解的方法,并调用这些方法
                        • 最后调用XML配置中指定的 init-method

                          6、使用Bean

                          应用程序可以通过Spring容器获取Bean实例,并调用其方法。

                          关键源码:

                          public interface ApplicationContext extends EnvironmentAware, ResourceLoaderAware, ApplicationEventPublisher, MessageSource {
                              Object getBean(String name) throws BeansException;
                               T getBean(String name, Class requiredType) throws BeansException;
                              Object getBean(String name, Object... args) throws BeansException;
                          }
                          

                          源码分析

                          • ApplicationContext 接口提供了 getBean() 方法,用于获取Bean实例。
                          • 在ClassPathXmlApplicationContext()类中,getBean() 方法会调用 DefaultListableBeanFactory 的 getBean() 方法,获取Bean实例。

                            7、销毁Bean

                            当Spring容器关闭时,会调用Bean的销毁方法。销毁方法可以通过实现 DisposableBean 接口、使用@PreDestory 注解或者在XML配置中指定destroy-method 来定义。

                            关键源码:

                            public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory {
                                public void destroyBean(String name) {
                                    // 获取 BeanDefinition
                                    BeanDefinition beanDefinition = getBeanDefinition(name);
                                    // 获取 Bean 实例
                                    Object bean = getBean(name);
                                    // 检查是否实现了 DisposableBean 接口
                                    if (bean instanceof DisposableBean) {
                                        try {
                                            ((DisposableBean) bean).destroy();
                                        } catch (Exception e) {
                                            throw new BeanCreationException("Failed to destroy bean", e);
                                        }
                                    }
                                    // 检查是否有 @PreDestroy 注解
                                    Method[] methods = bean.getClass().getDeclaredMethods();
                                    for (Method method : methods) {
                                        if (method.isAnnotationPresent(PreDestroy.class)) {
                                            try {
                                                method.setAccessible(true);
                                                method.invoke(bean);
                                            } catch (Exception e) {
                                                throw new BeanCreationException("Failed to destroy bean", e);
                                            }
                                        }
                                    }
                                    // 调用 XML 配置中的 destroy-method
                                    String destroyMethod = beanDefinition.getDestroyMethod();
                                    if (destroyMethod != null) {
                                        try {
                                            Method method = bean.getClass().getMethod(destroyMethod);
                                            method.invoke(bean);
                                        } catch (Exception e) {
                                            throw new BeanCreationException("Failed to destroy bean", e);
                                        }
                                    }
                                }
                            }
                            

                            源码分析

                            • 在 destoryBean() 方法中,首先检查Bean是否实现了DisposableBean接口,如果是,则调用 destory() 方法。
                            • 接着,检查是否有@PreDestroy注解的方法,并调用这些方法。
                            • 最后调用XML配置中指定的 destory-method

                              动手debug尝试

                              在 IDEA 中打开 Spring 源码,找到 AbstractApplicationContext.refresh() 方法,依次跳转查看每个步骤的执行流程,结合断点调试观察 Bean 的创建过程。通过实践,这些抽象概念会变得直观!

                              Spring 6.x版本,IOC注册bean调用链

                              AbstractApplicationContext类

                              refresh() =》 obtainFreshBeanFactory() =》refreshBeanFactory()

                              AbstractRefreshableApplicationContext类

                              refreshBeanFactory() =》loadBeanDefinitions()

                              AbstractXmlApplicationContext类

                              loadBeanDefinitions(DefaultListableBeanFactory beanFactory) =》loadBeanDefinitions(XmlBeanDefinitionReader reader)

                              AbstractBeanDefinitionReader类

                              loadBeanDefinitions(Resource… resources) =》loadBeanDefinitions((Resource)resource)

                              XmlBeanDefinitionReaderl类

                              loadBeanDefinitions(Resource resource) =》 doLoadBeanDefinitions() =》 registerBeanDefinitions()

                              DefaultBeanDefinitionDocumentReader类

                              registerBeanDefinitions() =》 doRegisterBeanDefinitions()

                              Spring 6.x版本,IOC依赖注入调用链

                              AbstractApplicationContext.refresh()

                              → finishBeanFactoryInitialization(beanFactory) // 初始化所有单例 Bean

                              → beanFactory.preInstantiateSingletons()

                              → DefaultListableBeanFactory.getBean(beanName)

                              → AbstractBeanFactory.doGetBean(…)

                              → AbstractAutowireCapableBeanFactory.createBean(…)

                              → doCreateBean(…) // 核心步骤 ❗

                              → createBeanInstance() // 1. 通过构造器实例化

                              → populateBean() // 2. 【依赖注入发生在此处】

                              → initializeBean() // 3. 初始化(例如 @PostConstruct)

                              四. 单例池管理(缓存已创建的Bean)

                              思考

                              单例Bean如何保证全局唯一? 单例池。

                              关键源码:(DefaultSingletonBeanRigistry类)

                              private final Map singletonObjects = new ConcurrentHashMap(256);
                              public Object getSingleton(String beanName) {
                                  Object singletonObject = this.singletonObjects.get(beanName);
                                  if (singletonObject == null) {
                                      // 双重检查锁创建单例...
                                  }
                                  return singletonObject;
                              }
                              

                              Spring 6.x优化了单例创建的并发控制逻辑,减少锁竞争,提升容器启动速度。

                              五. 扩展机制(BeanPostProcessor)

                              思考

                              如何在Bean初始化前后插入自定义逻辑?比如打印日志。

                              关键源码:(BeanPostPorcessor类)

                              @Component
                              public class MyBeanPostProcessor implements BeanPostProcessor {
                                  @Override
                                  public Object postProcessBeforeInitialization(Object bean, String beanName) {
                                      System.out.println("初始化之前:" + beanName);
                                      return bean;
                                  }
                              }
                              

                              源码分析

                              这段源码触发点是在initionlizeBean()方法中,循环调用所有BeanPostProcessor的方法。

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码