spring如何getBean

spring总是有一种神奇的魔力,让人想要去探究他到底是做了什么.

提前搭建好工程

在IDEA中选择Spring

  • 不要选择Spring Initializr, 那是使用SpringBoot的,不利于学习使用.

一路下一步, 填写项目名称, finish.

可以看到,在项目目录下的lib中,IDEA会帮我们把包导好.
lib

直接开始代码
项目目录结构

启动类放置的位置是按照SpringBoot的习惯,实际放在哪里都可以.

// Hello类中的代码
public class Hello {

    private String message = "";

    public String getMessage(){
        return message;
    }

    public void setMessage(String message){
        this.message = message;
    }

    public Hello(){}

    public Hello(String message){
        this.message = message;
    }

}

// HelloConfig类中的代码
@Configuration
@ComponentScan(basePackages = "com.echi.demo")
public class HelloConfig {

    @Bean
    public Hello hello(){
        return new Hello("hello world");
    }
}

// 主启动类的代码
public class MainClass {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(HelloConfig.class);

        Hello hello = (Hello)applicationContext.getBean("hello");
        System.out.println(hello.getMessage());
    }

}

因为是学习,所以没有加入SpringMvc中关于父子容器,路径拦截的配置, 简简单单创建一个容器将bean放进去再取出来就好了.

开始调试

因为是要看看spring是获取bean的,所以直接将断点打在getBean()这行.

从下面将spring中的代码贴上来
spring代码好看的一点就是,名字都很长, 可以做到见名知意

bean没有标注@Lazy
    // 类名 AbstractApplicationContext
    @Override
    public Object getBean(String name) throws BeansException {
        // 看看这个beanFactory是不是还活着
        // 如果已经死亡抛出一个IllegalStateException 
        assertBeanFactoryActive();
        // 根据名称去获取bean
        return getBeanFactory().getBean(name);
    }

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


    protected <T> T doGetBean(
            final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {

        // 这句和返回值没关系可以不看, 是处理bean的名称的. 
        // bean可以拥有别名,也可能是由factoryBean导致的名称干扰
        final String beanName = transformedBeanName(name);
        Object bean;

        // Eagerly check singleton cache for manually registered singletons.
        // 这句已经有注释, 是 直接去单例缓存池中获取bean.
        // 因为我们没有配置懒加载,所以这边会获取到bean
        // 返回值就是我们在config中配置的Hello对象
        Object sharedInstance = getSingleton(beanName); 
        if (sharedInstance != null && args == null) {
            // 省略部分是一些log
            ...

            // 此处是做一些判断,因为bean已经取出来了.但是取出来的有可能是factoryBean
            // 如果是factoryBean,是不能直接用的,还要通过factoryBean来获取bean
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

        else {
            // 这部分代码是没取到时的处理,稍后再展开
            ...
        }

        // Check if required type matches the type of the actual bean instance.
        // 做最后的检查
        if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
            // 检查代码省略
            ...
        }
        // 已经获取到bean, 也已经做过检查, 确认没问题了 将bean返回
        return (T) bean;
    }

在上面这段代码可以看到,成功获取到bean.
但是只有get()的动作,那什么时候放进去的呢?

既然在getBean()的时候没有看到在哪里放进去,那断点就往前打一句

    // 类名 AnnotationConfigApplicationContext
    public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
        // 做一些初始化的工作
        // 在这里初始化了
        // - beanFactory
        // - AnnotatedBeanDefinitionReader
        // - ClassPathBeanDefinitionScanner
        this();
        register(annotatedClasses);

        // 主要内容在这个方法内
        refresh();
    }


    // 类名 AbstractApplicationContext
    // 这个方法里全是调用,调用链很深,就不深入进去了.
    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 省略部分代码
            ...

            try {
                ...

                // 在这一个方法内,将Hello类的BeanDefinition装入beanDefinitionMap中.
                invokeBeanFactoryPostProcessors(beanFactory);

                ...

                // 在这个方法内将Hello类装入到单例缓存池中.
                finishBeanFactoryInitialization(beanFactory);


                ...
            }

            catch (BeansException ex) {
                // 省略不必要的代码
                ...
            }

            finally {
                ...
            }
        }
    }

    // 类名 AbstractApplicationContext
    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        // 省略代码
        ...

        // 调用了beanFactory的方法将类装载进单例缓存池
        beanFactory.preInstantiateSingletons();
    }


    // 类名 DefaultListableBeanFactory

    @Override
    public void preInstantiateSingletons() throws BeansException {
        ...

        // 获取bean定义的名称
        List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);

        // 循环去挨个实例化
        for (String beanName : beanNames) {
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                // 如果是factoryBean, 有factoryBean的实例化方式
                if (isFactoryBean(beanName)) {
                    // 使用factoryBean的方式创建bean
                    ...
                }
                // 如果是单纯的bean,直接走上面getBean时省略的步骤.
                // 在getBean()中会创建bean
                else {
                    getBean(beanName);
                }
            }
        }

        for (String beanName : beanNames) {
            // 调用bean初始化后的回调
            ...
        }
    }

