前言
作为标签解析的第二分支,也正是因为自定义标签的存在,才让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),那接下来利用这些资料来创建实例了。