Spring源码分析系列bean的加载

Posted 霓裳梦竹

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring源码分析系列bean的加载相关的知识,希望对你有一定的参考价值。

前言

以 BeanFactory bf  = new XmlBeanFactory(new ClassPathResource("beans.xml"));为例查看bean的加载过程。

一、首先来看Spring中是如何实现的

 1 @Override
 2     public Object getBean(String name) throws BeansException {
 3         return getBean(name, Object.class);
 4     }
 5 
 6     @Override
 7     public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
 8         try {
 9             if (isSingleton(name)) {
10                 return doGetSingleton(name, requiredType);
11             }
12             else {
13                 return lookup(name, requiredType);
14             }
15         }
16         catch (NameNotFoundException ex) {
17             throw new NoSuchBeanDefinitionException(name, "not found in JNDI environment");
18         }
19         catch (TypeMismatchNamingException ex) {
20             throw new BeanNotOfRequiredTypeException(name, ex.getRequiredType(), ex.getActualType());
21         }
22         catch (NamingException ex) {
23             throw new BeanDefinitionStoreException("JNDI environment", name, "JNDI lookup failed", ex);
24         }
25     }
1   //检查要加载的bean是否是单例    
2   @Override
3     public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
4         return this.shareableResources.contains(name);
5     }
//如果是单例的话从单例的map中获取bean,如果有的话说明已经加载则直接从map中获取,如果没有则将bean放入单例类的map中,单例在Spring的容器中只会被创建一次,后续再获取bean,就直接从单例缓存获取了。当日这里只是尝试加载,首先尝试从缓存中加载,如果加载不成功则再次尝试从singletonFactories中加载。因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,在Spring中场景bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光到缓存中,一旦下一个bean创建时候需要依赖上一个bean则直接使用ObjectFactory.
@SuppressWarnings("unchecked")
    private <T> T doGetSingleton(String name, Class<T> requiredType) throws NamingException {
        synchronized (this.singletonObjects) {
            if (this.singletonObjects.containsKey(name)) {
                Object jndiObject = this.singletonObjects.get(name);
                if (requiredType != null && !requiredType.isInstance(jndiObject)) {
                    throw new TypeMismatchNamingException(
                            convertJndiName(name), requiredType, (jndiObject != null ? jndiObject.getClass() : null));
                }
                return (T) jndiObject;
            }
            T jndiObject = lookup(name, requiredType);
            this.singletonObjects.put(name, jndiObject);
            return jndiObject;
        }
    }
 1 //实例化Bean
 2 /**
 3      * Perform an actual JNDI lookup for the given name via the JndiTemplate.
 4      * <p>If the name doesn‘t begin with "java:comp/env/", this prefix is added
 5      * if "resourceRef" is set to "true".
 6      * @param jndiName the JNDI name to look up
 7      * @param requiredType the required type of the object
 8      * @return the obtained object
 9      * @throws NamingException if the JNDI lookup failed
10      * @see #setResourceRef
11      */
12     protected <T> T lookup(String jndiName, Class<T> requiredType) throws NamingException {
13         Assert.notNull(jndiName, "‘jndiName‘ must not be null");
14         String convertedName = convertJndiName(jndiName);
15         T jndiObject;
16         try {
17             jndiObject = getJndiTemplate().lookup(convertedName, requiredType);
18         }
19         catch (NamingException ex) {
20             if (!convertedName.equals(jndiName)) {
21                 // Try fallback to originally specified name...
22                 if (logger.isDebugEnabled()) {
23                     logger.debug("Converted JNDI name [" + convertedName +
24                             "] not found - trying original name [" + jndiName + "]. " + ex);
25                 }
26                 jndiObject = getJndiTemplate().lookup(jndiName, requiredType);
27             }
28             else {
29                 throw ex;
30             }
31         }
32         if (logger.isDebugEnabled()) {
33             logger.debug("Located object with JNDI name [" + convertedName + "]");
34         }
35         return jndiObject;
36     }

