详解Spring的GetBean方法

导读:本篇文章讲解 详解Spring的GetBean方法,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

Spring容器提供了五种获取Bean的方式,可以根据beanName获取Bean,也可以根据classType来获取Bean,所有根据beanName来获取Bean的方式,底层都会通过调用下面的doGetBean()来获取Bean对象

protected <T> T doGetBean(
      String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)

一、根据beanName获取Bean

Spring容器提供了三种根据beanName获取Bean的方式:

// 启动Spring容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 直接根据beanName来获取
UserService userService = (UserService) context.getBean("userService");
// 根据beanName获取Bean后,判断该Bean是否属于UserService类型
UserService userService1 = (UserService) context.getBean("userService",UserService.class);
// 指定构造方法参数,使用指定的构造方法的生成Bean,如果UserService是单例的,则容器启动的过程中就会去自行推断构造方法然后创建单例Bean;如果UserService是原型的,则每次都会getBean()时,会根据后面提供的构造方法参数使用指定的构造方法创建Bean
UserService userService2 = (UserService) context.getBean("userService",new OrderService());

上面三种方式都会调用doGetBean()来获取Bean对象,下面将详细介绍doGetBean()的实现

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

    // name有可能是 &xxx 或者 xxx,如果name是&xxx,那么beanName就是xxx
    // name有可能传入进来的是别名,那么beanName就是id
    String beanName = transformedBeanName(name);
    Object beanInstance;

    // Eagerly check singleton cache for manually registered singletons.
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        // 如果sharedInstance是FactoryBean,那么就调用getObject()返回对象
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

上述代码在《实例化单例Bean》的FactoryBean创建部分有大概介绍过,下面将详细介绍核心的代码方法

1.1 name转换,获取真正的beanName

transformedBeanName()主要将传进来的name转换成真正的beanName,具体实现如下:

protected String transformedBeanName(String name) {
    return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

先看里面的BeanFactoryUtils.transformedBeanName()实现,下面代码在FactoryBean的创建中已经讲过,想要获取一个FactoryBean的对象,那么传进来的name是以“&”前缀开头,可以只有一个“&”,也可以有多个“&”,下面的代码只是将前缀的“&”去掉而已

public static String transformedBeanName(String name) {
    Assert.notNull(name, "'name' must not be null");
    if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
        return name;
    }
    return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
        do {
            beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
        }
        while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
        return beanName;
    });
}

接下来看canonicalName()实现了怎样的功能,调用BeanFactoryUtils.transformedBeanName()返回的可能还不是最终的beanName,可能是Bean的一个别名,使用@Bean注解创建Bean的时候,可以为Bean指定多个名称,如下所示:

Spring会把第一个名字即“userService”作为真正的beanName,而后面的名字作为Bean的别名进行存储

@Bean({"userService","userService1"})
public UserService userService(){
    return new UserService();
}

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
//根据别名也能获取Bean
UserService userService = (UserService) context.getBean("userService1");

Bean的别名存放在一个aliasMap中,其中KEY=别名,VALUE=beanName/别名,根据别名从aliasMap中拿到的可能是真正的beanName,也可能还是一个别名,所以用do-while循环,直到拿出来的名字从aliasMap再找不到对应的值,那么该名字就是真正的beanName了

public String canonicalName(String name) {
    String canonicalName = name;
    // Handle aliasing...
    String resolvedName;
    do {
        resolvedName = this.aliasMap.get(canonicalName);
        if (resolvedName != null) {
            canonicalName = resolvedName;
        }
    }
    while (resolvedName != null);
    return canonicalName;
}

1.2 父BeanFactory创建Bean对象

获取到真正的beanName之后,不论是单例Bean还是原型Bean,都会调用getSingleton(beanName)去单例池中查找是否有单例Bean。如果从单例池中取出对象了,接下来的getObjectForBeanInstance()主要根据原始name判断当前Bean是否是FactoryBean,以及获取FactoryBean对象。如果单例池中不存在,则需要创建Bean实例

如果当前BeanFactory的beanDefinitionMap中不存在该beanName,且存在parentBeanFactory,则让parentBeanFactory去创建Bean

BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
    // Not found -> check parent.
    // &&&&xxx---->&xxx
    String nameToLookup = originalBeanName(name);
    if (parentBeanFactory instanceof AbstractBeanFactory) {
        return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
            nameToLookup, requiredType, args, typeCheckOnly);
    }
    else if (args != null) {
        // Delegation to parent with explicit args.
        return (T) parentBeanFactory.getBean(nameToLookup, args);
    }
    else if (requiredType != null) {
        // No args -> delegate to standard getBean method.
        return parentBeanFactory.getBean(nameToLookup, requiredType);
    }
    else {
        return (T) parentBeanFactory.getBean(nameToLookup);
    }
}

originalBeanName(name)方法用于将别名和”&&&”格式的名字进行转换真正的beanName或FactoryBean的beanName

protected String originalBeanName(String name) {
    String beanName = transformedBeanName(name);
    if (name.startsWith(FACTORY_BEAN_PREFIX)) {
        beanName = FACTORY_BEAN_PREFIX + beanName;
    }
    return beanName;
}

