`
songzi0206
  • 浏览: 158840 次
  • 性别: Icon_minigender_1
  • 来自: 上海
博客专栏
Group-logo
All are from ...
浏览量:33813
Group-logo
Programming w...
浏览量:19681
社区版块
存档分类
最新评论

BeanDefinition 解析和注册

阅读更多

       上一篇 Spring IOC BeanFactory 大概分析了 Spring IOC之最简单的容器接口的设计和实现的框架,可以看出 Spring的接口职责的明确划分。有了对上层接口设计的认识,这一篇就直接从容器 XmlBeanFactory着手,解读一下从 Bean配置的读取、解析、并注册为BeanDefinition的过程,最后介绍常用BeanFactoryPostProcessor对BeanDefinition解析之后做进一步修改,从而实现一些特殊的需求。

 

       上一篇结尾之前展示如何利用 DefaultListableBeanFactory编程 Spring IOC,这对于客户端来说是很不方便的,而 XmlBeanFactory 才是第一个比较方便的 IOC容器,客户端可以简单配置 XML文件就可以,当然对于一些生命周期事件的注册可能还不是那么方便 ,这在 Spring高级的容器 ApplicationContext 中会扩展。一般的, XmlBeanFactory使用如下:

BeanFactory container = new XmlBeanFactory(new ClassPathResource("配置文件路径"));
//Person person = (Person)container.getBean("person");
//you can use person object now

       短短两行代码非常简单,其实代表了两大步骤:容器启动加载 BeanDefinition 资源、实例化 Bean 对象。本文着重介绍启动加载资源,不对Bean的实例化和装配过程进行分析。初始化 BeanFactory 时走的是下面类图蓝色的路径 :


 

    从上图 XmlBeanFactory 出发沿蓝色 路径,一次初始化浅色类对象,跟踪代码如下:

 

//在XmlBeanFactory Class中,初始化XmlBeanDefinitionReader ,
//并将自己做为BeanDefinitionRegistry传递
//给XmlBeanDefinitionReader 
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);

//XmlBeanDefinitionReader Class的构造方法
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
		super(registry);
}

//进入XmlBeanDefinitionReader父类AbstractBeanDefinitionReader构造
//方法,这里会走PathMatchingResourcePatternResolver路径
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		this.registry = registry;

		// Determine ResourceLoader to use.
//一般ApplicationContext走这条路径,因为ApplicationContext实现了
//ResourceLoader的功能,因此它比XmlBeanFactory更方便
		if (this.registry instanceof ResourceLoader) {
			this.resourceLoader = (ResourceLoader) this.registry;
		}
		else {
			this.resourceLoader = new PathMatchingResourcePatternResolver();
		}
	}


//初始化PathMatchingResourcePatternResolver,构造方法
//用DefaultResourceLoader对象做为
//PathMatchingResourcePatternResolver依赖的ResourceLoader
public PathMatchingResourcePatternResolver() {
		this.resourceLoader = new DefaultResourceLoader();
	}

//DefaultResourceLoader构造方法,主要是初始化ClassLoader
public DefaultResourceLoader() {
		this.classLoader = ClassUtils.getDefaultClassLoader();
	}

 

    到此,XmlBeanFactory初始化完毕,接着在XmlBeanFactory中开始加载BeanDefinition资源:

this.reader.loadBeanDefinitions(resource);

 

    XmlBeanDefinition.loadBeanDefinitions方法:

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}


public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isInfoEnabled()) {
			logger.info("Loading XML bean definitions from " + encodedResource.getResource());
		}

		Set currentResources = (Set) this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected recursive 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.set(null);
			}
		}
	}

 

      直接看try-catch部分,会根据client指定的Resource获取InputStream,然后再包装成org.xml.sax.InputSource对象,接着把加载任务交给doLoadBeanDefinitions方法,其他是资源清理相关代码。这里不同Resource获取InputStream可以看作是一个策略模式的实现,具体看ApplicationContext会更明显,最常见的如ClassPathRersource,FileSystemResource,ByteArrayResource,UrlResource 获取InputStream方式如下:

//ClassPathResource
public InputStream getInputStream() throws IOException {
		InputStream is = null;
		if (this.clazz != null) {
			is = this.clazz.getResourceAsStream(this.path);
		}
		else {
			is = this.classLoader.getResourceAsStream(this.path);
		}
		if (is == null) {
			throw new FileNotFoundException(
					getDescription() + " cannot be opened because it does not exist");
		}
		return is;
	}

//FileSystemResource
public FileInputStream(File file) throws FileNotFoundException {
	String name = (file != null ? file.getPath() : null);
	SecurityManager security = System.getSecurityManager();
	if (security != null) {
	    security.checkRead(name);
	}
        if (name == null) {
            throw new NullPointerException();
        }
	fd = new FileDescriptor();
	open(name);
    }

//UrlResource
public InputStream getInputStream() throws IOException {
		URLConnection con = this.url.openConnection();
		con.setUseCaches(false);
		return con.getInputStream();
	}

//ByteArrayResource
public InputStream getInputStream() throws IOException {
		return new ByteArrayInputStream(this.byteArray);
	}

       重点看doLoadBeanDefinitions(InputSource, Resource),去除异常处理代码之后,总共就3行:

int validationMode = getValidationModeForResource(resource);
Document doc = this.documentLoader.loadDocument(
	inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);

       第一行获取验证模式,如xsd,schema等XML的验证,可以重写该方法来做自己特定的配置验证。第二行是将输入流加载为xml的Document对象,中间会依赖DefaultDocumentLoader,最后会用javax.xml.parsers包来parse xml文档。到这里还没解析XML配置中各个Bean,继续看源码registerBeanDefinitions(Document,Resource)方法:

BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;

       下面的主角是BeanDefinitionDocumentReader,当然此处使用DefaultBeanDefinitionDocumentReader实现类,看他的方法registerBeanDefinitions(Document,XmlReaderContext):

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;

		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();

		BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);

		preProcessXml(root);
		parseBeanDefinitions(root, delegate);
		postProcessXml(root);
}

        留下两个钩子preProcessXmlpostProcessXml 给客户端增强,主要是中间的parseBeanDefinitions方法:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		if (delegate.isDefaultNamespace(root.getNamespaceURI())) {
			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;
					String namespaceUri = ele.getNamespaceURI();
					if (delegate.isDefaultNamespace(namespaceUri)) {
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}
//处理具体节点
	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		if (DomUtils.nodeNameEquals(ele, IMPORT_ELEMENT)) {
//处理xml import文档
			importBeanDefinitionResource(ele);
		}
		else if (DomUtils.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		else if (DomUtils.nodeNameEquals(ele, BEAN_ELEMENT)) {
//具体处理一个BeanDefinition
                        processBeanDefinition(ele, delegate);
		}
	}

           具体processBeanDefinition:

/**
	 * Process the given bean element, parsing the bean definition
	 * and registering it with the registry.
	 */
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		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));
		}
	}
 

      这里会利用工具类BeanDefinitionReaderUtils来将bean对象注册到BeanDefinitionRegistry中,具体代码嘛就跟上一篇例子使用DefaultListableBeanFactory编码实现很像了。最终Bean配置会被解析成BeanDefinition注册到DefaultListableBeanFactory.beanDefinitionMap中。之后客户端如果要获取Bean对象,XmlBeanFactory会根据注册的BeanDefinition信息进行实例化。

      貌似Bean配置的读取和解析就已进结束,其实不尽然,Web开发配置时经常配置PropertyPlaceholderConfigurer将一些数据库用户名密码等配置放到单独的property文件中,做为一种BeanFactoryPostProcessor,它实际上是在BeanDefinition解析之后,对BeanDefinition信息进行了修改。对于ApplicationContext来说这一步是自动的,无需客户端手动调用。而对于XmlBeanFactory来说就需要自己手动调用了:

XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("testXml.xml"));
		// 声明要使用的PropertyPlaceholderConfigurer
		PropertyPlaceholderConfigurer propertyPostProcessor = new PropertyPlaceholderConfigurer();
		propertyPostProcessor.setLocation(new ClassPathResource("dbValue.properties"));
		// 执行后处理操作
		propertyPostProcessor.postProcessBeanFactory(beanFactory);
		//之后再获取bean,BeanDefinition原始的配置信息已经被dbValue.properties对应的值修改
                ......
 

 

 

  • 大小: 52.8 KB
分享到:
评论
1 楼 xiyueshenyan 2015-06-29  
BeanDefinitionRegistry是个接口,图片错了

相关推荐

    Spring源码学习二:BeanDefinition解析1

    在实际应用中,Spring的BeanDefinition解析涉及到BeanDefinitionReader和BeanDefinitionRegistry两个组件。BeanDefinitionReader负责读取XML、Java配置或注解信息并创建BeanDefinition对象。BeanDefinitionRegistry...

    Spring源码学习四:BeanDefinition装载前奏曲1

    总结来说,Spring的BeanDefinition装载过程涉及了从XML配置文件解析、BeanDefinition创建、BeanFactory初始化到Bean实例化的多个步骤。在学习源码时,应重点关注BeanDefinition的创建、加载和实例化过程,理解Spring...

    撸一撸Spring Framework-IoC-BeanDefinition(csdn)————程序.pdf

    在BeanFactory篇中,我们了解到BeanFactory在启动时会从配置元信息(通常是XML文件)中读取BeanDefinition,并将它们注册到BeanDefinitionRegistry中。这个过程是通过一系列的委托和解析机制完成的。 当我们运行...

    Spring配置类解析(下)(csdn)————程序.pdf

    如果是,那么它们需要注册BeanDefinition。此外,Spring还会遍历配置类中的`BeanMethod`,即带有`@Bean`注解的方法,为每个方法生成对应的BeanDefinition。如果遇到方法重载,即相同类型的方法,Spring不会生成新的...

    spring运行过程中动态注册bean

    #### 创建BeanDefinition 接下来,使用`BeanDefinitionBuilder`来构建Bean的定义。`BeanDefinitionBuilder`提供了简洁的API来定义一个Bean的所有属性和行为,包括它的类型、依赖注入的属性等。在这个例子中,我们...

    00000025_beandefine的用法.rar

    注册过程包括解析配置源,创建BeanDefinition对象,并将其与Bean的ID关联。 三、BeanDefinition的应用场景 1. 动态Bean创建:BeanDefinition可以被用来动态地定义和修改Bean的配置,实现运行时的Bean定制。 2. ...

    Spring多种加载Bean方式解析

    主要的流程是找到定义Bean的方式,然后生成BeanDefinition后注册到Spring上下文中,由Spring自动创建Bean的实例。 BeanDefinition BeanDefinition是一个接口,用来描述一个Bean实例,例如是SINGLETON还是PROTOTYPE...

    spring源码学习

    ##### 1.2 Annotation方式配置的BeanDefinition解析 - **扫描的过程**:Spring通过`ClassPathBeanDefinitionScanner`工具类来扫描指定的包及其子包,找到其中带有特定注解的类。扫描过程中,Spring使用了Ant路径...

    Spring解密之XML解析与Bean注册示例详解

    Spring解密之XML解析与Bean注册是通过XmlBeanDefinitionReader和DefaultListableBeanFactory来实现的,XML解析是将XML配置文件转换为BeanDefinition对象,而Bean注册是将BeanDefinition对象注册到容器中。...

    Spring源码解密之默认标签的解析

    最后,使用`BeanDefinitionReaderUtils`的`registerBeanDefinition`方法将解析后的`BeanDefinition`注册到Bean定义注册表中,使得Spring容器在启动时能够识别并实例化这些Bean。注册过程中,`getReaderContext().get...

    Spring框架系列(7) - Spring IOC实现原理详解之IOC初始化流程.doc

    通过设置资源解析器和环境、设置配置路径、初始化 BeanFactory、加载 Bean 定义资源、解析 Bean 定义资源和注册 BeanDefinition,这些步骤组成了 IOC 初始化流程。 在 Spring 框架中,IOC 容器是核心组件,负责管理...

    06 源码分析-spring自定义标签解析过程

    源码中的类和方法会详细解释每个步骤的具体实现,包括如何注册解析器、如何解析标签、如何处理属性和依赖,以及如何生成`BeanDefinition`对象。通过阅读这些代码,开发者可以更好地掌握Spring框架的内部工作原理,...

    自定义 Schema 解析 Spring Bean

    3. **编写Schema处理器**: 这个处理器是Spring的BeanDefinitionParser接口的实现,它负责解析自定义元素,并根据配置创建相应的BeanDefinition。例如,如果解析到`&lt;myapp:service&gt;`元素,处理器可以创建一个服务类的...

    spring中的BeanFactory解析xml文件

    3. **注册BeanDefinition**:解析后的BeanDefinition被添加到BeanFactory的内部注册表中。BeanDefinition包含了bean的所有元数据,如class、属性值、构造函数参数、依赖关系等。 4. **依赖解析**:在注册Bean...

    Spring读取配置文件原理(Spring如何依赖注入的)

    4. 注册BeanDefinition:将每个BeanDefinition注册到BeanDefinitionRegistry中,通常是`DefaultListableBeanFactory`。 然后,Spring容器会根据BeanDefinition实例化bean,进行依赖注入。依赖注入有两种方式:...

    Spring 5.2.9 06 源码分析-spring自定义标签解析过程

    这是一个上下文对象,包含了当前解析环境的信息,如`BeanDefinitionRegistry`(用于注册`BeanDefinition`)、`Element`(当前处理的XML元素)以及`ReaderContext`(提供了更广泛的上下文信息)。`ParserContext`为...

    深入解析Spring IoC:源码与实践指南

    接着,`AnnotatedBeanDefinitionReader`被实例化,它的主要职责是读取和解析标记了Spring注解的类,将这些类转换成BeanDefinition,BeanDefinition包含了关于Bean的所有元数据,如类名、依赖关系、初始化方法等。...

    Spring XmlBeanFactory 容器的基本实现.doc

    它通过 `DefaultListableBeanFactory` 和 `XmlBeanDefinitionReader` 来加载、解析 XML 配置,将 Bean 定义存储为 `BeanDefinition` 对象,然后根据这些定义创建和管理 Bean 实例。这个过程涉及到资源读取、XML 解析...

    Spring源码解析.pdf

    - **注册**:在IoC容器启动过程中,所有定义的Bean都会被转换成`BeanDefinition`对象,并通过`BeanDefinitionRegistry`接口注册到容器中。 - **管理**:容器通过一个内部的Map结构存储Bean名称和对应的`Bean...

    这一次搞懂Spring自定义标签以及注解解析原理说明.docx

    开发者需要创建一个自定义的`NamespaceHandler`,并实现`parse`方法来解析自定义标签,将解析结果转化为`BeanDefinition`对象并注册到Spring的IoC容器中。为了使Spring知道如何找到对应的`NamespaceHandler`,还需要...

Global site tag (gtag.js) - Google Analytics