Spring IOC源代码具体解释之容器初始化
Spring IOC源代码具体解释之容器初始化
ClassPathResource res = new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
- 创建IOC配置文件的抽象资源
- 创建一个BeanFactory
- 把读取配置信息的BeanDefinitionReader,这里是XmlBeanDefinitionReader配置给BeanFactory
- 从定义好的资源位置读入配置信息,详细的解析过程由XmlBeanDefinitionReader来完毕,这样完毕整个加载bean定义的过程。
首先看资源的定位,一个常见的ApplicationContext
ApplicationContext =new FileSystemXmlApplicationContext(xmlPath);
追踪FileSystemXmlApplicationContext。里面有一些构造函数,都指向以下这个
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent); //有前面类图 可 以知道,终于 父类 为AbstractApplicationContext
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
//追踪super,终于定位到 AbstractApplicationContext中
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
public void setConfigLocations(String... locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
// 该方法调用 SystemPropertyUtils.resolvePlaceholders(path) ;对 path 中的占位 符进行
// 替换, eg : path 路径中含有 ${user.dir} ,则将替换为: System.getProperty(user.dir);
for (int i = 0; i < locations.length; i++) {
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
代码中 configLocations 为 AbstractRefreshableConfigApplicationContext 类中 string[] 类型的字段。至此应用程序传入路径保存在 AbstractRefreshableConfigApplicationContext 中 。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//调用容器准备刷新的方法,获取 容器的当时时间,同一时候给容器设置同步标识
prepareRefresh();
//告诉子类启动refreshBeanFactory()方法。Bean定义资源文件的载入从
//子类的refreshBeanFactory()方法启动
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//为BeanFactory配置容器特性,比如类载入器、事件处理器等
prepareBeanFactory(beanFactory);
try {
//为容器的某些子类指定特殊的BeanPost事件处理器
postProcessBeanFactory(beanFactory);
//调用全部注冊的BeanFactoryPostProcessor的Bean
invokeBeanFactoryPostProcessors(beanFactory);
//为BeanFactory注冊BeanPost事件处理器.
//BeanPostProcessor是Bean后置处理器。用于监听容器触发的事件
registerBeanPostProcessors(beanFactory);
//初始化信息源,和国际化相关.
initMessageSource();
//初始化容器事件传播器.
initApplicationEventMulticaster();
//调用子类的某些特殊Bean初始化方法
onRefresh();
//为事件传播器注冊事件监听器.
registerListeners();
//初始化全部剩余的单态Bean.
finishBeanFactoryInitialization(beanFactory);
//初始化容器的生命周期事件处理器,并公布容器的生命周期事件
finishRefresh();
}
catch (BeansException ex) {
//销毁以创建的单态Bean
destroyBeans();
//取消refresh操作。重置容器的同步标识.
cancelRefresh(ex);
throw ex;
}
}
}
prepareRefresh(): 为刷新准备上下文环境
- obtainFreshBeanFactory() :让子类刷新内部 bean 工厂。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {//假设已经有容器。销 毁 容器中的bean,关闭容器
destroyBeans();
closeBeanFactory();
}
try {
//创建IoC容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
//对IoC容器进行定制化,如设置启动參数,开启注解的自己主动装配等
customizeBeanFactory(beanFactory);
//调用加载Bean定义的方法,主要这里使用了一个委派模式。在当前类中仅仅定义了抽象的loadBeanDefinitions方法,详细的实现调用子类容器
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是否存在,假设存在则先销毁beans并关闭beanFactory,接着创建DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)装载bean
相同。AbstractRefreshableApplicationContext中仅仅定义了抽象的loadBeanDefinitions方法,容器真正调用的是其子类AbstractXmlApplicationContext对该方法的实现,AbstractXmlApplicationContext的主要源代码例如以下:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
// 这里使用XMLBeanDefinitionReader来加载bean定义信息的XML文件
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//这里配置reader的环境,当中ResourceLoader是我们用来定位bean定义信息资源位置的
///由于上下文本身实现了ResourceLoader接口,所以能够直接把上下文作为ResourceLoader传递给XmlBeanDefinitionReader
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
initBeanDefinitionReader(beanDefinitionReader);
//这里转到定义好的XmlBeanDefinitionReader中对加载bean信息进行处理
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
//调用XmlBeanDefinitionReader来加载bean定义信息。
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
Xml Bean读取器(XmlBeanDefinitionReader)调用其父类AbstractBeanDefinitionReader的 reader.loadBeanDefinitions方法读取Bean定义资源。
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
//这里得到当前定义的ResourceLoader,默认的 我 们 使用DefaultResourceLoader
ResourceLoader resourceLoader = getResourceLoader();
.........//假设没有找到我们须要的ResourceLoader,直接抛出异常
if (resourceLoader instanceof ResourcePatternResolver) {
// 这里处理我们在定义位置时使用的各种pattern,须要ResourcePatternResolver来完毕
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
return loadCount;
}
........
}
else {
// 这里通过ResourceLoader来完毕位置定位
Resource resource = resourceLoader.getResource(location);
// 这里已经把一个位置定义转化为Resource接口,能够供XmlBeanDefinitionReader来使用了
int loadCount = loadBeanDefinitions(resource);
return loadCount;
}
}
重载方法
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int counter = 0;
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
return counter;
}
从对AbstractBeanDefinitionReader的loadBeanDefinitions方法源代码分析能够看出该方法做了下面两件事:
首先,调用资源载入器的获取资源方法resourceLoader.getResource(location)。获取到要载入的资源。
其次,真正运行载入功能是其子类XmlBeanDefinitionReader的loadBeanDefinitions方法。
2、资源载入
//获取Resource的具 体实现方法
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
//假设是类路径的方式。那须要使用ClassPathResource 来得到bean 文件的资源对象
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
try {
// 假设是URL 方式,使用UrlResource 作为bean 文件的资源对象
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
}
//假设既不是classpath标识。又不是URL标识的Resource定位,则调用
//容器本身的getResourceByPath方法获取Resource
return getResourceByPath(location);
}
@Override
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
Bean资源载入
//XmlBeanDefinitionReader加载资源 的入口方法
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//将读入的XML资源进行特殊编码处理
return loadBeanDefinitions(new EncodedResource(resource));
}
//这里是加载XML形式Bean定义资源文件方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
.......
try {
//将资源文件转为InputStream的IO流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//从InputStream中得到XML的解析源
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//这里是具体的读取过程
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
//关闭从Resource中得到的IO流
inputStream.close();
}
}
.........
26}
//从特定XML文件里实际加载Bean定义资源的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
//将XML文件转换为DOM对象,解析过程由documentLoader实现
Document doc = this.documentLoader.loadDocument(
inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware);
//这里是启动对Bean定义解析的具体过程,该解析过程会用到Spring的Bean配置规则
return registerBeanDefinitions(doc, resource);
}
.......
}
3、资源解析
以下这句是XmlBeanDefinitionReader中关于DocumentLoader。用了DefaultDocumentLoader
private DocumentLoader documentLoader = new DefaultDocumentLoader();
//使用标准的JAXP 将 加载的B ean 定义 资源 转换成document对象
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
//创建文件解析器工厂
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
//创建文档解析器
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
//解析Spring的Bean定义资源
return builder.parse(inputSource);
}
protected DocumentBuilder createDocumentBuilder(
DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler)
throws ParserConfigurationException {
DocumentBuilder docBuilder = factory.newDocumentBuilder();
if (entityResolver != null) {
docBuilder.setEntityResolver(entityResolver);
}
if (errorHandler != null) {
docBuilder.setErrorHandler(errorHandler);
}
return docBuilder;
}
4、bean生成(依据DOM资源解析成bean)
Document doc = this.documentLoader.loadDocument()后面。有一句return registerBeanDefinitions(doc, resource);
//依照Spring的Bean语义要求 将Bean 定 义 资源解析并转换为容器内部数据结构
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//得到BeanDefinitionDocumentReader来对xml格式的BeanDefinition解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//获得容器中注冊的Bean数量
int countBefore = getRegistry().getBeanDefinitionCount();
//解析过程入口。这里使用了委派模式,BeanDefinitionDocumentReader仅仅是个接口,//详细的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完毕
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//统计解析的Bean数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
//创建BeanDefinitionDocumentReader对象。 解 析 D ocument对象
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
}
首先。通过调用XML解析器将Bean定义资源文件转换得到Document对象,可是这些Document对象并没有依照Spring的Bean规则进行解析。这一步是加载的过程
其次,在完毕通用的XML解析之后,依照Spring的Bean规则对Document对象进行解析。
继续
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
这里的documentReader为BeanDefinitionDocumentReader对象,BeanDefinitionDocumentReader接口通过registerBeanDefinitions方法调用事实上现类DefaultBeanDefinitionDocumentReader对Document对象进行解析
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;//获得XML 描 述 符
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();//获得Document的根元素
doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
//BeanDefinitionParserDelegate中定义了Spring Bean 定 义 XML 文件的各种元素
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)) {
return;
}
}
}
//在解析Bean定义之前,进行自己定义的解析,增强解析过程的可扩展性
preProcessXml(root);
//从Document的根元素開始进行Bean定义的Document对象,见以下解析
parseBeanDefinitions(root, this.delegate);
//在解析Bean定义之后,进行自己定义的解析,添加解析过程的可扩展性
postProcessXml(root);
this.delegate = parent;
}
//上面的createDelegate()方法, 创建BeanDefinitionParserDelegate,用于完毕真正的解析过程
protected BeanDefinitionParserDelegate createDelegate(
XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
//BeanDefinitionParserDelegate初始化Document根元素
delegate.initDefaults(root, parentDelegate);
return delegate;
}
//上面的parseBeanDefinitions
// 使 用Spring的Bean规则从Document的根元素開始进行Bean定义的Document对象
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//Bean定义的Document对象使用了Spring默认的XML命名空间
if (delegate.isDefaultNamespace(root)) {
//获取Bean定义的Document对象根元素的全部子节点
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
//获得Document节点是XML元素节点
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
//假设使用的是默认命名控件,使用Spring的Bean规则解析元素节点
parseDefaultElement(ele, delegate);
}
else {
//没有使用Spring默认的XML命名空间。则使用用户自己定义的解//析规则解析元素节点
delegate.parseCustomElement(ele);
}
}
}
}
else {
//使用默认解析规则解析Document根节点
delegate.parseCustomElement(root);
}
}
//使用默 认 规 则 是个什么样子呢
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//元素节点是IMPORT导入
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//元素节点是Alias相关
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//元素节点是Bean元素
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//托付给BeanDefinitionParserDelegate来完毕对bean元素的 处 理 。这个类包括了详细的bean解析的过程。
// 把解析bean文件得到的信息放到BeanDefinition里,他是bean信息的主要载体。也是IOC容器的管理对象。
//BeanDefinitionHolder是对BeanDefinition的封装。即Bean定义的封装类
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {//假设存在bean定义的话
//对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//向Spring IoC容器注冊解析得到的Bean定义。这是Bean定义向IoC容器注冊的入口 。实际上是放到一个Map里面
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
//在完毕向Spring IoC容器注冊解析得到的Bean定义之后。发送注冊事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
//解析<Bean>元素的入口
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
//解析Bean定义资源文件里的<Bean>元素。这个 方 法中主要处理<Bean>元素的id,name
//和别名属性
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//获取<Bean>元素中的id属性值
String id = ele.getAttribute(ID_ATTRIBUTE);
//获取<Bean>元素中的name属性值
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
////获取<Bean>元素中的alias属性值
List<String> aliases = new ArrayList<String>();
//将<Bean>元素中的全部name属性值存放到别名中
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
//假设<Bean>元素中没有配置id属性时,将别名中的第一个值赋值给beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
//检查<Bean>元素所配置的id或者name的唯一性,containingBean标识<Bean>
//元素中是否包括子<Bean>元素
if (containingBean == null) {
//检查<Bean>元素所配置的id、name或者别名是否反复
checkNameUniqueness(beanName, aliases, ele);
}
//具体对<Bean>元素中配置的Bean定义进行解析的地方
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
//假设<Bean>元素中没有配置id、别名或者name。且没有包括子//<Bean>元素,为解析的Bean生成一个唯一beanName并注冊
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
//假设<Bean>元素中没有配置id、别名或者name,且包括了子//<Bean>元素,为解析的Bean使用别名向IoC容器注冊
beanName = this.readerContext.generateBeanName(beanDefinition);
//为解析的Bean使用别名注冊时,为了向后兼容 //Spring1.2/2.0。给别名加入类名后缀
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("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);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
//当解析出错时,返回null
return null;
}
//具体对<Bean>元素中配置的Bean定义其它属性进行解析,因为上面的方法中已经对//Bean的id、name和别名等属性进行了处理,该方法中主要处理除这三个以外的其它属性数据
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
//记录解析的<Bean>
this.parseState.push(new BeanEntry(beanName));
//这里仅仅读取<Bean>元素中配置的class名字,然后加载到BeanDefinition中去
//仅仅是记录配置的class名字,不做实例化,对象的实例化在依赖注入时完毕
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
//假设<Bean>元素中配置了parent属性。则获取parent属性的值
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//依据<Bean>元素配置的class名称和parent属性值创建BeanDefinition
//为加载Bean定义信息做准备
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//对当前的<Bean>元素中配置的一些属性进行解析和设置。如配置的单态(singleton)属性等
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//为<Bean>元素解析的Bean设置description信息 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//对<Bean>元素的meta(元信息)属性解析
parseMetaElements(ele, bd);
//对<Bean>元素的lookup-method属性解析
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//对<Bean>元素的replaced-method属性解析
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析<Bean>元素的构造方法设置
parseConstructorArgElements(ele, bd);
//解析<Bean>元素的<property>设置
parsePropertyElements(ele, bd);
//解析<Bean>元素的qualifier属性
parseQualifierElements(ele, bd);
//为当前解析的Bean设置所需的资源和依赖对象
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
//解析<Bean>元素出错时。返回null
return null;
}
//解析<property>元素中ref,value或者集合等子元素
public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
//假设<property>没有使用Spring默认的命名空间,则使用用户自己定义的规则解析//内嵌元素
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
//假设子元素是bean,则使用解析<Bean>元素的方法解析
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
//假设子元素是ref。ref中仅仅能有下面3个属性:bean、local、parent
else if (nodeNameEquals(ele, REF_ELEMENT)) {
//获取<property>元素中的bean属性值,引用其它解析的Bean的名称
//能够不再同一个Spring配置文件里。详细请參考Spring对ref的配置规则
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
//获取<property>元素中的local属性值。引用同一个Xml文件里配置
//的Bean的id,local和ref不同,local仅仅能引用同一个配置文件里的Bean
refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
if (!StringUtils.hasLength(refName)) {
//获取<property>元素中parent属性值,引用父级容器中的Bean
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean', 'local' or 'parent' is required for <ref> element", ele);
return null;
}
}
}
//没有配置ref的目标属性值
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
//创建ref类型数据。指向被引用的对象
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
//设置引用类型值是被当前子元素所引用
ref.setSource(extractSource(ele));
return ref;
}
//假设子元素是<idref>。使用解析ref元素的方法解析
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
//假设子元素是<value>,使用解析value元素的方法解析
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
//假设子元素是null。为<property>设置一个封装null值的字符串数据
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
//假设子元素是<array>,使用解析array集合子元素的方法解析
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
//假设子元素是<list>,使用解析list集合子元素的方法解析
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
//假设子元素是<set>,使用解析set集合子元素的方法解析
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
//假设子元素是<map>,使用解析map集合子元素的方法解析
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
//假设子元素是<props>,使用解析props集合子元素的方法解析
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
//既不是ref,又不是value。也不是集合。则子元素配置错误,返回null
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
5、Bean是怎样在IOC容器中注冊的
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
//得到须要 注冊 的 bean名字
String beanName = definitionHolder.getBeanName();
//開始注冊
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 别名也是能够 通过IOC容器和bean联系起来的进行注冊
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
上面的registry对象是BeanDefinitionRegistry,上一步是BeanDefinitionReaderUtils.registerBeanDefinition(),即当调用BeanDefinitionReaderUtils向IoC容器注冊解析的BeanDefinition时。真正完毕注冊功能的是DefaultListableBeanFactory。
那么DefaultListableBeanFactory怎么会和BeanDefinitionReaderUtils扯到一起呢?
事实上上面的类图还忽略了一点,就是
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {}
DefaultListableBeanFactory 实现了BeanDefinitionRegistry接口。这下就豁然开朗,那么DefaultListableBeanFactory是怎样注冊的呢
@Override
//---------------------------------------------------------------------
// 这里是IOC容器对BeanDefinitionRegistry接口的实现
//---------------------------------------------------------------------
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
.....//这里省略了对BeanDefinition的验证过程
//先看看在容器里是不是已经有了同名的bean,假设有抛出异常。
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
...........
}
else {
//把bean的名字加到IOC容器中去
this.beanDefinitionNames.add(beanName);
}
//这里把bean的名字和Bean定义联系起来放到一个HashMap中去,IOC容器通过这个Map来维护容器里的Bean定义信息。
this.beanDefinitionMap.put(beanName, beanDefinition);
removeSingleton(beanName);
}
总结IOC初始化过程
1、setConfigLocations方法
2、初始化的入口在容器实现中的 refresh()调用来完毕
3、AbstractRefreshableApplicationContext实现的 refreshBeanFactory()方法
4、创建DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)装载bean定义
5、转到XmlBeanDefinitionReader中的loadBeanDefinitions。
6、XmlBeanDefinitionReader通过调用其父类DefaultResourceLoader的getResource方法获取要载入的资源
7 、DocumentLoader将Bean定义资源转换成Document对象
8、doLoadBeanDefinitions中进入registerBeanDefinitions 解 析 D ocument对象
9、DefaultListableBeanFactory中使用一个HashMap的集合对象存放IoC容器中注冊解析的BeanDefinition,
Spring IOC源代码具体解释之容器初始化的更多相关文章
- Spring IOC源代码具体解释之容器依赖注入
Spring IOC源代码具体解释之容器依赖注入 上一篇博客中介绍了IOC容器的初始化.通过源代码分析大致了解了IOC容器初始化的一些知识.先简单回想下上篇的内容 加载bean定义文件的过程.这个过程 ...
- Spring IOC源代码具体解释之整体结构
Spring ICO具体解释之整体结构 IOC介绍 IOC, spring的核心.贯穿Spring始终.直观的来说.就是由spring来负责控制对象的生命周期和对象间的关系,将对象之间的关系抽象出来. ...
- Spring IOC之基于注解的容器配置
Spring配置中注解比XML更好吗?基于注解的配置的介绍提出的问题是否这种途径比XML更好.简单来说就是视情况而定. 长一点的答案是每一种方法都有自己的长处也不足,而且这个通常取决于开发者决定哪一种 ...
- Spring Ioc源码分析系列--容器实例化Bean的四种方法
Spring Ioc源码分析系列--实例化Bean的几种方法 前言 前面的文章Spring Ioc源码分析系列--Bean实例化过程(二)在讲解到bean真正通过那些方式实例化出来的时候,并没有继续分 ...
- Spring IOC - 控制反转(依赖注入) - 配置初始化和销毁的方法
在Spring中如果某个bean在初始化之后,或销毁之前要做一些额外操作可以为该bean配置初始化和销毁的我方法,在这些方法中完成需要的功能. 实验: 通过断点调试模式,测试初始化方法和销毁方法的执行 ...
- 做一个合格的程序员之浅析Spring AOP源代码(十八) Spring AOP开发大作战源代码解析
事实上上一篇文章价值非常小,也有反复造轮子的嫌疑,网上AOP的实例非常多,不胜枚举,事实上我要说的并非这个,我想要说的就是上一节中spring的配置文件: 我们这边并没实用到我们上几节分析的哪几个AO ...
- SpringBoot | 1.2 全注解下的Spring IoC
前言 在学习SpringBoot之前,有几个Spring的重要的基础概念需要提一下,SpringBoot对这些基础概念做进一步的封装,完成自动配置.首先就是Spring的控制反转IOC,由于Sprin ...
- Spring IoC容器初始化过程学习
IoC容器是什么?IoC文英全称Inversion of Control,即控制反转,我么可以这么理解IoC容器: 把某些业务对象的的控制权交给一个平台或者框架来同一管理,这个同一管理的平台可以称为I ...
- Spring IoC容器的初始化过程
Spring IoC容器的初始化包括 BeanDefinition的Resource定位.载入和注册 这三个基本的过程.IoC容器的初始化过程不包含Bean依赖注入的实现.Bean依赖的注入一般会发生 ...
随机推荐
- API函数的学习与运用
1.窗口 1.获取最前方的窗口句柄 GetForegroundWindow() 返回值:HWND类型. 调用方式:HWND hwnd=GetForegroundWindow();即hwnd就存着你的窗 ...
- 转 DNS原理及其解析过程【精彩剖析】
DNS原理及其解析过程[精彩剖析] http://369369.blog.51cto.com/319630/812889/ DNS原理及其解析过程 精彩剖析 网络通讯大部分是基于T ...
- Centos 6.3nginx安装
1. 增加源: vi /etc/yum.repos.d/nginx.repo CentOS: [nginx] name=nginx repo baseurl=http://nginx.org/pack ...
- 通过过滤器和增强request对象解决get提交请求服务器端乱码。
1.表单用get方式提交 <%@ page language="java" contentType="text/html; charset=UTF-8" ...
- pandas read excel文件碰到的一个小问题
今天利用pandas读取excel时,爆出如下错误: 代码为: import pandas as pd db_eua=pd.read_excel('db_eua.xlsx',sheetname='EU ...
- Jekyll搭建个人博客
网上也有HEXO 搭建的博客,有人说使用 HEXO 在多台电脑上发布博客,操作起来并不是那么方便,所以使用Jekyll 来搭建. Jekyll配置 1,安装ruby环境 Windows系统使用Ruby ...
- 最近看点JAVA
这本的书名:<求精要决:JAVA EE编程开发安全精解> 请得很懂 试一下servlet代码: <!DOCTYPE html> <html> <head> ...
- Python的并发并行[1] -> 线程[1] -> 多线程的建立与使用
多线程的建立与使用 目录 生成线程的三种方法 单线程与多线程对比 守护线程的设置 1 生成线程的三种方法 三种方式分别为: 创建一个Thread实例,传给它一个函数 创建一个Thread实例,传给它一 ...
- centos忘记密码,重新设置密码的方法
(1)重新启动Centos,在启动过程中,长按“ESC”键,进入GNU GRUB界面. (2)选择要进入的系统,按“E”键(在启动之前编辑命令). (3)选择第二项操作系统的内核“kernel”,按& ...
- linux之rpm指令
rmp原本是Red Hat Linux发行版专门用来管理Linux各项套件的程序,由于它遵循GPL规则且功能强大方便,因而广受欢迎.逐渐受到其他发行版的采用.RPM套件管理方式的出现,让Linux易于 ...