XML注入Bean的使用

xml配置:

<?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"
	   default-lazy-init="false">

	<bean id="user" class="com.morris.spring.entity.User">
		<description>这是一个描述信息,没什么卵用</description>
		<meta key="str" value="I'm a meta attribute."/>
		<property name="username" value="morris"/>
		<property name="age" value="18"/>
	</bean>

</beans>

使用ClassPathXmlApplicationContext来创建容器:

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("xml/spring-bean.xml");
User user = (User) context.getBean("user");
System.out.println(user);

运行结果如下:

User(username=morris, age=18)
源码分析

xml解析的入口:

org.springframework.context.support.AbstractApplicationContext#refresh
-> org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory
	-> org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
		-> org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)

下面只会展示一些源码以及关键流程节点:

AbstractXmlApplicationContext#loadBeanDefinitions

创建了一个XmlBeanDefinitionReader对象,将解析XML的工作委托给他:

// org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)	
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	// Create a new XmlBeanDefinitionReader for the given BeanFactory.
	/**
	 * 委托模式,委托XmlBeanDefinitionReader解析xml
	 */
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

	// Configure the bean definition reader with this context's
	// resource loading environment.
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	beanDefinitionReader.setResourceLoader(this);
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

	// Allow a subclass to provide custom initialization of the reader,
	// then proceed with actually loading the bean definitions.
	initBeanDefinitionReader(beanDefinitionReader);
	/**
	 * 真正的解析
	 */
	loadBeanDefinitions(beanDefinitionReader);
}

XmlBeanDefinitionReader.doLoadBeanDefinitions

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions

使用JDK提供的DOM解析xml为Document对象:

// org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
		throws BeanDefinitionStoreException {

	try {
		// 使用JDK提供的DOM解析xml为Document对象
		Document doc = doLoadDocument(inputSource, resource);
		// 注册BD
		int count = registerBeanDefinitions(doc, resource);
... ...
	}

XmlBeanDefinitionReader.registerBeanDefinitions

又使用委托模式,将从Document对象中解析BD的任务交给BeanDefinitionDocumentReader:

// org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
	/**
	 * 又使用委托模式,将从Document对象中解析BD的任务交给BeanDefinitionDocumentReader
	 */
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	int countBefore = getRegistry().getBeanDefinitionCount();
	/**
	 * @see DefaultBeanDefinitionDocumentReader#registerBeanDefinitions(org.w3c.dom.Document, org.springframework.beans.factory.xml.XmlReaderContext)
	 */
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
	return getRegistry().getBeanDefinitionCount() - countBefore;
}

DefaultBeanDefinitionDocumentReader.parseBeanDefinitions

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

开始解析Bean标签:

// org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
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);
	}
}

DefaultBeanDefinitionDocumentReader.parseDefaultElement

何为默认标签:上面例子中的<bean>标签。

解析默认标签:

// org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElement	
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
		// 处理import标签
		importBeanDefinitionResource(ele);
	}
	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
		// 处理alias标签
		processAliasRegistration(ele);
	}
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		// 处理Bean标签
		processBeanDefinition(ele, delegate);
	}
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		// 处理beans标签
		// recurse
		doRegisterBeanDefinitions(ele);
	}
}

BeanDefinitionParserDelegate.parseBeanDefinitionElement

org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, java.lang.String, org.springframework.beans.factory.config.BeanDefinition)

解析<bean>元素:

// org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, java.lang.String, org.springframework.beans.factory.config.BeanDefinition)
public AbstractBeanDefinition parseBeanDefinitionElement(
		Element ele, String beanName, @Nullable BeanDefinition containingBean) {
... ...
	try {
		// GenericBeanDefinition
		AbstractBeanDefinition bd = createBeanDefinition(className, parent);

		// 解析Bean标签的属性,只是简单的将xml中属性的值设置到BD,没有任何处理逻辑
		parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
		// 处理Bean标签的description子标签
		bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

		// 解析子元素meta
		parseMetaElements(ele, bd);
		// 解析look-up子元素
		parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
		// 解析replaced-method子元素
		parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
		// 解析constructor-arg子元素
		parseConstructorArgElements(ele, bd);
		// 解析property子元素
		parsePropertyElements(ele, bd);
		// 解析qualifier子元素
		parseQualifierElements(ele, bd);

		bd.setResource(this.readerContext.getResource());
		bd.setSource(extractSource(ele));

		return bd;
... ...

BeanDefinitionParserDelegate.createBeanDefinition

创建GenericBeanDefinition:

// org.springframework.beans.factory.support.BeanDefinitionReaderUtils#createBeanDefinition
public static AbstractBeanDefinition createBeanDefinition(
		@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {

	GenericBeanDefinition bd = new GenericBeanDefinition();
	bd.setParentName(parentName);
	if (className != null) {
		if (classLoader != null) {
			bd.setBeanClass(ClassUtils.forName(className, classLoader));
		}
		else {
			bd.setBeanClassName(className);
		}
	}
	return bd;
}

BeanDefinitionParserDelegate.parseBeanDefinitionAttributes

解析Bean标签的属性,只是简单的将xml中属性的值设置到BD,没有任何处理逻辑:

// org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionAttributes
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
		@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {

	if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
		error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
	}
	else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
		bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
	}
	else if (containingBean != null) {
		// Take default from containing bean in case of an inner bean definition.
		bd.setScope(containingBean.getScope());
	}
... ...

BeanDefinitionParserDelegate.parseMetaElements

meta子元素的的使用:

<bean id="user" class="com.morris.spring.entity.User">
	<description>这是一个描述信息,没什么卵用</description>
	<meta key="str" value="I'm a meta attribute."/>
</bean>

这段代码并不会直接体现在User的属性当中,而是一个额外的声明,当需要使用里面的信息的时候可以通过BeanDefinition的getAttribute(key)方法进行获取。

public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
	NodeList nl = ele.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
			Element metaElement = (Element) node;
			String key = metaElement.getAttribute(KEY_ATTRIBUTE);
			String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
			BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
			attribute.setSource(extractSource(metaElement));
             // meta放入到bd的attribute中
			attributeAccessor.addMetadataAttribute(attribute);
		}
	}
}

