一赃份、問(wèn)題
我們?cè)趕pring的xml配置文件里經(jīng)常定義各種各樣的配置(tx饿这、bean抛丽、mvc、bean等等)晌缘。以及集成第三方框架時(shí),也會(huì)看到一些spring之外的配置,例如dubbo的配置齐莲、security的配置、redis的配置等等磷箕。spring作為一個(gè)龐大的容器,到底是怎么工作的呢选酗?這篇文章將簡(jiǎn)單的介紹一下其實(shí)現(xiàn)機(jī)制,具體細(xì)節(jié)朋友們可以自己查看相關(guān)代碼。
二岳枷、代碼分析
稍微了解一點(diǎn)spring的朋友都知道spring的入口方法就是refresh(),所以我們直接進(jìn)入refresh()方法【不清楚的可以查看一下ClassPathXmlApplicationContext,一步一步跟蹤進(jìn)去】芒填。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
...由于篇幅,我省略了一部分
而解析spring配置的入口就在obtainFreshBeanFactory().refreshBeanFactory()中:
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);//就是這里
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
我們首先會(huì)告訴spring的配置文件所在路徑,即setLocations(),spring創(chuàng)建完BeanFactory,接下來(lái)要做的就是解析配置,完成其他各種屬性的封裝(BeanDefinition空繁、各種代理殿衰、映射關(guān)系等等)。這里的loadBeanDefinitions就是解析配置的核心方法盛泡。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
DefaultListableBeanFactory 作為BeanFactory的一個(gè)核心實(shí)例,且實(shí)現(xiàn)了BeanDefinitionRegistry接口,在解析之前當(dāng)然是要給配置文件解析器(XmlBeanDefinitionReader)設(shè)置一個(gè)存放BeanDefinition的容器(即將注冊(cè)bean的功能委托給BeanFactory)闷祥。然后配置文件解析器就開(kāi)始它的解析工作了。
我們直接定位到XmlBeanDefinitionReader的doLoadBeanDefinitions方法:
try {
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
利用w3c的DOM將配置文件包裝成了一個(gè)Document對(duì)象,接下來(lái)就是利用Dom來(lái)解析childNode相關(guān)屬性傲诵。
接下來(lái),我們沿著代碼定位到DefaultBeaDefinitionDocumentReader中的的parseDefinitions方法:
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);
}
}
核心部分來(lái)了,spring迭代根節(jié)點(diǎn)<beans/>,一個(gè)一個(gè)的解析其子節(jié)點(diǎn).如果是<beans/>節(jié)點(diǎn),則走parseDefaultElement方法,我們這里關(guān)注的是parseCustomElement():
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));
}
根據(jù)元素的namespaceUri(也就是xml文件頭那一串schema)找到對(duì)應(yīng)的NamespaceHandler.【這里就是擴(kuò)展自定義配置的入口】
我們接下來(lái)看一下resolve()方法:
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);
}
}
}
對(duì)應(yīng)的NamespaceHandler就是根據(jù)handlerMappings找到匹配關(guān)系的,所以我們只需要看DefaultNamespaceHandlerResolver.getHandlerMappings()即可凯砍。
private Map<String, Object> getHandlerMappings() {
if (this.handlerMappings == null) {
synchronized (this) {
if (this.handlerMappings == null) {
try {
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);//handlerMappingsLocation默認(rèn)的路徑就是META-INF/spring.handlers
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;
}
spring將classpath下的META-INF/spring.handlers中的配置加載到Map中.這個(gè)spring.handler文件就是以NamespaceUrl作為key,對(duì)應(yīng)的Handler作為value的鍵值對(duì),例如:
···
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
···
當(dāng)我們解析到<context/>配置的時(shí)候,我們會(huì)根據(jù)context的schema,也就是[http://www.springframework.org/schema/context]作為key找到對(duì)應(yīng)的ContextNamespacehandler進(jìn)行解析的箱硕,一切都清楚了。
三悟衩、總結(jié)
spring這點(diǎn)做得很強(qiáng)大,利用這種類(lèi)似于Serviceloader的功能來(lái)平等對(duì)待第三方剧罩,方便別人擴(kuò)展集成。我們也要停下來(lái)想一想,當(dāng)我們自己開(kāi)發(fā)這種核心組件的時(shí)候一定要做到"平等對(duì)待第三方"以及核心足夠小,依賴(lài)少座泳。這也是一種修行惠昔!