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的加载的主要内容,如果未能解决你的问题,请参考以下文章