总结:Bean默认标签解析后的结果就是生成一个BeanDefinition添加到Spring容器当中。

BeanDefinitionParserDelegate.parseCustomElement

org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)

解析自定义标签,自定义标签类似下面的context:component-scan

<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"
	   default-lazy-init="false">
	<context:component-scan base-package="com.morris.spring"/>
</beans>

解析自定义标签的步骤如下:

  1. 获取自定义标签的namespace命令空间,例如:http://www.springframework.org/schema/context。

  2. 根据命名空间获取NamespaceHandler对象。

  3. 调用NamespaceHandler对象的parse()进行解析。

// org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	// 获得namespace,如http://www.springframework.org/schema/context
	String namespaceUri = getNamespaceURI(ele);
	if (namespaceUri == null) {
		return null;
	}
	/**
	 * @see DefaultNamespaceHandlerResolver#resolve(java.lang.String)
	 */
	NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
	if (handler == null) {
		error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
		return null;
	}
	/**
	 * @see NamespaceHandlerSupport#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
	 */
	// 开始解析自定义标签
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

DefaultNamespaceHandlerResolver.resolve

  1. 获取所有的handlerMappings。

  2. 根据namespace从handlerMappings中获得NamespaceHandler。

  3. 如果解析器是类名,就通过反射进行实例化,并放入handlerMappings中,第二次使用时就是对象,不再需要实例化了。

  4. 调用NamespaceHandler的init()方法进行初始化。

// org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#resolve
public NamespaceHandler resolve(String namespaceUri) {
	// 获取所有的handlerMappings
	Map<String, Object> handlerMappings = getHandlerMappings();
	// 根据key获得value,如org.springframework.context.config.ContextNamespaceHandler
	Object handlerOrClassName = handlerMappings.get(namespaceUri);
	if (handlerOrClassName == null) {
		return null;
	}
	else if (handlerOrClassName instanceof NamespaceHandler) {
		// 第二次及后续解析相同的namespace时,就会进入这里,类在下面的else中被缓存了
		return (NamespaceHandler) handlerOrClassName;
	}
	else {
		String className = (String) handlerOrClassName;
		try {
			Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
			if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
				throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
						"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
			}
			// 实例化NamespaceHandler
			NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
			// 调用NamespaceHandler的init()方法
			namespaceHandler.init();
			handlerMappings.put(namespaceUri, namespaceHandler);
			return namespaceHandler;
		}
... ...
	}
}

DefaultNamespaceHandlerResolver.getHandlerMappings

加载所有META-INF/spring.handlers中的内容封装为一个Map。

