原创 Spring源码解析正确姿势之IOC(3)

前言

前面将传统标签解析讲完了,接下来该讲自定义标签解析重点是componentScan

正文

将镜头拉回到DefaultBeanDefinitionDocumentReader类中的下面方法中(不熟悉的看前面博客)

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	if (delegate.isDefaultNamespace(root)) {
		NodeList nl = root.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			if (node instanceof Element) {
				Element ele = (Element) node;
				if (delegate.isDefaultNamespace(ele)) {

					//默认标签解析
					parseDefaultElement(ele, delegate);
				}
				else {

					//自定义标签解析
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	else {
		delegate.parseCustomElement(root);
	}
}

接着点入delegate.parseCustomElement(ele);方法中,我们看一下是如何解析自定义标签的

@Nullable
	public BeanDefinition parseCustomElement(Element ele) {
		return parseCustomElement(ele, null);
	}

继续点下去,这里传入的第二个参数是null,稍微记一下

@Nullable
	public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		//1 每一个自定义标签都有namespaceUri,这里是获取它的uri
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		//2通过uri获得对应的处理类handler
		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));
	}

这里重点解释一下
看代码注释1说每一个自定义标签都有namespaceUri
什么意思呢,例如spring.xml中的这个自定义标签(带有前缀的这种标签叫自定义标签类似还有<aop:xxx>,<mvc:xxx>)

 <context:component-scan base-package="XXX.xxx.xxx">
    </context:component-scan>

它的namespaceUri就是这个:
http://www.springframework.org/schema/context
这个你可以去你的spring配置文件里面找一下肯定找到的到。
与namespaceUri对应的handler又是怎么会是呢?
点入NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);这个方法就是注释2

public NamespaceHandler resolve(String namespaceUri) {
	//获取spring中所有jar包里面的 "META-INF/spring.handlers"文件,并且建立namespaceUri和对应handler映射关系
	Map<String, Object> handlerMappings = getHandlerMappings();

	//根据namespaceUri:http://www.springframework.org/schema/context,获取到这个命名空间的处理类
	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);

			//调用处理类的init方法,在init方法中完成标签元素解析类的注册
			namespaceHandler.init();
			handlerMappings.put(namespaceUri, namespaceHandler);
			return namespaceHandler;
		}
		catch (ClassNotFoundException ex) {
			throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
					"] for namespace [" + namespaceUri + "]", ex);
		}
		catch (LinkageError err) {
			throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
					className + "] for namespace [" + namespaceUri + "]", err);
		}
	}
}

(注意:这里调用了namespaceHandler.init();很重要,后面会讲到)也许有人会问,你怎么知道是"META-INF/spring.handlers"文件这个文件呢?点入getHandlerMappings

private Map<String, Object> getHandlerMappings() {
		Map<String, Object> handlerMappings = this.handlerMappings;
		if (handlerMappings == null) {
			synchronized (this) {
				handlerMappings = this.handlerMappings;
				if (handlerMappings == null) {
					if (logger.isTraceEnabled()) {
						logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
					}
					try {
						//加载"META-INF/spring.handlers"文件过程
						Properties mappings =
								PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
						if (logger.isTraceEnabled()) {
							logger.trace("Loaded NamespaceHandler mappings: " + mappings);
						}

						//所有"META-INF/spring.handlers"文件里面的内容建立映射关系
						handlerMappings = new ConcurrentHashMap<>(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 handlerMappings;
	}

大家看这个变量this.handlerMappingsLocation,是一个location,找一下这个变量定义的地方

public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {

	/**
	 * 这个变量定义了路径
	 */
	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. */
	@Nullable
	private final ClassLoader classLoader;

	/** 这个变量是看我们要找的,并未初始化*/
	private final String handlerMappingsLocation;

接着看类构造器,第一个构造器传入了DEFAULT_HANDLER_MAPPINGS_LOCATION,调用第二个构造器对handlerMappingsLocation进行了赋值,所以handlerMappingsLocation的值是"META-INF/spring.handlers";

public DefaultNamespaceHandlerResolver() {
	this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}

//中间还有一个构造,这里就不展示了
public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader, String handlerMappingsLocation) {
	Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
	this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
	this.handlerMappingsLocation = handlerMappingsLocation;
}

看到这里应该没啥问题了吧,镜头拉回到上面

@Nullable
	public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		//1 每一个自定义标签都有namespaceUri,这里是获取它的uri
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		//2通过uri获得对应的处理类handler
		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));
	}

上面说了,在resovle方法中调用了nameSpaceHandler的init()方法,这里我们看一下如何去找

 <context:component-scan base-package="XXX.xxx.xxx">
    </context:component-scan>

对应处理类,找到处理类后点进去
都在这图上了,看到对应处理类了没

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());
	}

}

看到这个init方法没,做了什么?就是将具体的parser处理类放到spring中
比如ComponentScanBeanDefinitionParser();
点入这个类中,有一个很重要的方法parse,这个类完成了ComponentScan工作,逻辑为:
1.寻找你配置的basePackage下的所有.class文件(带有@Component)
2.找到后封装成beanDefinition放到set容器返回
3.注册到spring中

public BeanDefinition parse(Element element, ParserContext parserContext) {
		//获取basePackage属性
		String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
		basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
		//可以用逗号分开
		String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

	//创建注解扫描器
	// Actually scan for bean definitions and register them.
	ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
	//扫描并把扫描的类封装成beanDefinition对象  核心方法,重要程度 5
	Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
	registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

	return null;
}

那在哪里调用的呢?其实已经讲到了,就在上面
看到这里应该没啥问题了吧,镜头拉回到上面

@Nullable
	public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		//1 每一个自定义标签都有namespaceUri,这里是获取它的uri
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		//2通过uri获得对应的处理类handler
		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));
	}

最后有个return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));充分的应用的多态

最后

讲到这里自定义标签就讲完了,总结一下:
我们这ioc前三章博客,就是说了将spring.xml变成beanDefinition,存入到spring中,那么我们对那些类实例化了嘛,并没有,只是完成了准备工作,再说实例化之前先得介绍一下beanDefinition。下一章讲一下beanDefinition是个啥?在spring容器中有啥作用。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值