二、FactoryBean的使用

一般情况下,Spring通过反射机制利用bean的class属性指定实现类来实例化bean.在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息,配置信息的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.beans.factory.FactoryBean<T>的工厂类接口,可以通过实现该接口定制实例化bean的逻辑。

FactoryBean接口对于Spring框架来说是非常重要的,Spring自身就提供了70多个FactoryBean的实现,他们影藏了一些实例化复杂bean的细节,给上层应用带来了便利。

该接口中的方法

  1 /*
  2  * Copyright 2002-2016 the original author or authors.
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *      http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 package org.springframework.beans.factory;
 18 
 19 /**
 20  * Interface to be implemented by objects used within a {@link BeanFactory} which
 21  * are themselves factories for individual objects. If a bean implements this
 22  * interface, it is used as a factory for an object to expose, not directly as a
 23  * bean instance that will be exposed itself.
 24  *
 25  * <p><b>NB: A bean that implements this interface cannot be used as a normal bean.</b>
 26  * A FactoryBean is defined in a bean style, but the object exposed for bean
 27  * references ({@link #getObject()}) is always the object that it creates.
 28  *
 29  * <p>FactoryBeans can support singletons and prototypes, and can either create
 30  * objects lazily on demand or eagerly on startup. The {@link SmartFactoryBean}
 31  * interface allows for exposing more fine-grained behavioral metadata.
 32  *
 33  * <p>This interface is heavily used within the framework itself, for example for
 34  * the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the
 35  * {@link org.springframework.jndi.JndiObjectFactoryBean}. It can be used for
 36  * custom components as well; however, this is only common for infrastructure code.
 37  *
 38  * <p><b>{@code FactoryBean} is a programmatic contract. Implementations are not
 39  * supposed to rely on annotation-driven injection or other reflective facilities.</b>
 40  * {@link #getObjectType()} {@link #getObject()} invocations may arrive early in
 41  * the bootstrap process, even ahead of any post-processor setup. If you need access
 42  * other beans, implement {@link BeanFactoryAware} and obtain them programmatically.
 43  *
 44  * <p>Finally, FactoryBean objects participate in the containing BeanFactory‘s
 45  * synchronization of bean creation. There is usually no need for internal
 46  * synchronization other than for purposes of lazy initialization within the
 47  * FactoryBean itself (or the like).
 48  *
 49  * @author Rod Johnson
 50  * @author Juergen Hoeller
 51  * @since 08.03.2003
 52  * @see org.springframework.beans.factory.BeanFactory
 53  * @see org.springframework.aop.framework.ProxyFactoryBean
 54  * @see org.springframework.jndi.JndiObjectFactoryBean
 55  */
 56 public interface FactoryBean<T> {
 57 
 58     /**
 59          *返回由FactoryBean创建的bean实例,如果isSingleton返回true则该实例会放到Spring容器中单实例缓存池中
 60      * Return an instance (possibly shared or independent) of the object
 61      * managed by this factory.
 62      * <p>As with a {@link BeanFactory}, this allows support for both the
 63      * Singleton and Prototype design pattern.
 64      * <p>If this FactoryBean is not fully initialized yet at the time of
 65      * the call (for example because it is involved in a circular reference),
 66      * throw a corresponding {@link FactoryBeanNotInitializedException}.
 67      * <p>As of Spring 2.0, FactoryBeans are allowed to return {@code null}
 68      * objects. The factory will consider this as normal value to be used; it
 69      * will not throw a FactoryBeanNotInitializedException in this case anymore.
 70      * FactoryBean implementations are encouraged to throw
 71      * FactoryBeanNotInitializedException themselves now, as appropriate.
 72      * @return an instance of the bean (can be {@code null})
 73      * @throws Exception in case of creation errors
 74      * @see FactoryBeanNotInitializedException
 75      */
 76     T getObject() throws Exception;
 77 
 78     /**
 79          *返回FactoryBean创建的bean类型
 80      * Return the type of object that this FactoryBean creates,
 81      * or {@code null} if not known in advance.
 82      * <p>This allows one to check for specific types of beans without
 83      * instantiating objects, for example on autowiring.
 84      * <p>In the case of implementations that are creating a singleton object,
 85      * this method should try to avoid singleton creation as far as possible;
 86      * it should rather estimate the type in advance.
 87      * For prototypes, returning a meaningful type here is advisable too.
 88      * <p>This method can be called <i>before</i> this FactoryBean has
 89      * been fully initialized. It must not rely on state created during
 90      * initialization; of course, it can still use such state if available.
 91      * <p><b>NOTE:</b> Autowiring will simply ignore FactoryBeans that return
 92      * {@code null} here. Therefore it is highly recommended to implement
 93      * this method properly, using the current state of the FactoryBean.
 94      * @return the type of object that this FactoryBean creates,
 95      * or {@code null} if not known at the time of the call
 96      * @see ListableBeanFactory#getBeansOfType
 97      */
 98     Class<?> getObjectType();
 99 
100     /**
101          *返回由FactoryBean创建的bean实例的作用域是singleton还是prototype
102      * Is the object managed by this factory a singleton? That is,
103      * will {@link #getObject()} always return the same object
104      * (a reference that can be cached)?
105      * <p><b>NOTE:</b> If a FactoryBean indicates to hold a singleton object,
106      * the object returned from {@code getObject()} might get cached
107      * by the owning BeanFactory. Hence, do not return {@code true}
108      * unless the FactoryBean always exposes the same reference.
109      * <p>The singleton status of the FactoryBean itself will generally
110      * be provided by the owning BeanFactory; usually, it has to be
111      * defined as singleton there.
112      * <p><b>NOTE:</b> This method returning {@code false} does not
113      * necessarily indicate that returned objects are independent instances.
114      * An implementation of the extended {@link SmartFactoryBean} interface
115      * may explicitly indicate independent instances through its
116      * {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean}
117      * implementations which do not implement this extended interface are
118      * simply assumed to always return independent instances if the
119      * {@code isSingleton()} implementation returns {@code false}.
120      * @return whether the exposed object is a singleton
121      * @see #getObject()
122      * @see SmartFactoryBean#isPrototype()
123      */
124     boolean isSingleton();
125 
126 }

