700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Spring源码解析(五)——自定义标签解析

Spring源码解析(五)——自定义标签解析

时间:2022-04-22 13:28:01

相关推荐

Spring源码解析(五)——自定义标签解析

独角兽企业重金招聘Python工程师标准>>>

前言

作为标签解析的第二分支,也正是因为自定义标签的存在,才让Spring框架的诸多功能在短短的几行配置代码后,就生效了。

源码解读

承接上篇,我们来看看parseCustomElement 是如何实现的。

public class BeanDefinitionParserDelegate {@Nullablepublic BeanDefinition parseCustomElement(Element ele) {return parseCustomElement(ele, null);}@Nullablepublic BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {// 获取节点的命名空间String namespaceUri = getNamespaceURI(ele);// 获取指定命名空间的 NamespaceHandlerNamespaceHandler 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);}@Overridepublic 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.handlersProperties 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.handlershttp\:///schema/context=org.springframework.context.config.ContextNamespaceHandlerhttp\:///schema/jee=org.springframework.ejb.config.JeeNamespaceHandlerhttp\:///schema/lang=org.springframework.scripting.config.LangNamespaceHandlerhttp\:///schema/task=org.springframework.scheduling.config.TaskNamespaceHandlerhttp\:///schema/cache=org.springframework.cache.config.CacheNamespaceHandler

# spring-beans模块下 META-INF/spring.handlershttp\:///schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandlerhttp\:///schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandlerhttp\:///schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

# spring-aop模块下 META-INF/spring.handlershttp\:///schema/aop=org.springframework.aop.config.AopNamespaceHandler

# spring-jdbc模块下 META-INF/spring.handlershttp\:///schema/jdbc=org.springframework.jdbc.config.JdbcNamespaceHandler

# spring-oxm模块下 META-INF/spring.handlershttp\:///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>();@Overridepublic BeanDefinition parse(Element element, ParserContext parserContext) {return findParserForElement(element, parserContext).parse(element, parserContext);}private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {// 调用 Node.getLocalNameString 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic void init() {registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenCacheBeanDefinitionParser());registerBeanDefinitionParser("advice", new CacheAdviceParser());}}

public class TaskNamespaceHandler extends NamespaceHandlerSupport {@Overridepublic 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 {@Overridepublic 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@Nullablepublic 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);// 注册扫描的 beanDefinitionsregisterComponents(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) {// 找到符合条件的 BeanDefinitionSet<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、@Descriptionif (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);// 注册 BeanDefinitionregisterBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;}public Set<BeanDefinition> findCandidateComponents(String basePackage) {Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();try {// classpath*:basePackage/**/*.classString 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),那接下来利用这些资料来创建实例了。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。