1.3 当前BeanFactory来创建Bean

从mergedBeanDefinitions中根据beanName获取RootBeanDefinition,《实例化单例Bean》中提到过真正创建Bean的时候,用的是合并后的RootBeanDefinition而不是普通的BeanDefinition

然后判断当前RootBeanDefinition是否是抽象的,如果是抽象的将会抛出异常

接着获取该Bean的@DependsOn注解依赖的beanName

  • 创建@DependsOn注解依赖Bean对象

    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    // 检查BeanDefinition是不是Abstract的
    checkMergedBeanDefinition(mbd, beanName, args);
    // Guarantee initialization of beans that the current bean depends on.
    String[] dependsOn = mbd.getDependsOn();
    

    DependsOn注解的使用如下:

    DependsOn注解指明在创建UserServicce的Bean之前,需要先创建orderService和user,在Spring生成BeanDefinition的时候,就回去解析@DependsOn,然后将依赖的beanName存放在BeanDefinition的字符串数组dependsOn中

    @Component
    @DependsOn({"orderService","user"})
    public class UserService {
    }
    

    如果DependsOn数组不为空,将遍历数组,首先判断是否存在@DependsOn的循环依赖(这个地方的循环依赖不同于依赖注入的循环依赖),即UserService依赖于user,而User的@DependsOn又依赖于userService,对于这种情况,将直接抛出异常

    如果不存在循环依赖,则把这种依赖关系存放在dependentBeanMap中,它的key表示被依赖Bean的beanName,而value是一个Set,存放依赖该Bean的beanName集合,然后调用getBean(dep)来创建依赖的Bean对象

    if (dependsOn != null) {
        // dependsOn表示当前beanName所依赖的,当前Bean创建之前dependsOn所依赖的Bean必须已经创建好了
        for (String dep : dependsOn) {
            // beanName是不是被dep依赖了,如果是则出现了循环依赖
            if (isDependent(beanName, dep)) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
            }
            // dep被beanName依赖了,存入dependentBeanMap中,dep为key,beanName为value
            registerDependentBean(dep, beanName);
    
            // 创建所依赖的bean
            try {
                getBean(dep);
            }
            catch (NoSuchBeanDefinitionException ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
            }
        }
    }
    
  • 创建单例Bean

    如果RootBeanDefinition的scope属性是单例的,则调用getSingleton创建单例Bean

    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            try {
                return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
                destroySingleton(beanName);
                throw ex;
            }
        });
        // 前面讲过,主要用于获取FactoryBean
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
    

    getSingleton(beanName, ObjectFactory<?> singletonFactory)先从单例池中根据beanName来获取单例Bean,如果单例池中没有,则再通过createBean来创建单例Bean

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                ……
                try {
                    // 通过lambda表达式调用createBean方法
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                        }
                ……
                // 添加到单例池
                    if (newSingleton) {
                        addSingleton(beanName, singletonObject);
                    }
            }
            return singletonObject;
        }
    }
    
  • 创建原型Bean

    如果RootBeanDefinition的scope属性是原型的,则直接调用createBean方法创建原型Bean

    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);
        }
        beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
    }
    
  • 创建其他作用域Bean

    除了单例和原型Bean,Spring还支持request、session和application作用域的Bean

    首先获取scopeName,然后根据scopeName获取对应的Scope对象,比如:RequestScope和SessionScope

    Scope对象的get方法都会去调用AbstractRequestAttributesScope类的get(),RequestScope和SessionScope都继承该类

    String scopeName = mbd.getScope();
    Scope scope = this.scopes.get(scopeName);
    if (scope == null) {
        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
    }
    try {  // session.getAttriute(beaName)  setAttri
        Object scopedInstance = scope.get(beanName, () -> {
            beforePrototypeCreation(beanName);
            try {
                return createBean(beanName, mbd, args);
            }
            finally {
                afterPrototypeCreation(beanName);
            }
        });
        beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
    }
    

    getScope()返回Scope对象对应的Scope值,然后调用getAttribute方法,如果request和session中都没有Bean实例,

    public Object get(String name, ObjectFactory<?> objectFactory) {
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        Object scopedObject = attributes.getAttribute(name, getScope());
        if (scopedObject == null) {
            // lambda表达式调用createBean创建实例
            scopedObject = objectFactory.getObject();
            // 并将Bean设置到对应的属性中
            attributes.setAttribute(name, scopedObject, getScope());
            ……
        }
        return scopedObject;
    }
    

    getAttribute根据传进来的Scope值,判断从request还是session去根据name获取对应属性的对象

    public Object getAttribute(String name, int scope) {
        if (scope == SCOPE_REQUEST) {
            if (!isRequestActive()) {
                throw new IllegalStateException(
                    "Cannot ask for request attribute - request is not active anymore!");
            }
            return this.request.getAttribute(name);
        }
        else {
            HttpSession session = getSession(false);
            if (session != null) {
                Object value = session.getAttribute(name);
                if (value != null) {
                    this.sessionAttributesToUpdate.put(name, value);
                }
                return value;
            }
            return null;
        }
    }
    

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/112166.html

(0)
Java光头强的头像Java光头强

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!