Spring源码解析(五)——自定义标签解析
2018-06-16 21:14
465 查看
前言
作为标签解析的第二分支,也正是因为自定义标签的存在,才让Spring框架的诸多功能在短短的几行配置代码后,就生效了。源码解读
承接上篇,我们来看看 parseCustomElement 是如何实现的。public class BeanDefinitionParserDelegate { @Nullable public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); } @Nullable public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { // 获取节点的命名空间 String namespaceUri = getNamespaceURI(ele); // 获取指定命名空间的 NamespaceHandler NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } // 调用 NamespaceHandler.parse对节点进行解析 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); } }
让我们来看下是 如何获取到指定命名空间,对应的 NamespaceHandler。
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver { private final String handlerMappingsLocation; public DefaultNamespaceHandlerResolver(ClassLoader classLoader) { // 会给 handlerMappingsLocation赋值:"META-INF/spring.handlers" this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION); } @Override public NamespaceHandler resolve(String namespaceUri) { // 获取命名空间和对应 NamespaceHandler映射关系 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); } } } private Map<String, Object> getHandlerMappings() { // 仅会在首次调用的时候初始化,锁防并发 if (this.handlerMappings == null) { synchronized (this) { if (this.handlerMappings == null) { try { // 加载所有配置文件:META-INF/spring.handlers 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将 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 this.handlerMappings; } }
映射关系存在不同模块的“META-INF/spring.handlers”,使用工具类将其最终转化为 Map结构,key 是命名空间的名字,让我们来看看这些映射关系。
# spring-context模块下 META-INF/spring.handlers 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
# spring-beans模块下 META-INF/spring.handlers http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
# spring-aop模块下 META-INF/spring.handlers http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
# spring-jdbc模块下 META-INF/spring.handlers http\://www.springframework.org/schema/jdbc=org.springframework.jdbc.config.JdbcNamespaceHandler
# spring-oxm模块下 META-INF/spring.handlers http\://www.springframework.org/schema/oxm=org.springframework.oxm.config.OxmNamespaceHandler
这些对应的 NamespaceHandler 都是 NamespaceHandlerSupport 的子类,其中解析的逻辑在抽象父类中已经实现了:
public abstract class NamespaceHandlerSupport implements NamespaceHandler { // 存放了自定义节点和对应解析器的映射 private final Map<String, BeanDefinitionParser> parsers = new HashMap<String, BeanDefinitionParser>(); @Override public BeanDefinition parse(Element element, ParserContext parserContext) { return findParserForElement(element, parserContext).parse(element, parserContext); } private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) { // 调用 Node.getLocalName String localName = parserContext.getDelegate().getLocalName(element); // 获取对应的解析器 BeanDefinitionParser parser = this.parsers.get(localName); if (parser == null) { parserContext.getReaderContext().fatal( "Cannot locate BeanDefinitionParser for element [" + localName + "]", element); } return parser; } /** * 调用这个方法进行解析器的注册 */ protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) { this.parsers.put(elementName, parser); } }
可以看出解析逻辑委托给 BeanDefinitionParser.parse 实现,注册由子类实现:
public class UtilNamespaceHandler extends NamespaceHandlerSupport { @Override 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()); } }
public class ContextNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { 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()); } }
public class CacheNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenCacheBeanDefinitionParser()); registerBeanDefinitionParser("advice", new CacheAdviceParser()); } }
public class TaskNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { this.registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); this.registerBeanDefinitionParser("executor", new ExecutorBeanDefinitionParser()); this.registerBeanDefinitionParser("scheduled-tasks", new ScheduledTasksBeanDefinitionParser()); this.registerBeanDefinitionParser("scheduler", new SchedulerBeanDefinitionParser()); } }
public class JeeNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("jndi-lookup", new JndiLookupBeanDefinitionParser()); registerBeanDefinitionParser("local-slsb", new LocalStatelessSessionBeanDefinitionParser()); registerBeanDefinitionParser("remote-slsb", new RemoteStatelessSessionBeanDefinitionParser()); } }
这里就不一一列举了,只需要找到对应模块下的 “META-INF/spring.handlers”,找到对应命名空间的 NamespaceHandler.init 方法,就可以看到自定义标签和解析器的对应关系了。
<component-scan>解析
由于自定义标签过多,这里就用<component-scan>举例分析。这个标签的作用就是指定包路径,<context:component-scan>会默认打开<context:annotation-config>:前者支持@Component、@Repository、@Service、@Controller
后者支持@Required、@Autowired、 @PostConstruct、@PersistenceContext、@Resource、@PreDestroy
public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser { // 标签/子标签相关的属性 private static final String BASE_PACKAGE_ATTRIBUTE = "base-package"; private static final String RESOURCE_PATTERN_ATTRIBUTE = "resource-pattern"; private static final String USE_DEFAULT_FILTERS_ATTRIBUTE = "use-default-filters"; private static final String ANNOTATION_CONFIG_ATTRIBUTE = "annotation-config"; private static final String NAME_GENERATOR_ATTRIBUTE = "name-generator"; private static final String SCOPE_RESOLVER_ATTRIBUTE = "scope-resolver"; private static final String SCOPED_PROXY_ATTRIBUTE = "scoped-proxy"; private static final String EXCLUDE_FILTER_ELEMENT = "exclude-filter"; private static final String INCLUDE_FILTER_ELEMENT = "include-filter"; private static final String FILTER_TYPE_ATTRIBUTE = "type"; private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression"; @Override @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { // 获取指定的包路径 String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE); basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage); // 根据 , 或 ; 进行分割 String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); // 这里进行具体的包扫描 Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); // 注册扫描的 beanDefinitions registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; } }
主要的实现在扫描逻辑在 ClassPathBeanDefinitionScanner 中实现:
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider { public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; /** * use-default-filters默认为 true * 会执行 registerDefaultFilters,即注册 @Component、@ManagedBean、@Named筛选器 * 如果设置为 false,则会根据 include-filter/exclude-filter来过滤 */ if (useDefaultFilters) { registerDefaultFilters(); } setEnvironment(environment); setResourceLoader(resourceLoader); } protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>(); // 遍历指定的包路径 for (String basePackage : basePackages) { // 找到符合条件的 BeanDefinition Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); /** * 用 @Component举例 * 如果指定了 value,则使用该值作为 beanName * 否则使用类名,首字母小写 */ String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); // 除了扫描检索出的 bean信息,再添加一些默认配置 if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } // @Lazy、@Primary、@DependsOn、@Role、@Description if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); // 注册 BeanDefinition registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; } public Set<BeanDefinition> findCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>(); try { // classpath*:basePackage/**/*.class String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); for (Resource resource : resources) { if (traceEnabled) { logger.trace("Scanning " + resource); } if (resource.isReadable()) { try { MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); // 筛选出被 @Component、@ManagedBean、@Named标识的类 if (isCandidateComponent(sbd)) { if (debugEnabled) { logger.debug("Identified candidate component class: " + resource); } candidates.add(sbd); } else { ...// 省略日志 } } else { ...// 省略日志 } } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to read candidate component class: " + resource, ex); } } else { ...// 省略日志 } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); } return candidates; } }
构造器会初始化一些过滤条件,默认的会扫描@Component、@ManagedBean、@Named 表示的类,如果指定了<context:component-scan base-package="com.xxx.xxx" use-default-filters="false">,就可以自定义需要扫描的类型、以及过滤掉不需要扫描的类型,比如下面这样:
<context:component-scan base-package="com.xxx.xxx" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan>
总结
这一节讲了自定义标签的解析过程,配合上篇的默认标签解析,容器的创建基本上就算完成了,这些准备工作,就是为了实例创建而收集“资料”(BeanDefinition),那接下来利用这些资料来创建实例了。相关文章推荐
- Spring源码解析笔记3——自定义标签的解析
- Spring源码解析-自定义标签的解析
- [置顶] Spring 源码解析 ---- 自定义标签
- Spring源码解析之二 ------ 自定义标签的解析和注册(IOC的第一步)
- Spring源码解析:默认标签的解析过程
- Spring 源码解析之DispatcherServlet源码解析(五)
- spring源码分析-web容器初始化过程解析1
- Spring 源码解析之HandlerAdapter源码解析(三)
- Spring源码学习-容器初始化之FileSystemXmlApplicationContext(二)路径格式及解析方式(上) 推荐
- Spring源码分析-BeanDefinition加载、解析和注册
- Spring源码解析--(一:源码导入Eclipse)
- Spring-web源码解析之Filter-CharacterEncodingFilter
- spring 源码研究---bean包-- xml解析成bean对象
- 基于Spring开发——自定义标签及其解析
- Spring源码解析笔记9——容器的功能扩展BeanFactory的后处理(BeanFactoryPostProcessor)
- Spring IOC 源码解析
- Spring源码解析二:IOC容器初始化过程详解
- Spring源码解析 配置文件装载与解析
- spring boot 源码解析56-actuator请求处理流程(以EnvironmentEndpoint为例)
- 【Spring】Spring IOC原理及源码解析之scope=request、session