当配置文件中<bean>的class配置的实现类是FactoryBean时,通过getbean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。

1 <bean id="car" class="com.slp.factorybean.CarFactoryBean" carInfo="BMW,400,200000"/>

当调用getBean("car")时,Spring通过反射机制发现CarFactoryBean实现了FactoryBean的接口,这时Spring容器就调用接口方法CarFactoryBean#getObject()方法返回,如果希望获取CarFactoryBean的实例则需要使用getBean(beanName)方法时在beanName前显式的加上&前缀,例如getBean("&car");

三、从缓存中获取单例bean

介绍过FactoryBean的用法之后,就可以了解bean加载的过程了。单例在Spring的同一个容器内只会被创建一次,后续再获取bean直接从单例缓存中获取,当然这里也只是尝试加载,首先尝试从缓存中加载,然后再次尝试从singletonFactorry加载因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,Spring创建bean的原则不等bean创建完成就会创建bean的ObjectFactory提早曝光加入到缓存中,一旦下一个bean创建时需要依赖上个bean,则直接使用ObjectFactory.

四、从bean的实例中获取对象

在getBean方法中,getObjectForBeanInstance是个使用非常多的方法,,物流是从缓存中获得bean还是根据不同的scope策略加载bean,宗旨我们得到bean的第一步就是用这个方法检测其正确性。也就是检测当前bean是否是FactoryBean类型的bean,如果是那么需要调用该bean对应的FactoryBean实例中的getObject()作为返回值。

