能简单说一下 Spring IOC 的实现机制吗?

Spring的IOC本质上就是一个大工厂,我们可以想象一下一个工厂是如何运作的。

  • 生产产品:工厂最核心的功能是生产产品。在Spring中,不需要由Bean自己来实例化,而是交给Spring来完成。这是通过反射来实现的。

那么工厂如何管理生产过程呢?答案无疑是工厂模式

  • 库存产品:工厂通常都会有库房,用于存放产品,因为生产出来的产品不能立即运走。在Spring中,我们知道它是一个容器,容器中存放的就是对象。为了避免每次需要对象时都进行现场的反射创建,创建好的对象需要被存储起来。
  • 订单处理:工厂根据什么来提供产品呢?是根据订单。这些订单可能各式各样,有线上下单的,有工厂签订的,还有工厂销售人员上门签订的。这些订单经过处理后指导工厂进行生产和出货。

在Spring中也有类似的订单,它就是我们定义Bean和定义它们之间的依赖关系。这可以是XML形式的配置,也可以是我们熟悉的注解形式。

Bean 定义:

Bean 通过一个配置文件定义,把它解析成一个类型。

  • beans.properties

偷懒,这里直接用了最方便解析的 properties,这里直接用一个<key,value>类型的配置来代表 Bean 的定义,其中 key 是 beanName,value 是 class

userDao:cn.fighter3.bean.UserDao
  • BeanDefinition.java

bean 定义类,配置文件中 bean 定义对应的实体

public class BeanDefinition {

    private String beanName;

    private Class beanClass;
     //省略getter、setter
 }
  • ResourceLoader.java

资源加载器,用来完成配置文件中配置的加载

public class ResourceLoader {

    public static Map<String, BeanDefinition> getResource() {
        Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>(16);
        Properties properties = new Properties();
        try {
            InputStream inputStream = ResourceLoader.class.getResourceAsStream("/beans.properties");
            properties.load(inputStream);
            Iterator<String> it = properties.stringPropertyNames().iterator();
            while (it.hasNext()) {
                String key = it.next();
                String className = properties.getProperty(key);
                BeanDefinition beanDefinition = new BeanDefinition();
                beanDefinition.setBeanName(key);
                Class clazz = Class.forName(className);
                beanDefinition.setBeanClass(clazz);
                beanDefinitionMap.put(key, beanDefinition);
            }
            inputStream.close();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return beanDefinitionMap;
    }

}
  • BeanRegister.java

对象注册器,这里用于单例 bean 的缓存,我们大幅简化,默认所有 bean 都是单例的。可以看到所谓单例注册,也很简单,不过是往 HashMap 里存对象。

public class BeanRegister {

    //单例Bean缓存
    private Map<String, Object> singletonMap = new HashMap<>(32);

    /**
     * 获取单例Bean
     *
     * @param beanName bean名称
     * @return
     */
    public Object getSingletonBean(String beanName) {
        return singletonMap.get(beanName);
    }

    /**
     * 注册单例bean
     *
     * @param beanName
     * @param bean
     */
    public void registerSingletonBean(String beanName, Object bean) {
        if (singletonMap.containsKey(beanName)) {
            return;
        }
        singletonMap.put(beanName, bean);
    }

}
  • BeanFactory.java

spring-c6b3b707-cf53-4c7c-a6f9-8560950806fc.png

  • 对象工厂,我们最核心的一个类,在它初始化的时候,创建了 bean 注册器,完成了资源的加载。
  • 获取 bean 的时候,先从单例缓存中取,如果没有取到,就创建并注册一个 bean

    public class BeanFactory {
    
        private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    
        private BeanRegister beanRegister;
    
        public BeanFactory() {
            //创建bean注册器
            beanRegister = new BeanRegister();
            //加载资源
            this.beanDefinitionMap = new ResourceLoader().getResource();
        }
    
        /**
         * 获取bean
         *
         * @param beanName bean名称
         * @return
         */
        public Object getBean(String beanName) {
            //从bean缓存中取
            Object bean = beanRegister.getSingletonBean(beanName);
            if (bean != null) {
                return bean;
            }
            //根据bean定义,创建bean
            return createBean(beanDefinitionMap.get(beanName));
        }
    
        /**
         * 创建Bean
         *
         * @param beanDefinition bean定义
         * @return
         */
        private Object createBean(BeanDefinition beanDefinition) {
            try {
                Object bean = beanDefinition.getBeanClass().newInstance();
                //缓存bean
                beanRegister.registerSingletonBean(beanDefinition.getBeanName(), bean);
                return bean;
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
  • 测试

    • UserDao.java

    我们的 Bean 类,很简单

    public class UserDao {
    
        public void queryUserInfo(){
            System.out.println("A good man.");
        }
    }
    • 单元测试

      public class ApiTest {
          @Test
          public void test_BeanFactory() {
              //1.创建bean工厂(同时完成了加载资源、创建注册单例bean注册器的操作)
              BeanFactory beanFactory = new BeanFactory();
      
              //2.第一次获取bean(通过反射创建bean,缓存bean)
              UserDao userDao1 = (UserDao) beanFactory.getBean("userDao");
              userDao1.queryUserInfo();
      
              //3.第二次获取bean(从缓存中获取bean)
              UserDao userDao2 = (UserDao) beanFactory.getBean("userDao");
              userDao2.queryUserInfo();
          }
      }
    • 运行结果

      A good man.
      A good man.

至此,我们一个乞丐+破船版的 Spring 就完成了,代码也比较完整,有条件的可以跑一下。

标签: java, Java面试题, Java问题合集, Java编程, Java问题精选, Java常见问题