// org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#getHandlerMappings
private Map<String, Object> getHandlerMappings() {
	Map<String, Object> handlerMappings = this.handlerMappings;
	// 只初始化一次
	if (handlerMappings == null) {
		synchronized (this) {
			handlerMappings = this.handlerMappings;
			if (handlerMappings == null) {
				if (logger.isTraceEnabled()) {
					logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
				}
				try {
					/**
					 * @see DefaultNamespaceHandlerResolver#DEFAULT_HANDLER_MAPPINGS_LOCATION
					 */
					// 加载所有META-INF/spring.handlers为Properties
					Properties mappings =
							PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
					if (logger.isTraceEnabled()) {
						logger.trace("Loaded NamespaceHandler mappings: " + mappings);
					}
					handlerMappings = new ConcurrentHashMap<>(mappings.size());
					// 将Properties的内容转化为Map
					CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
					this.handlerMappings = handlerMappings;
				}
				catch (IOException ex) {
					throw new IllegalStateException(
							"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
				}
			}
		}
	}
	return handlerMappings;
}

下面来看下META-INF/spring.handlers的内容是什么,这里以spring-context为例,spring的其他jar下面也有这个文件:

http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler

NamespaceHandler.init

NamespaceHandler的init()只是往往父类的parsers属性中添加各种解析器,方便后续parse时使用。

// org.springframework.context.config.ContextNamespaceHandler#init
public void init() {
	// 往父类的parsers属性中添加各种解析器
	registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
	registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
	registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
	registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
	registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
	registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
	registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}

NamespaceHandler#parse

根据localName获得具体的解析器BeanDefinitionParser:

public BeanDefinition parse(Element element, ParserContext parserContext) {
	// ComponentScanBeanDefinitionParser
,根据名字获得具体的解析器
	BeanDefinitionParser parser = findParserForElement(element, parserContext);
	/**
	 * @see org.springframework.context.annotation.ComponentScanBeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
	 */
	return (parser != null ? parser.parse(element, parserContext) : null);
}

private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
	// localName为context:component-scan中的component-scan
	String localName = parserContext.getDelegate().getLocalName(element);
	// parsers这个属性一般是前面调用NamespaceHandler.init()时添加的
	BeanDefinitionParser parser = this.parsers.get(localName);
	if (parser == null) {
		parserContext.getReaderContext().fatal(
				"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
	}
	return parser;
}
总结

BeanDefinition的各种类型:

  • 经过解析XML注入的BeanDefinition的类型为GenericBeanDefinition。

  • 经过ComponentScan扫描注入的BeanDefinition的类型为ScannedGenericBeanDefinition。

bean标签

XML值的bean元素解析后会被封装为一个BeanDefinition对象,所以BeanDefinition对象会有一个属性来存储bean元素中的属性。

bean标签的属性总结:

  • id:Bean的唯一标识名。它必须是合法的XMLID,在整个XML文档中唯一。

  • name:用来为id创建一个或多个别名。它可以是任意的字母符合。多个别名之间用逗号或空格分开。

  • class:用来定义类的全限定名(包名+类名)。只有子类Bean不用定义该属性。

  • parent:子类Bean定义它所引用它的父类Bean。这时前面的class属性失效。子类Bean会继承父类Bean的所有属性,子类Bean也可以覆盖父类Bean的属性。注意:子类Bean和父类Bean是同一个Java类。

  • abstract(默认为”false”):用来定义Bean是否为抽象Bean。它表示这个Bean将不会被实例化,一般用于父类Bean,因为父类Bean主要是供子类Bean继承使用。

  • singleton(默认为“true”):定义Bean是否是Singleton(单例)。如果设为“true”,则在BeanFactory作用范围内,只维护此Bean的一个实例。如果设为“flase”,Bean将是Prototype(原型)状态,BeanFactory将为每次Bean请求创建一个新的Bean实例。

  • lazy-init(默认为“default”):用来定义这个Bean是否实现懒初始化。如果为“true”,它将在BeanFactory启动时初始化所有的SingletonBean。反之,如果为“false”,它只在Bean请求时才开始创建SingletonBean。

  • autowire(自动装配,默认为“default”):它定义了Bean的自动装载方式。

    • “no”:不使用自动装配功能。

    • “byName”:通过Bean的属性名实现自动装配。

    • “byType”:通过Bean的类型实现自动装配。

    • “constructor”:类似于byType,但它是用于构造函数的参数的自动组装。

    • “autodetect”:通过Bean类的反省机制(introspection)决定是使用“constructor”还是使用“byType”。

  • dependency-check(依赖检查,默认为“default”):它用来确保Bean组件通过JavaBean描述的所以依赖关系都得到满足。在与自动装配功能一起使用时,它特别有用。

    • none:不进行依赖检查。

    • objects:只做对象间依赖的检查。

    • simple:只做原始类型和String类型依赖的检查

    • all:对所有类型的依赖进行检查。它包括了前面的objects和simple。

  • depends-on(依赖对象):这个Bean在初始化时依赖的对象,这个对象会在这个Bean初始化之前创建。

  • init-method:用来定义Bean的初始化方法,它会在Bean组装之后调用。它必须是一个无参数的方法。

  • destroy-method:用来定义Bean的销毁方法,它在BeanFactory关闭时调用。同样,它也必须是一个无参数的方法。它只能应用于singletonBean。

  • factory-method:定义创建该Bean对象的工厂方法。它用于下面的“factory-bean”,表示这个Bean是通过工厂方法创建。此时,“class”属性失效。

  • factory-bean:定义创建该Bean对象的工厂类。如果使用了“factory-bean”则“class”属性失效。

  • autowire-candidate:采用xml格式配置bean时,将元素的autowire-candidate属性设置为false,这样容器在查找自动装配对象时,将不考虑该bean,即它不会被考虑作为其它bean自动装配的候选者,但是该bean本身还是可以使用自动装配来注入其它bean的。

Spring SPI与Java SPI的区别

  • Java中的SPI,文件名即接口,文件中的内容为接口的实现类,文件内容中的所有类都会被加载。

  • Spring中的SPI,文件中内容为key-value格式,可以实现不同接口的类放在一个文件中,还可以按需加载,节省内存。