仔细看一下getBean中创建bean的方式

    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;

        // 因此这里讨论的是使用该方法创建bean,所以单例缓存池中应该是没有缓存该bean的.
        Object sharedInstance = getSingleton(beanName); // 返回null
        if (sharedInstance != null && args == null) {
            // 不讨论此处
            ...
        }

        else {
            // 在单例缓存池中没有找到bean, 进入该判断分支
            // 判断是否有依赖循环的情况
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

            // 看看有没有父容器,是不是在父容器中已经有该bean
            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // 没有父容器, 此处不讨论
                ...
            }


            if (!typeCheckOnly) {
                // 将该bean标记为已创建, 防止多线程竞争时创建多次. 内部使用的是双重检查
                markBeanAsCreated(beanName);
            }

            try {
                // 开始创建bean的流程
                // 先获取beanDefinition
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                // 检查该bean有没有依赖其他bean, 如果有需要将依赖的bean先解决
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    // 解决依赖bean
                    ...
                }

                // 正式开始创建bean
                // 如果是单例bean,进入该分支
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
                        @Override
                        public Object getObject() throws BeansException {
                            try {
                                // 再内部就是一堆很深的调用,不再深入.
                                return createBean(beanName, mbd, args);
                            }
                            catch (BeansException ex) {
                                destroySingleton(beanName);
                                throw ex;
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
                // 创建prototype的bean进入该分支
                // prototype就是每次获取都是一个全新的bean
                else if (mbd.isPrototype()) {
                    // It's a prototype -> create a new instance.
                    Object prototypeInstance = null;
                    try {
                        beforePrototypeCreation(beanName);
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }

                else {
                    String scopeName = mbd.getScope();
                    final Scope scope = this.scopes.get(scopeName);
                    // 如果不是单例也不是原型模式, 说明是其他的 request ,session和global session
                    // 这几种scope都是和web请求相关的
                }
            }
            catch (BeansException ex) {
                cleanupAfterBeanCreationFailure(beanName);
                throw ex;
            }
        }

        if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
            ...
        }
        return (T) bean;
    }
bean标注@Lazy的情况

@Lazy表示懒加载,只有在使用的情况才会去加载.
在上面的例子中,bean的创建是在ApplicationContext applicationContext = new AnnotationConfigApplicationContext(HelloConfig.class);这一行执行时创建的,在getBean()时直接可以取到.
加了@Lazy注解之后,bean的创建的在getBean()的时候去创建并放入单例缓存池中的.


   转载规则


《spring如何getBean》 echi1995 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
AQS同步器原理 AQS同步器原理
总所周知,java是支持多线程的.在多线程情况下,可能会出现多个线程同时访问同一个共享,可变资源的情况;这种资源可能是:对象,变量,文件等.共享:资源可以由多个线程同时访问可变:资源可以在其生命周期内被修改 为什么需要同步器先来个栗子
下一篇 
SpringBoot(二) SpringBoot如何实现自动配置 SpringBoot(二) SpringBoot如何实现自动配置
springboot也用了很久了,用的时候只知道他很好用,不需要再额外的配置乱七八糟的东西,那么到底是怎么做到的呢?以前我只会回答:SpringBoot中已经帮助我们配置了.现在回过来看看这个问题, 配置千奇百怪,SpringBoot是怎么
  目录