Spring ioc容器的实现
1. 说明
本文将通过源码分析spring ioc容器的实现。
参考书籍《Spring 技术内幕 第二版》
2. 开始
-
依赖翻转
首先要明白Spring容器的思想依赖翻转,什么是依赖翻转呢?在面向对象编程中经常会出现一个对象内部引用了另一个对象,这种关系就是对象的依赖关系,通过这种依赖关系,对象之间就可以合作完成各种不同的功能。但是他也有缺陷,如果不同的类相互引用势必会造成代码高度耦合,如何解决这个问题呢?下面是我自己个人的理解。
通常我们经常会写如下代码
class A{ B b=new B(); }
这是很典型的一种引用关系,但是假如有一天,B这个类进行了修改,我们使用了一个B1去继承B,然后重写了方法,这个时候我们要对代码进行修改。
class A{ B b=new B1(); }
但是这种做法并不好,原因有两个,我们需要将所有出现
B b=new B()
的地方进行更改,显然这样做的成本很高,另外根据面向对象的基本原则中的开闭原则,应该对扩展开放面对修改闭合,也就是说我们可以新增代码,但是不要去修改代码。可是这里不修改似乎不行。那我们换一种思路,下面是伪代码。
class A{ B b;//这里有一个b成员,但是没有初始化,请根据配置文件通过setB方法为b赋予值 void setB(B b){ this.b=b; } }
我们站在A类的角度,以前的代码是由A去维护B的引用,而现在确不是了,A类只提供了一个引用,但是这个引用的实际类型是什么并不知道,它需要我们在实例化的时候去设置。
那么当我们需要用到B1的时候,我们岂不是只要更改一下配置文件就可以完成代码的更新,这样既符合开闭原则,而且A类不与具体的对象相关联,而是与接口相关,这对我们维护代码提供了遍历。
总结一下前面的分析,我们以前的做法是类主动维护依赖关系,而现在却把维护这种关系的责任交给了类的外部,这种思想便是依赖翻转。
-
Spring IoC
任何理念思想都应该有产品对象,Spring的核心IoC容器就是这样一个产品,Spring框架中所有的对象都交给IoC容器管理,包括对象之间的依赖关系,如下面代码所示。
class A{
@AutoWired
B b;
}
通过注解就可以将B的实例注入进对象中,从而完成了对象的依赖关系管理。
那么对于一个IoC容器而言,需要实现什么样的功能呢?
最重要的两个功能应该是
-
如何获取被管理的对象
-
如何设置依赖关系
-
BeanFactory和BeanDefinition
解释一下两个最重要的类。
在Spring中,我们通常把由Spring容器管理的对象称为Bean。BeanFactory是Spring中最基本和最核心的接口,顾名思义可以知道这个接口用来管理和创建对象,因此这个接口定义了IoC容器的基本功能,也就是说BeanFactory实际上代表IoC容器,凡是实现了BeanFactory接口的实现类都代表IoC容器,如我们经常通过下面代码获取容器
ApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
上面的ApplicationContext就是我们所说的容器,而他也是BeanFactory的实现类类。
再看看BeanFactory的源码
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String name) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);
}
BeanFactory最多的方法就是getBean,这符合一个容器的特征。什么是容器呢?现实生活中,容器就是用来装东西的,IoC容器就是用来装bean的,既然里面装着bean,那么我们自然可以把它取出来。
BeanDefinition是什么呢?
IoC容器需要管理对象,其实有两种方法,第一种实现思路是直接管理我们写好的类,如果要获取对象从类中new一个出来就可以了,显然这种方式不太灵活,并且每次都要去判断该对象依赖的对象是什么。另一种实现思路是容器本身管理一种类型,所有用户的类都要转换成容器规定的这种类型,这样的话IoC容器具有什么样的功能取决于容器管理的类型而不是用户写的类。Spring IoC管理的就是容器实现的数据类型,这种类型就是BeanDefinition。
我们通常通过下面的xml文件管理bean。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="testService" class="TestService" scope="singleton"></bean>
</beans>
对于Spring Ioc来说,会把配置文件中的所有bean标签转化为一个BeanDefinition,然后添加到Spring IoC容器中管理,也就是BeanFactory的实现类。
看一下BeanDefinition的源码
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
void setParentName(@Nullable String parentName);
@Nullable
String getParentName();
void setBeanClassName(@Nullable String beanClassName);
@Nullable
String getBeanClassName();
void setScope(@Nullable String scope);
@Nullable
String getScope();
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
void setDependsOn(@Nullable String... dependsOn);
@Nullable
String[] getDependsOn();
void setAutowireCandidate(boolean autowireCandidate);
boolean isAutowireCandidate();
void setPrimary(boolean primary);
boolean isPrimary();
void setFactoryBeanName(@Nullable String factoryBeanName);
@Nullable
String getFactoryBeanName();
void setFactoryMethodName(@Nullable String factoryMethodName);
@Nullable
String getFactoryMethodName();
ConstructorArgumentValues getConstructorArgumentValues();
default boolean hasConstructorArgumentValues() {
return !getConstructorArgumentValues().isEmpty();
}
MutablePropertyValues getPropertyValues();
default boolean hasPropertyValues() {
return !getPropertyValues().isEmpty();
}
void setInitMethodName(@Nullable String initMethodName);
@Nullable
String getInitMethodName();
void setDestroyMethodName(@Nullable String destroyMethodName);
@Nullable
String getDestroyMethodName();
void setRole(int role);
int getRole();
void setDescription(@Nullable String description);
@Nullable
String getDescription();
boolean isSingleton();
boolean isPrototype();
boolean isAbstract();
@Nullable
String getResourceDescription();
@Nullable
org.springframework.beans.factory.config.BeanDefinition getOriginatingBeanDefinition();
}
可以看到BeanDefinition有很多属性去描述一个bean,比如这个bean属于哪个类,init方法和destroy方法是什么,是否是懒加载,依赖的对象有哪些。
目前我们分析了是谁管理对象,管理的元素是什么,下一个问题是ben的信息放在哪里,从我们使用的角度来看,我们通常可以通过下面的代码获取一个bean
TestService testService1=context.getBean(TestService.class);
TestService testService2= (TestService) context.getBean("textService");
通过我们的编程经验,如果要求查找的效率最高,内部应该维护的是HashMap。我们跟踪一下代码找到这个数据结构出来。
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
上面的代码是由我们通过跟踪getBean方法发现的,我们的目标是一个单例实例,最终发现其保存在一个ConcurrentHashMap中,由此可见我们的猜想是对的,之所以使用ConcurrentHashMap是为了线程安全问题而考虑的。
目前我们知道了bean是如何描述的,bean是由哪个类管理的,存放bean的具体数据结构是什么。但是距离实现一个IoC容器还面临着不少问题,如:
- 我们定义的bean是转换成BeanDefinition的。
- IoC容器什么时候将我们的bean的配置文件转化为map这种数据结构的。
- 如何通过一个BeanDefinition获取到我们需要的bean对象
我们至少要解决上面几个问题才能构建出自己的IoC容器处理,下面我们继续分析。
-
refresh方法
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 为refresh过程做准备 prepareRefresh(); //告诉子类刷新内部bean工厂,完成该步骤,bean就会进入到容器内部 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //准备bean工厂在此上下文中使用做准备 prepareBeanFactory(beanFactory); try { // 允许在上下文子类中对bean工厂进行后处理。 postProcessBeanFactory(beanFactory); // 调用作为bean注册进上下文的处理器 invokeBeanFactoryPostProcessors(beanFactory); // 注册拦截bean创建的bean处理器。 registerBeanPostProcessors(beanFactory); // 初始化此上下文的消息源 initMessageSource(); // 初始化此上下文的事件多播器。 initApplicationEventMulticaster(); // 在特定的上下文子类中初始化其他特殊bean。 onRefresh(); // 检查监听器bean并注册它们。 registerListeners(); // 实例化所有剩余(非延迟初始化)单例。 finishBeanFactoryInitialization(beanFactory); // 最后一步:发布相应的事件。 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // 摧毁已经创建的单例以避免悬空资源。 destroyBeans(); // 重置 'active' 标记. cancelRefresh(ex); //向调用者传播异常 throw ex; } finally { // 重置Spring核心中的常见内省缓存, 因为我们不再需要单例bean的元数据 resetCommonCaches(); } } }
上面refresh方法的注释是根据源码注释翻译过来的,可以看出refresh方法与容器初始化密切相关。
IoC容器的初始化的关键在于BeanFactory的获取,通过对代码
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
的跟踪发现如下方法:@Override protected final void refreshBeanFactory() throws BeansException { //如果存在BeanFactory就销毁 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //创建BeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); //加载BeanDefinition loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
这个方法做了两件事,创建BeanFactory,加载BeanDefinition。
容器的初始化首先应该创建容器,我们继续跟踪BeanFactory的创建。
protected DefaultListableBeanFactory createBeanFactory() { return new DefaultListableBeanFactory(getInternalParentBeanFactory()); }
可以发现是直接返回来一个
DefaultListableBeanFactory
;此时获得的BeanFactory是空的,里面什么信息都没有,需要loadBeanDefinitions
加载。@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { //为传入的BeanFactory参数创建一个XmlBeanDefinitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 使用此上下文的资源加载环境配置bean定义读取器。 beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); //允许子类提供reader的自定义初始化,然后继续实际加载bean定义。 initBeanDefinitionReader(beanDefinitionReader); //加载BeanDefinition loadBeanDefinitions(beanDefinitionReader); }
从该方法可以看出,BeanDefinition的加载与BeanDefinitionReader关系非常大。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { //获取resource Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } //获取配置文件的位置,这一步实际上拿到了我们spring.xml的路径信息 String[] configLocations = getConfigLocations(); if (configLocations != null) { //通过reader的loadBeanDefinitions方法,参数为配置信息的位置加载beanDefinition reader.loadBeanDefinitions(configLocations); } }
到了这一步,IoC容器的初始化过程已经非常明显了,现在留给我们的问题只有两个,配置文件是如何获取的,如何加载的,这些问题与
getConfigLocations
方法和reader
的loadBeanDefinitions
方法有关。 -
资源定位
bean的配置信息我们一般写在配置文件中,容器管理bean的自然要找到配置信息。通过下面这行代码的分析
ApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
配置文件是写在构造方法参数中的,并且
ClassPathXmlApplicationContext
意味着从ClassPath加载该文件。public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); //设置配置文件的位置 setConfigLocations(configLocations); if (refresh) { refresh(); } }
public void setConfigLocations(@Nullable String... locations) { if (locations != null) { Assert.noNullElements(locations, "Config locations must not be null"); this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; } }
通过构造方法,location信息已经保存在
configLocations
了,然后回到上一节的末尾方法;protected String[] getConfigLocations() { return (this.configLocations != null ? this.configLocations :getDefaultConfigLocations()); }
可以看到是直接返回的,如果我们没有传入配置文件路径,那么他会返回一个默认的位置(这个默认的位置其实是null);
现在已经拿到了配置文件的路径,但是本节开始说过一个问题
ClassPathXmlApplicationContext
就是从ClassPath下加载该文件,这里我们只有文件路径信息,但是IoC容器如何直到从哪里加载该信息呢?继续跟踪代码
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { //获取一个ResourceLoader ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { //通过ResourceLoader的getResources方法,以配置文件位置信息获取resource Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); //以resource为参数加载BeanDefinition int count = loadBeanDefinitions(resources); if (actualResources != null) { Collections.addAll(actualResources, resources); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]"); } return count; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // Can only load single resources by absolute URL. Resource resource = resourceLoader.getResource(location); int count = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location [" + location + "]"); } return count; } }
Resource对象就是配置文件的封装,获取到了resource就相当于准确定位到了配置文件。
-
载入和解析
获取到了Resource对象就可以完成BeanDefinition的载入和解析了,这个过程将配置文件的信息加载进入IoC容器,然后通过这些信息获取到具体的BeanDefinition实例。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); } Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { //获取了配置文件的输入流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //-》进入该方法 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
进入
doLoadBeanDefinitions(inputSource, encodedResource.getResource())
语句protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //将配置文件解析成Document对象 Document doc = doLoadDocument(inputSource, resource); //-> 进入该方法 int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }
进入
int count = registerBeanDefinitions(doc, resource);
语句public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //获取一个BeanDefinitionDocumentReader对象 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); //调用documentReader的方法 -> 进入该方法 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
继续
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; //-> doRegisterBeanDefinitions(doc.getDocumentElement()); }
继续
doRegisterBeanDefinitions(doc.getDocumentElement());
protected void doRegisterBeanDefinitions(Element root) { //BeanDefinitionParserDelegate真正完成解析工作 BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } preProcessXml(root); //解析BeanDefinition -> parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
继续
parseBeanDefinitions(root, this.delegate);
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //不断地获取子元素 if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { //-> parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
继续
parseDefaultElement(ele, delegate);
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { //如果ele元素是一个Bean的话,调用下面的方法 processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
继续
processBeanDefinition(ele, delegate);
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //通过delegate的parseBeanDefinitionElement方法获取BeanDefinitionHolder实例 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
继续
delegate.parseBeanDefinitionElement(ele);
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { //获取bean的id String id = ele.getAttribute(ID_ATTRIBUTE); //获取bean的名称 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); if (logger.isTraceEnabled()) { logger.trace("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { beanName = this.readerContext.generateBeanName(beanDefinition); // Register an alias for the plain bean class name, if still possible, // if the generator returned the class name plus a suffix. // This is expected for Spring 1.2/2.0 backwards compatibility. String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isTraceEnabled()) { logger.trace("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils.toStringArray(aliases); //将BeanDefinition封装成一个BeanDefinitionHolder对象返回 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; }
上面的方法结束,就获取到了一个BeanDefinition
根据返回语句
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
可知到这一步配置文件中的bean已经转化为一个BeanDefinition了 -
注册
BeanDefinition已经获取到了,现在要做的试将BeanDefinition保存在IoC容器中,这一步就发送在BeanDefinitionHolder获取之后。
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // 使用初始的名称注册 String beanName = definitionHolder.getBeanName(); //-> 进入方法 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 注册别名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
继续
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
,这个方法将beanName和BeanDefinition作为参数传入,可以预料到最终的注册就发送在这个方法中。public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { //断言校验beanName和beanDefinition是否有问题 Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); //判断BeanDefinition是不是AbstractBeanDefinition类型的 if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } //判断已经存在同名的BeanDefinition BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) { if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } else if (existingDefinition.getRole() < beanDefinition.getRole()) { if (logger.isInfoEnabled()) { logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(existingDefinition)) { if (logger.isDebugEnabled()) { logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else { if (logger.isTraceEnabled()) { logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } this.beanDefinitionMap.put(beanName, beanDefinition); } else { if (hasBeanCreationStarted()) { synchronized (this.beanDefinitionMap) { //将beanName作为key,BeanDefinition作为value保存在HashMap中 this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; removeManualSingletonName(beanName); } } else { // Still in startup registration phase this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); removeManualSingletonName(beanName); } this.frozenBeanDefinitionNames = null; } if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }
this.beanDefinitionMap.put(beanName, beanDefinition);
将BeanDefinition保存到了文章最开始猜想的HashMap中,此时BeanDefinition已经保存到了IoC容器中。 -
依赖注入
依赖注入发生在第一次获取Bean的时候,注意完成了上一步,IoC容器中存放的并不是bean对象而是BeanDefinition,从BeanDefinition信息获取bean对象就是依赖注入的过程。
通过beanFactory的getBean方法获取,这里我们以beanName作为参数。
public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); }
进入doGetBean方法
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; // 单例对象会放到缓存中,所以先去缓存中获取bean Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { if (logger.isTraceEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); } } //从factoryBean对象中获取bean bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // Fail if we're already creating this bean instance: // We're assumably within a circular reference. if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // 检查对应的BeanDefinition是否存在 BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // 如果当前BeanFactory中不能存在,并且有父BeanFactory则检查父BeanFactory 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); } } if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // 保证当前bean依赖的bean的初始化. String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } // 创建bean实例 //如果当前bean是一个单例 if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { // 如果是prototype类型 -> 创建一个新的实例. 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); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { Object scopedInstance = scope.get(beanName, () -> { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } // Check if required type matches the type of the actual bean instance. if (requiredType != null && !requiredType.isInstance(bean)) { try { T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); if (convertedBean == null) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } return convertedBean; } catch (TypeMismatchException ex) { if (logger.isTraceEnabled()) { logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean; }
3. 总结
spring IoC容器是spring框架的核心,spring IoC管理着spring框架的bean,是spring mvc等其他框架的基石,只有充分理解IoC的原理,才能对框架本身理解的更加透彻。