penglongli / blog

18 stars 1 forks source link

Spring IoC 原理简单分析 #119

Open penglongli opened 6 years ago

penglongli commented 6 years ago

Spring IoC 原理简单分析

Spring IoC 的最主要的目的是为了解耦。IoC 容器管理了一系列的 Bean,在服务启动的过程中,会通过配置文件(XML)、注解标识(@ Bean)来把需要管理的 Bean 实例化加载进来。这些 Bean 默认是单例的,也可以有其它的几种模式在下文会简单说明。

目前用到比较多的就是使用 @Bean 的办法来加载。

@ Bean 的使用

首先,我们写一个 Spring Boot 的应用:

@Controller
@EnableAutoConfiguration
public class Application {

    @Bean
    public User user1() {
        User user = new User();
        user.setAge(23);
        user.setName("Pelin_li");

        return user;
    }

    @Bean
    public User user2() {
        User user = new User();
        user.setAge(23);
        user.setName("Pelin_li2");

        return user;
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Resource
    private ApplicationContext context;

    @RequestMapping("/")
    @ResponseBody
    User home() {
        return (User) context.getBean("user2");
    }
}

在其中我们定义了 User 类的两个 Bean 实例化过程。然后我们调用 ApplicationContex.getBean("user2") 来得到 Bean 的值。结果是肯定的,我们得到了 "Pelin_li2" 的对象。

可以看到 @Bean 我们并没有定义 name 属性,默认的是方法名。

getBean() 调用过程

上边是简单的使用,下边先看下他们之间的关系图:

如果 debug 代码,可以看到大概的调用过程:

  1. 调用 AbstractApplicationContext.getBean(String name) 方法:

    @Override
    public Object getBean(String name) throws BeansException {
       assertBeanFactoryActive();
       return getBeanFactory().getBean(name);
    }

    其中,getBeanFactory() 方法由 GenericApplicationContext.java 实现,返回的是 DefaultListableBeanFactory 。即上述代码最终会调用:

    DefaultListableBeanFactory.getBean(String name)
  2. 调用 AbstractBeanFactory.getBean(String name) 方法(因为 DefaultListableBeanFactory 继承自这个 abstract 类):

    @Override
    public Object getBean(String name) throws BeansException {
       return doGetBean(name, null, null, false);
    }

    可以发现,其调用了 doGetBean() 方法,我们看下此方法的实现:

    protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
    
       final String beanName = transformedBeanName(name);
    Object bean;
    
       // Eagerly check singleton cache for manually registered singletons.
    Object sharedInstance = getSingleton(beanName);
       if (sharedInstance != null && args == null) {
            if (logger.isDebugEnabled()) {
                if (isSingletonCurrentlyInCreation(beanName)) {
                    logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");
                }
                else {
                    logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
       //省略
    }

    其中:

    • Object sharedInstance = getSingleton(beanName); 通过此方法获得共享对象,看 getSingleton() 方法能够发现,IoC 容器最底层其实就是一个 ConcurrentHashMap
    • bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); 这个方法主要是用来判断 Object 是否是 FactoryBean 的一个实例。

IoC 容器加载过程