【Spring】IOC核心源码学习(三):bean标签和自定义标签实现原理
[size=large]本文将解析spring bean定义标签和自定义标签的解析实现原理。
这里说的标签仅限于以xml作为bean定义描述符的spring容器,继承AbstractXmlApplicationContext的一些子 容器,如XmlApplicationContext、ClassPathXmlApplicationContext、 FileSystemXmlApplicationContext等。同时也仅限于描述schema作为标签定义的情况。
Spring xml ioc 容器常用标签和自定义标签
以 Xml 资源定义的容器配置是我们最常见的一种方式。
Spring 容器需要解析 xml 的标签,并把 xml 里 bean 的定义转化为内部的结构 BeanDifinition 。
Spring 的标签有很多种,其支持的常见的标签有:
标签说明例子
<bean>最常用的,定义一个普通 bean。
<bean id="myBean" lazy-init="true"/>
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="save*"/> <tx:method name="remove*"/> <tx:method name="*" read-only="true"/> </tx:attributes> </tx:advice>
<aop:config proxy-target-advice-ref="txAdvice"/> <aop:advisor pointcut="..." advice-ref="fooAdvice"/> </aop:config>
<util:list id="list" list- value="mapValue1"></entry> <entry key="key12" value="mapValue2"></entry> </util:map>
<bean id="loginAction" p:name="test"></bean>
<lang:groovy id="test" refresh-check-delay="5000" script-source="classpath:com/test/groovy/test.groovy"> </lang:groovy>
<jee:jndi-lookup id="simple" jndi-name="jdbc/MyDataSource" cache="true" resource-ref="true" lookup-on-startup="false" expected-type="com.myapp.DefaultFoo" proxy-interface="com.myapp.Foo"/>
<?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" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd "> <bean id="otherBean" scope="prototype"/> <bean id="myBean" lazy-init="true"/> <bean id="singletonBean" name="code"> http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd
http\://www.springframework.org/schema/aop/spring-aop-2.0.xsd = org/springframework/aop/config/spring-aop-2.0.xsd http\://www.springframework.org/schema/aop/spring-aop-2.5.xsd = org/springframework/aop/config/spring-aop-2.5.xsd http\://www.springframework.org/schema/aop/spring-aop.xsd = org/springframework/aop/config/spring-aop-2.5.xsd http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd = org/springframework/beans/factory/xml/spring-beans-2.0.xsd http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd = org/springframework/beans/factory/xml/spring-beans-2.5.xsd http\://www.springframework.org/schema/beans/spring-beans.xsd = org/springframework/beans/factory/xml/spring-beans-2.5.xsd http\://www.springframework.org/schema/context/spring-context-2.5.xsd = org/springframework/context/config/spring-context-2.5.xsd http\://www.springframework.org/schema/context/spring-context.xsd = org/springframework/context/config/spring-context-2.5.xsd http\://www.springframework.org/schema/jee/spring-jee-2.0.xsd = org/springframework/ejb/config/spring-jee-2.0.xsd http\://www.springframework.org/schema/jee/spring-jee-2.5.xsd = org/springframework/ejb/config/spring-jee-2.5.xsd ......
<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" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd> <bean> </beans>
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler 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/jms=org.springframework.jms.config.JmsNamespaceHandler http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
public void init() { registerBeanDefinitionParser("constant", new ConstantBeanDefinitionParser()); registerBeanDefinitionParser("property-path", new PropertyPathBeanDefinitionParser()); registerBeanDefinitionParser("list", new ListBeanDefinitionParser()); registerBeanDefinitionParser("set", new SetBeanDefinitionParser()); registerBeanDefinitionParser("map", new MapBeanDefinitionParser()); registerBeanDefinitionParser("properties", new PropertiesBeanDefinitionParser()); }
private static class ListBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { protected Class getBeanClass(Element element) { return ListFactoryBean.class; } protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { String listClass = element.getAttribute("list-class"); List parsedList = parserContext.getDelegate().parseListElement(element, builder.getRawBeanDefinition()); builder.addPropertyValue("sourceList", parsedList); if (StringUtils.hasText(listClass)) { builder.addPropertyValue("targetListClass", listClass); } String scope = element.getAttribute(SCOPE_ATTRIBUTE); if (StringUtils.hasLength(scope)) { builder.setScope(scope); } } }
public class PluggableSchemaResolver implements EntityResolver { /***定义schema location的映射文件路径***/ public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas"; private static final Log logger = LogFactory.getLog(PluggableSchemaResolver.class); private final ClassLoader classLoader; private final String schemaMappingsLocation; /** Stores the mapping of schema URL -> local schema path */ private Properties schemaMappings; public PluggableSchemaResolver(ClassLoader classLoader) { this.classLoader = classLoader; this.schemaMappingsLocation = DEFAULT_SCHEMA_MAPPINGS_LOCATION; } public PluggableSchemaResolver(ClassLoader classLoader, String schemaMappingsLocation) { Assert.hasText(schemaMappingsLocation, "'schemaMappingsLocation' must not be empty"); this.classLoader = classLoader; this.schemaMappingsLocation = schemaMappingsLocation; } /**==========中间省略部分代码=========**/ /***此处完成schema的加载***/ protected String getSchemaMapping(String systemId) { if (this.schemaMappings == null) { if (logger.isDebugEnabled()) { logger.debug("Loading schema mappings from [" + this.schemaMappingsLocation + "]"); } try { this.schemaMappings = PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader); if (logger.isDebugEnabled()) { logger.debug("Loaded schema mappings: " + this.schemaMappings); } } catch (IOException ex) { throw new FatalBeanException( "Unable to load schema mappings from location [" + this.schemaMappingsLocation + "]", ex); } } return this.schemaMappings.getProperty(systemId); } }
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver { /** * The location to look for the mapping files. Can be present in multiple JAR files. */ public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers"; /** Logger available to subclasses */ protected final Log logger = LogFactory.getLog(getClass()); /** ClassLoader to use for NamespaceHandler classes */ private final ClassLoader classLoader; /** Resource location to search for */ private final String handlerMappingsLocation; /** Stores the mappings from namespace URI to NamespaceHandler class name / instance */ private Map handlerMappings; public DefaultNamespaceHandlerResolver() { this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION); } public DefaultNamespaceHandlerResolver(ClassLoader classLoader) { this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION); } public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) { Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null"); this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); this.handlerMappingsLocation = handlerMappingsLocation; } /**==========中间省略部分代码=========**/ /************************ * Load the specified NamespaceHandler mappings lazily. * 此处加载延迟加载spring.handlers,只有第一次自定义标签被解析到,才会被加载。 ****************************/ private Map getHandlerMappings() { if (this.handlerMappings == null) { try { Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isDebugEnabled()) { logger.debug("Loaded mappings [" + mappings + "]"); } this.handlerMappings = new HashMap(mappings); } catch (IOException ex) { IllegalStateException ise = new IllegalStateException( "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]"); ise.initCause(ex); throw ise; } } return this.handlerMappings; } }
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource); Document doc = this.documentLoader.loadDocument( inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); return registerBeanDefinitions(doc, resource); } 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); } }
getEntityResolver()
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // Support old XmlBeanDefinitionParser SPI for backwards-compatibility. if (this.parserClass != null) { XmlBeanDefinitionParser parser = (XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass); return parser.registerBeanDefinitions(this, doc, resource); } // Read document based on new BeanDefinitionDocumentReader SPI. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }