恋人心谁翻唱的好听:spring是如何解析自己的配置文件的(1)

来源:百度文库 编辑:中财网 时间:2024/04/27 22:35:19


SpringBeanWebXMLAOP 最近没事儿,就想看看spring是如何加载配置文件的(例如:applicationContext.xml),怕会忘掉,所以记下来:

1.入口
org.springframework.web.context.ContextLoaderListener(这里推荐使用listener,查了查原因,大部分是因为listener启动的时间更早)
//重要源码
/*--------------初始化入口---------------*/
/**
* Initialize the root web application context.
*/
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
this.contextLoader.initWebApplicationContext(event.getServletContext());
}

2.初始化过程
org.springframework.web.context.ContextLoader
//重要源码
/**
* Name of servlet context parameter (i.e., "contextConfigLocation")
* that can specify the config location for the root context, falling back
* to the implementation's default otherwise.
* @see org.springframework.web.context.support.XmlWebApplicationContext#DEFAULT_CONFIG_LOCATION
*/
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";//这个就是在web.xml中配置的applicationContext.xml的键

/**
* Name of the class path resource (relative to the ContextLoader class)
* that defines ContextLoader's default strategy names.
*/
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";//这个文件中配置了默认的ContextLoader
引用ContextLoader.properties中的内容:
# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.
# 这里设置了默认的loder是org.springframework.web.context.support.XmlWebApplicationContext
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

/**
* Initialize Spring's web application context for the given servlet context,
* according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
* "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
* @param servletContext current servlet context
* @return the new WebApplicationContext
* @throws IllegalStateException if there is already a root application context present
* @throws BeansException if the context failed to initialize
* @see #CONTEXT_CLASS_PARAM
* @see #CONFIG_LOCATION_PARAM
*/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        ......
// Determine parent for root web application context, if any.
// 取得父上下文环境。这里还没仔细看
ApplicationContext parent = loadParentContext(servletContext);

// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
this.context = createWebApplicationContext(servletContext, parent);// ① 下面对这个进行说明
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);//将这个环境放入webapp的空共空间中
currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);
}

/**
* Instantiate the root WebApplicationContext for this loader, either the
* default context class or a custom context class if specified.
*

This implementation expects custom contexts to implement the
* {@link ConfigurableWebApplicationContext} interface.
* Can be overridden in subclasses.
*

In addition, {@link #customizeContext} gets called prior to refreshing the
* context, allowing subclasses to perform custom modifications to the context.
* @param servletContext current servlet context
* @param parent the parent ApplicationContext to use, or null if none
* @return the root WebApplicationContext
* @throws BeansException if the context couldn't be initialized
* @see ConfigurableWebApplicationContext
*/
//对①的说明
protected WebApplicationContext createWebApplicationContext(
ServletContext servletContext, ApplicationContext parent) throws BeansException {

Class contextClass = determineContextClass(servletContext);//② 下面进行说明
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}

ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setParent(parent);
wac.setServletContext(servletContext);
wac.setConfigLocation(servletContext.getInitParameter(CONFIG_LOCATION_PARAM));
customizeContext(servletContext, wac);
//这个方法是一个非常深的模板方法 ③ 一会说明
wac.refresh();

return wac;
}

/**
* Return the WebApplicationContext implementation class to use, either the
* default XmlWebApplicationContext or a custom context class if specified.
* @param servletContext current servlet context
* @return the WebApplicationContext implementation class to use
* @throws ApplicationContextException if the context class couldn't be loaded
* @see #CONTEXT_CLASS_PARAM
* @see org.springframework.web.context.support.XmlWebApplicationContext
*/
//对②的说明
protected Class determineContextClass(ServletContext servletContext) throws ApplicationContextException {
//如果这里配置了CONTEXT_CLASS_PARAM参数,则加载这个类
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName);
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
//否则,则从ContextLoader.properties中取得相应ApplicationContext的类进行加载
//这里默认的就是返回org.springframework.web.context.support.XmlWebApplicationContext这个类
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}

下面来说一下比较负载的ConfigurableWebApplicationContext类,也是对③的说明
从上面的代码可以知道XmlWebApplicationContext是ConfigurableWebApplicationContext的实现类或者子类,看看他们的关系:
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext---》
//可以看到AbstractRefreshableWebApplicationContext实现了ConfigurableWebApplicationContext,但是这里面没有refresh()方法
//因为它在后面
public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource---》
//这里也没有,还在后面
public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
implements BeanNameAware, InitializingBean---》
//这里也没有,不过里面有一个重要的方法:
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
......
/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
//这个方法也是在其父类中被定义的,但是在它之中被实现的(也是一个模板)
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);//这个是重要的方法,在XmlWebApplicationContext中被实现了,一会看④
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException(
"I/O error parsing XML document for application context [" + getDisplayName() + "]", ex);
}
}

protected final void closeBeanFactory() {
synchronized (this.beanFactoryMonitor) {
this.beanFactory = null;
}
}
}---》
//这里面终于出现了refresh()方法
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean {
......
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();
}

catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
beanFactory.destroySingletons();

// Reset 'active' flag.
cancelRefresh(ex);

// Propagate exception to caller.
throw ex;
}
}
}
}

//看一下⑤这个方法:
/**
* Tell the subclass to refresh the internal bean factory.
* @return the fresh BeanFactory instance
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();//还记得这个方法吗?
ConfigurableListableBeanFactory beanFactory = getBeanFactory();

if (logger.isInfoEnabled()) {
logger.info("Bean factory for application context [" + getId() + "]: " +
ObjectUtils.identityToString(beanFactory));
}
if (logger.isDebugEnabled()) {
logger.debug(beanFactory.getBeanDefinitionCount() + " beans defined in " + this);
}

return beanFactory;
}
//下面看④这个方法,这个方法被是现在XmlWebApplicationContext类中:
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws 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.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);
}
//看看loadBeanDefinitions(beanDefinitionReader)这个方法:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//这个就是在web.xml中配置的:例如:
/*
contextConfigLocation
/WEB-INF/dataAccessContext-local.xml /WEB-INF/applicationContext.xml


*/
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (int i = 0; i < configLocations.length; i++) {
reader.loadBeanDefinitions(configLocations[i]);
}
}
}
//下面是reader.loadBeanDefinitions(configLocations[i]);
//这个方法在XmlBeanDefinitionReader的父类中实现的:
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {
......
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}

public int loadBeanDefinitions(String location, Set actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}

if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);//这又调用了子类的实现
if (actualResources != null) {
for (int i = 0; i < resources.length; i++) {
actualResources.add(resources[i]);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);//这又调用了子类的实现
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
}
//回到XmlBeanDefinitionReader看看resourceLoader.getResource这个方法:
/**
* Load bean definitions from the specified XML file.
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));//EncodedResource包装一下,生成InputStream对象
}
//loadBeanDefinitions(EncodedResource encodedResource)里面比较重要的方法:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
......
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
......
}
//doLoadBeanDefinitions(InputSource inputSource, Resource resource)里面的重要方法:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
......
return registerBeanDefinitions(doc, resource);
......
}
//看看registerBeanDefinitions(doc, resource)方法:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// Support old XmlBeanDefinitionParser SPI for backwards-compatibility.
//说明是为了支持旧的的XmlBeanDefinitionParser,为了向后兼容
if (this.parserClass != null) {
XmlBeanDefinitionParser parser =
(XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass);
return parser.registerBeanDefinitions(this, doc, resource);
}
// Read document based on new BeanDefinitionDocumentReader SPI.
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
//注意createReaderContext(resource)这个方法:
/*
protected XmlReaderContext createReaderContext(Resource resource) {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();//看看这个方法:
}
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, this.namespaceHandlerResolver);//⑥ 这里声明了XmlReaderContext
}
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());//⑦这里声明了NamespaceHandlerResolver
}
*/
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
//看看createBeanDefinitionDocumentReader()
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
//this.documentReaderClass在类中的值是:DefaultBeanDefinitionDocumentReader.class;
return (BeanDefinitionDocumentReader) BeanUtils.instantiateClass(this.documentReaderClass);
}
//所以下面要DefaultBeanDefinitionDocumentReader里面的registerBeanDefinitions方法了:
//接下来要做的就是指定解析器,进行xml文件的各种解析了
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;

logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
//这里声明了一个解析器的委托(委托这个类去办一些什么事呢?)
BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);

preProcessXml(root);
parseBeanDefinitions(root, delegate);//这个比较重要,看这个
postProcessXml(root);
}

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root.getNamespaceURI())) {
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;
String namespaceUri = ele.getNamespaceURI();
if (delegate.isDefaultNamespace(namespaceUri)) {
//这里解析一些默认的标签
parseDefaultElement(ele, delegate);
}
else {
//这个是我比较感兴趣的,比如annotation-config这种标签的解析
//所以重点看一下这个方法:
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

//去BeanDefinitionParserDelegate看看parseCustomElement这个方法:
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = ele.getNamespaceURI();
//取得针对命名空间解析器的句柄
//this.readerContext = XmlReaderContext 就是⑥,getNamespaceHandlerResolver()返回的是⑦
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));
}
//看看this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
public NamespaceHandler resolve(String namespaceUri) {
//又是一个重要的,没有见过的方法
//如果map为空的,则初始化一个,但是里面都是字符串
Map handlerMappings = getHandlerMappings();
//最初取出来的为字符串
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
//由于最初的handlerOrClassName其实是字符串,所以不会进入这个分支的
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
//初始时会进入这个分支,然后加载相应的class
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();//这个方法重要⑦
//覆盖原map中相应的值
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);
}
}
}

//看看getHandlerMappings()方法:
/**
* Load the specified NamespaceHandler mappings lazily.
*/
//如果这个map是空的,则初始化它
private Map getHandlerMappings() {
if (this.handlerMappings == null) {
try {
//this.handlerMappingsLocation这个文件中的内容是:
/*
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
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/jms=org.springframework.jms.config.JmsNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
*/
//将配置文件中的键值存入map中,例如:"http\://www.springframework.org/schema/aop", "org.springframework.aop.config.AopNamespaceHandler"
//此时键和值都是字符串
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded mappings [" + mappings + "]");
}
this.handlerMappings = new HashMap(mappings);
}
catch (IOException ex) {
IllegalStateException ise = new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]");
ise.initCause(ex);
throw ise;
}
}
return this.handlerMappings;
}

//下面来看⑦,举其中一个handler的例子,ContextNamespaceHandler:
//其实就是注册了很多的解析器,用来解析固定的标签
public void init() {
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
registerJava5DependentParser("annotation-config",
"org.springframework.context.annotation.AnnotationConfigBeanDefinitionParser");
registerJava5DependentParser("component-scan",
"org.springframework.context.annotation.ComponentScanBeanDefinitionParser");
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}

//最后简单说说⑧
NamespaceHandlerSupport:BeanDefinition parse(Element element, ParserContext parserContext)---》
//它之用只定义了doParse方法
LoadTimeWeaverBeanDefinitionParser(以这个为例子,它复杂一些)extends AbstractSingleBeanDefinitionParser ---》
//parseInternal方法调用了其中的:doParse方法
AbstractSingleBeanDefinitionParser:protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext)
AbstractSingleBeanDefinitionParser extends AbstractBeanDefinitionParser ---》
//这个方法中调用了parseInternal(element, parserContext)方法,这个方法在其子类中实现,最后调用LoadTimeWeaverBeanDefinitionParser的doParse方法
AbstractBeanDefinitionParser:public final BeanDefinition parse(Element element, ParserContext parserContext)