spring ioc
Spring IoC容器的设计
一)从接口BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,是一条主要的BeanFactory
设计路径。在这条接口设计路径中,BeanFactory接口定义了基本的IoC容器的规范。在这个接口定义中,包
括了getBean()这样的IoC容器的基本方法。而HierarchicalBeanFactory接口在继承了BeanFactory的基本接口
之后,增加了getParentBeanFactory()的接口功能,使BeanFactory具备了双亲IoC容器的管理功能。在接下来
的ConfigurableBeanFactory接口中,主要定义了一些对BeanFactory的配置功能,比如
通过setParentBeanFactory( )设置双亲IoC容器,通过addBeanPostProcessor( )配置Bean后置处理器,
等等。通过这些接口设计的叠加,定义了BeanFactory就是简单IoC容器的基本功能
二)第二条接口设计主线是,以ApplicationContext应用上下文接口为核心的接口设计,这里涉及的主要接口
设计有,从BenaFactory到ListableBeanFactory,再到ApplicationContext,再到我们常用的WebApplicationContext或者ConfigurableApplicationContext接口。
在这个接口体系中,ListableBeanFactory和HierarchicalBeanFactory两个接口,连接BeanFactory接口定义和ApplicationContext应用上下文的接口定义。
在ListableBeanFactory接口中,细化了许多BeanFactory的接口功能,比如定义了getBeanDefinitionNames()接口方法;对于HierarchicalBeanFactory接口,
我们在前文中已经提到过;对于ApplicationContext接口,它通过继承MessageSource、ResourceLoader、
ApplicationEventPublisher接口,在BeanFactory简单IoC容器的基础上添加了许多对高级容器的特性的支持。
1. BeanFactory
以XmlBeanFactory为例
XmlBeanFactory中
1 @Deprecated 2 @SuppressWarnings({"serial", "all"}) 3 public class XmlBeanFactory extends DefaultListableBeanFactory { 4 5 private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); 6 7 8 /** 9 * Create a new XmlBeanFactory with the given resource, 10 * which must be parsable using DOM. 11 * @param resource XML resource to load bean definitions from 12 * @throws BeansException in case of loading or parsing errors 13 */ 14 public XmlBeanFactory(Resource resource) throws BeansException { 15 this(resource, null); 16 } 17 18 /** 19 * Create a new XmlBeanFactory with the given input stream, 20 * which must be parsable using DOM. 21 * @param resource XML resource to load bean definitions from 22 * @param parentBeanFactory parent bean factory 23 * @throws BeansException in case of loading or parsing errors 24 */ 25 public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { 26 super(parentBeanFactory); 27 this.reader.loadBeanDefinitions(resource); 28 } 29 30 }
参考XmlBeanFactory 编程式使用IoC容器
1 ClassPathResource res = new ClassPathResource("bean.xml"); 2 DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); 3 XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); 4 reader.loadBeanDefinitions(res);
这样,我们就可以通过factory对象来使用DefaultListableBeanFactory这个IoC容器了。
在使用IoC容器时,需要如下几个步骤:
1)创建IoC配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息。
2)创建一个BeanFactory,这里使用DefaultListableBeanFactory。
3)创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来载入XML文件形式的
BeanDefinition,通过一个回调配置给BeanFactory。
4)从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成。完成整个载入
和注册Bean定义之后,需要的IoC容器就建立起来了。这个时候就可以直接使用IoC容器了。
2. ApplicationContext
在ApplicationContext容器中,以常用的FileSystemXmlApplicationContext的实现为例
来说明ApplicationContext容器的设计原理。
在FileSystemXmlApplicationContext的设计中,我们看到ApplicationContext应用上下文的主要功能是在
FileSystemXmlApplicationContext的基类AbstractXmlApplicationContext中实现了,
在FileSystemXmlApplicationContext中,作为一个具体的应用上下文,只需要实现和它自身设计相关的
两个功能。
一个功能是,如果应用直接使用FileSystemXmlApplicationContext,对于实例化这个应用上下文的
支持,同时启动IoC容器的refresh( )过程。
1 /** 2 * Create a new FileSystemXmlApplicationContext with the given parent, 3 * loading the definitions from the given XML files. 4 * @param configLocations array of file paths 5 * @param refresh whether to automatically refresh the context, 6 * loading all bean definitions and creating all singletons. 7 * Alternatively, call refresh manually after further configuring the context. 8 * @param parent the parent context 9 * @throws BeansException if context creation failed 10 * @see #refresh() 11 */ 12 public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) 13 throws BeansException { 14 15 super(parent); 16 setConfigLocations(configLocations); 17 if (refresh) { 18 refresh(); 19 } 20 }
另一个功能是与FileSystemXmlApplicationContext设计具体相关的功能,这部分与怎样从文件系统
中加载XML的Bean定义资源有关。
通过这个过程,可以为在文件系统中读取以XML形式存在的BeanDefinition做准备,因为不同的
应用上下文实现对应着不同的读取BeanDefinition的方式,在FileSystemXmlApplicationContext中的
实现代码如下:
1 /** 2 * Resolve resource paths as file system paths. 3 * <p>Note: Even if a given path starts with a slash, it will get 4 * interpreted as relative to the current VM working directory. 5 * This is consistent with the semantics in a Servlet container. 6 * @param path path to the resource 7 * @return Resource handle 8 * @see org.springframework.web.context.support.XmlWebApplicationContext#getResourceByPath 9 */ 10 @Override 11 protected Resource getResourceByPath(String path) { 12 if (path != null && path.startsWith("/")) { 13 path = path.substring(1); 14 } 15 return new FileSystemResource(path); 16 }
可以看到,调用这个方法,可以得到FileSystemResource的资源定位。
IoC容器的初始化过程
简单来说,IoC容器的初始化是由前面介绍的refresh( )方法来启动的,具体来说,这个启动包括
BeanDefinition的Resource定位、载入和注册三个基本过程。
Spring把这三个过程分开,并使用不同的模块来完成,如使用相应的ResourceLoader、
BeanDefinitionReader、BeanDefinitionRegistry等模块,通过这样的设计方式,可以让用户更加灵活
的对这三个过程进行剪裁或扩展,定义出最合适自己的IoC容器的初始化过程。
第一个过程是Resource定位过程。这个Resource定位指的是BeanDefinition的资源定位,它由
ResourceLoader通过统一的Resource接口来完成,这个Resource对这种形式的BeanDefinition的使用
都提供了统一接口。比如,在文件系统中的Bean定义信息可以使用FileSystemResource来进行抽象;
在类路径中的Bean定义信息可以使用前面提到的ClassPathResource来使用。
第二个过程是BeanDefinition的载入。这个过程是把用户定义好的Bean表示成IoC容器内部的数据结构,
而这个容器内部的数据结构就是BeanDefinition。
第三个过程是向IoC容器注册这些BeanDefinition的过程。是通过调用BeanDefinitionRegistry接口的
实现来完成的。这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。通过分析,可以
看到,在IoC容器内部将BeanDefinition注入到一个HashMap中,通过HashMap来管理这些BeanDefinition
数据的。
IoC容器的依赖注入
当前IoC容器已经载入了用户定义的Bean信息,开始分析依赖注入的原理。首先,注意到依赖注入的过程是
用户第一次向IoC容器索要Bean时触发的,当然也有例外,也就是我们可以在BeanDefinition信息中通过控制
lazy-init属性来让容器完成对Bean的预实例化。下面从DefaultListableBeanFactory的基类AastractBeanFactory
入手去看看getBean的实现
1 public Object getBean(String name) throws BeansException { 2 return doGetBean(name, null, null, false); 3 } 4 5 /** 6 * Return an instance, which may be shared or independent, of the specified bean. 7 * @param name the name of the bean to retrieve 8 * @param requiredType the required type of the bean to retrieve 9 * @param args arguments to use if creating a prototype using explicit arguments to a 10 * static factory method. It is invalid to use a non-null args value in any other case. 11 * @param typeCheckOnly whether the instance is obtained for a type check, 12 * not for actual use 13 * @return an instance of the bean 14 * @throws BeansException if the bean could not be created 15 */ 16 @SuppressWarnings("unchecked") 17 protected <T> T doGetBean( 18 final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) 19 throws BeansException { 20 21 final String beanName = transformedBeanName(name); 22 Object bean; 23 24 // Eagerly check singleton cache for manually registered singletons. 25 Object sharedInstance = getSingleton(beanName); 26 if (sharedInstance != null && args == null) { 27 if (logger.isDebugEnabled()) { 28 if (isSingletonCurrentlyInCreation(beanName)) { 29 logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + 30 "' that is not fully initialized yet - a consequence of a circular reference"); 31 } 32 else { 33 logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); 34 } 35 } 36 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); 37 } 38 39 else { 40 // Fail if we're already creating this bean instance: 41 // We're assumably within a circular reference. 42 if (isPrototypeCurrentlyInCreation(beanName)) { 43 throw new BeanCurrentlyInCreationException(beanName); 44 } 45 46 // Check if bean definition exists in this factory. 47 BeanFactory parentBeanFactory = getParentBeanFactory(); 48 if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { 49 // Not found -> check parent. 50 String nameToLookup = originalBeanName(name); 51 if (args != null) { 52 // Delegation to parent with explicit args. 53 return (T) parentBeanFactory.getBean(nameToLookup, args); 54 } 55 else { 56 // No args -> delegate to standard getBean method. 57 return parentBeanFactory.getBean(nameToLookup, requiredType); 58 } 59 } 60 61 if (!typeCheckOnly) { 62 markBeanAsCreated(beanName); 63 } 64 65 try { 66 final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); 67 checkMergedBeanDefinition(mbd, beanName, args); 68 69 // Guarantee initialization of beans that the current bean depends on. 70 String[] dependsOn = mbd.getDependsOn(); 71 if (dependsOn != null) { 72 for (String dependsOnBean : dependsOn) { 73 getBean(dependsOnBean); 74 registerDependentBean(dependsOnBean, beanName); 75 } 76 } 77 78 // Create bean instance. 79 if (mbd.isSingleton()) { 80 sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { 81 public Object getObject() throws BeansException { 82 try { 83 return createBean(beanName, mbd, args); 84 } 85 catch (BeansException ex) { 86 // Explicitly remove instance from singleton cache: It might have been put there 87 // eagerly by the creation process, to allow for circular reference resolution. 88 // Also remove any beans that received a temporary reference to the bean. 89 destroySingleton(beanName); 90 throw ex; 91 } 92 } 93 }); 94 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); 95 } 96 97 else if (mbd.isPrototype()) { 98 // It's a prototype -> create a new instance. 99 Object prototypeInstance = null; 100 try { 101 beforePrototypeCreation(beanName); 102 prototypeInstance = createBean(beanName, mbd, args); 103 } 104 finally { 105 afterPrototypeCreation(beanName); 106 } 107 bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); 108 } 109 110 else { 111 String scopeName = mbd.getScope(); 112 final Scope scope = this.scopes.get(scopeName); 113 if (scope == null) { 114 throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'"); 115 } 116 try { 117 Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() { 118 public Object getObject() throws BeansException { 119 beforePrototypeCreation(beanName); 120 try { 121 return createBean(beanName, mbd, args); 122 } 123 finally { 124 afterPrototypeCreation(beanName); 125 } 126 } 127 }); 128 bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); 129 } 130 catch (IllegalStateException ex) { 131 throw new BeanCreationException(beanName, 132 "Scope '" + scopeName + "' is not active for the current thread; " + 133 "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", 134 ex); 135 } 136 } 137 } 138 catch (BeansException ex) { 139 cleanupAfterBeanCreationFailure(beanName); 140 throw ex; 141 } 142 } 143 144 // Check if required type matches the type of the actual bean instance. 145 if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) { 146 try { 147 return getTypeConverter().convertIfNecessary(bean, requiredType); 148 } 149 catch (TypeMismatchException ex) { 150 if (logger.isDebugEnabled()) { 151 logger.debug("Failed to convert bean '" + name + "' to required type [" + 152 ClassUtils.getQualifiedName(requiredType) + "]", ex); 153 } 154 throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); 155 } 156 } 157 return (T) bean; 158 }
AbstractAutowireCapableBeanFactory中的createBean
1 /** 2 * Central method of this class: creates a bean instance, 3 * populates the bean instance, applies post-processors, etc. 4 * @see #doCreateBean 5 */ 6 @Override 7 protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { 8 if (logger.isDebugEnabled()) { 9 logger.debug("Creating instance of bean '" + beanName + "'"); 10 } 11 // Make sure bean class is actually resolved at this point. 12 resolveBeanClass(mbd, beanName); 13 14 // Prepare method overrides. 15 try { 16 mbd.prepareMethodOverrides(); 17 } 18 catch (BeanDefinitionValidationException ex) { 19 throw new BeanDefinitionStoreException(mbd.getResourceDescription(), 20 beanName, "Validation of method overrides failed", ex); 21 } 22 23 try { 24 // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. 25 Object bean = resolveBeforeInstantiation(beanName, mbd); 26 if (bean != null) { 27 return bean; 28 } 29 } 30 catch (Throwable ex) { 31 throw new BeanCreationException(mbd.getResourceDescription(), beanName, 32 "BeanPostProcessor before instantiation of bean failed", ex); 33 } 34 35 Object beanInstance = doCreateBean(beanName, mbd, args); 36 if (logger.isDebugEnabled()) { 37 logger.debug("Finished creating instance of bean '" + beanName + "'"); 38 } 39 return beanInstance; 40 }
/** * Actually create the specified bean. Pre-creation processing has already happened * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks. * <p>Differentiates between default bean instantiation, use of a * factory method, and autowiring a constructor. * @param beanName the name of the bean * @param mbd the merged bean definition for the bean * @param args arguments to use if creating a prototype using explicit arguments to a * static factory method. This parameter must be {@code null} except in this case. * @return a new instance of the bean * @throws BeanCreationException if the bean could not be created * @see #instantiateBean * @see #instantiateUsingFactoryMethod * @see #autowireConstructor */ protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); mbd.postProcessed = true; } } // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, new ObjectFactory<Object>() { public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); } // Initialize the bean instance. Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { exposedObject = initializeBean(beanName, exposedObject, mbd); } } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } } // Register bean as disposable. try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
在这里我们看到,与依赖注入关键的方法有createBeanInstance和populateBean,
在createBeanInstance中生成了Bean所包含的Java对象