本文分析自定义标签的解析:
一:BeanDefinitionParserDelegate 类
public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); } public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
通过前一篇文章介绍自定义标签的使用方法后,或多或少对自定义标签的实现过程有一个自己的想法。其实思路非常的简单,无非是根据对应的bean获取对应的命名空间,根据命名空间解析对应的处理器,然后根据用户自定义的处理器进行解析。
(1)获取标签的命名空间
(2)提取自定义标签处理器
有了命名空间,就可以进行NamespaceHandler的提取了,分析NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);在readerContext初始化的时候其属性namespaceHandlerResolver已经被初始化为DefaultNamespaceHandlerResolver的实例,所以,这里调用的resolve方法其实调用的是DefaultNamespaceHandlerResolver类中的方法。
/** * Default implementation of the {@link NamespaceHandlerResolver} interface. * Resolves namespace URIs to implementation classes based on the mappings * contained in mapping file. * * <p>By default, this implementation looks for the mapping file at * {@code META-INF/spring.handlers}, but this can be changed using the * {@link #DefaultNamespaceHandlerResolver(ClassLoader, String)} constructor. * * @author Rob Harrop * @author Juergen Hoeller * @since 2.0 * @see NamespaceHandler * @see DefaultBeanDefinitionDocumentReader */ 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 volatile Map<String, Object> handlerMappings; /** * Create a new {@code DefaultNamespaceHandlerResolver} using the * default mapping file location. * <p>This constructor will result in the thread context ClassLoader being used * to load resources. * @see #DEFAULT_HANDLER_MAPPINGS_LOCATION */ public DefaultNamespaceHandlerResolver() { this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION); } /** * Create a new {@code DefaultNamespaceHandlerResolver} using the * default mapping file location. * @param classLoader the {@link ClassLoader} instance used to load mapping resources * (may be {@code null}, in which case the thread context ClassLoader will be used) * @see #DEFAULT_HANDLER_MAPPINGS_LOCATION */ public DefaultNamespaceHandlerResolver(ClassLoader classLoader) { this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION); } /** * Create a new {@code DefaultNamespaceHandlerResolver} using the * supplied mapping file location. * @param classLoader the {@link ClassLoader} instance used to load mapping resources * may be {@code null}, in which case the thread context ClassLoader will be used) * @param handlerMappingsLocation the mapping file 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; } /** * Locate the {@link NamespaceHandler} for the supplied namespace URI * from the configured mappings. * @param namespaceUri the relevant namespace URI * @return the located {@link NamespaceHandler}, or {@code null} if none found */ @Override public NamespaceHandler resolve(String namespaceUri) { Map<String, Object> handlerMappings = getHandlerMappings(); Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } else if (handlerOrClassName instanceof NamespaceHandler) { 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) BeanUtils.instantiateClass(handlerClass); namespaceHandler.init(); handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } catch (ClassNotFoundException ex) { throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", ex); } catch (LinkageError err) { throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", err); } } } /** * Load the specified NamespaceHandler mappings lazily. */ private Map<String, Object> getHandlerMappings() { if (this.handlerMappings == null) { synchronized (this) { if (this.handlerMappings == null) { try { Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isDebugEnabled()) { logger.debug("Loaded NamespaceHandler mappings: " + mappings); } Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size()); CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); this.handlerMappings = handlerMappings; } catch (IOException ex) { throw new IllegalStateException( "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex); } } } } return this.handlerMappings; } @Override public String toString() { return "NamespaceHandlerResolver using mappings " + getHandlerMappings(); } }