无论是从缓存中获取到的bean还是通过不同的scope策略加载的bean都只是最原始的bena状态,并不一定是我们最终想要的bean.

五、循环依赖

循环依赖其实就是循环引用,循环调用时无法解决的,除非有终结条件,否则就是死循环,最终导致内存溢出错误。

Spring容器循环依赖包括构造器循环依赖个setter循环依赖。

1、构造器循环依赖

通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示循环依赖。

Spring容器将每一个正在创建的bean标识符放在一个“当前创建bean池”中,bean标识符在创建过程中将一直保持在这个池中,因此如果在创建bean的过程中发现自己已经在“当前创建bean池”里时,将抛出BeanCurrentlyInCreationException异常表示循环依赖,而对于创建完毕的bean将从“当前创建bean池”中清除掉。

警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘testA‘ defined in class path resource [beans2.xml]: Cannot resolve reference to bean ‘testB‘ while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘testB‘ defined in class path resource [beans2.xml]: Cannot resolve reference to bean ‘testC‘ while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘testC‘ defined in class path resource [beans2.xml]: Cannot resolve reference to bean ‘testA‘ while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘testA‘: Requested bean is currently in creation: Is there an unresolvable circular reference?
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘testA‘ defined in class path resource [beans2.xml]: Cannot resolve reference to bean ‘testB‘ while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘testB‘ defined in class path resource [beans2.xml]: Cannot resolve reference to bean ‘testC‘ while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘testC‘ defined in class path resource [beans2.xml]: Cannot resolve reference to bean ‘testA‘ while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘testA‘: Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:634)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:145)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1193)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1095)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
at com.slp.TestConfig.main(TestConfig.java:24)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘testB‘ defined in class path resource [beans2.xml]: Cannot resolve reference to bean ‘testC‘ while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘testC‘ defined in class path resource [beans2.xml]: Cannot resolve reference to bean ‘testA‘ while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘testA‘: Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:634)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:145)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1193)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1095)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 17 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘testC‘ defined in class path resource [beans2.xml]: Cannot resolve reference to bean ‘testA‘ while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘testA‘: Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:634)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:145)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1193)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1095)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 29 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘testA‘: Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:347)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 41 more

 

2、setter循环依赖

表示通过setter注入方式构成的循环依赖,对于setter注入造成的依赖是通过Spring容器提前暴露刚完成的构造器注入但未完成其他步骤的bean来完成的,而且只能解决单例作用域的bean循环依赖。通过提前暴露一个单例工厂方法,从而使得其他bean能引用到该bean.

1)Spring容器创建单例"testA"bean,首先根据无参构造器创建bean,并暴露一个ObjectFactory用于返回一个提前暴露一个创建中的bean,并将testA标识符放到"当前创建bean池",然后进行setter注入"testB";

2)Spring容器创建单例“testB”bean,首先根据无参构造器创建bean,并暴露一个"ObjectFactory"用于返货一个提前暴露一个创建中的bean,并将"testB"标识符放到"当前创建bean池",然后进行setter注入"circle".

3)Spring容器创建单例“testC” bean,首先根据无参构造器创建bean,并暴露一个ObjectFactory用于返回一个提前暴露一个创建中的bean,并将testC标识符放到“当前创建bean池”,然后进行setter注入testA,进行注入testA时由于提前暴露了ObjectFactory工厂,从而使用它返回提前暴露一个创建中的bean.

4)最后在依赖注入testB和testA完成setter注入

























































以上是关于Spring源码分析系列bean的加载的主要内容,如果未能解决你的问题,请参考以下文章

Spring源码分析Bean加载流程概览

Spring源码分析Bean加载流程概览

spring源码深度解析— IOC 之 开启 bean 的加载

Spring源码分析非懒加载的Bean实例化过程(下篇)

Spring读源码系列04----bean的加载---上

Spring读源码系列05----bean的加载---下