因为要开发新游戏问题,源码看了一半被迫中止。但是断了也一直没有动力再去续着看,所以就只好未完待续,看什么时候有动力接着看。
在使用ClassPathXmlApplicationContext的时候,ClassPathXmlApplicationContext最终调用的构建方法如下:
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
在super(parent)里面,调用的是父类AbstractApplicationContext的构造方法,如下:
/**
* Create a new AbstractApplicationContext with no parent.
*/
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
这里面简单构建了一个资源路径的解析器,用于解析资源的具体路径,默认的解析器为PathMatchingResourcePatternResolver。之后设置了资源的路径,以及刷新上下文。refresh()具体内容如下:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 刷新前准备
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 获取BeanFactory,其中包括bean的定位加载和注册
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// BeanFactory初始化
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.
// bean注册
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) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
这里面的源码,包括了BeanFactory的初始化、资源的定位加载和注册、bean的初始化以及注入等spring的基础功能,各部分的具体实现,下面介绍。
加载分两种加载,一种是直接定义在xml里面的标签,如果是这种bean定义的话直接调用相关的解析器进行加载,时序图如下:
另外一种是除之外的标签,例如这种自动扫描标签,如果是这个标签的话,那就调用其对应的ComponentScanBeanDefinitionParser进行解析。ComponentScanBeanDefinitionParser的初始化,是在解析xml的时候,遇到标签对应的NamespaceHandler,在其init方法里面初始化的,代码如下:
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());
}
}
而ContextNamespaceHandler本身的实例化,是在DefaultNamespaceHandlerResolver.resolve里面,根据spring.handlers的定义获取当前namespace对应的handler(例如http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
,这里的意思是标签对应的handler为ContextNamespaceHandler),获取到对应的handler名字,通过反射创建的,代码如下(代码有省略):
public NamespaceHandler resolve(String namespaceUri) {
// spring.handlers对应的key和value键值对
Map<String, Object> handlerMappings = getHandlerMappings();
Object handlerOrClassName = handlerMappings.get(namespaceUri);
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
String className = (String) handlerOrClassName;
try {
// 通过反射实例化handler
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);
// 注册context里面具体的标签对应的parser
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
...
}
}
}
如果是以扫描项目路径下的类的话,则是调用ComponentScanBeanDefinitionParser.parse方法进行Beandefination的加载,时序图如下:
这里插个题外话,通过自定义NamespaceHandler和BeanDefinitionParser,可以实现自定义的标签和功能并且在spring初始化的时候调用,例如我参与的第一个游戏战国之怒,平时开发的项目为业务项目,而其对应的common-resource、common-socket、ramcache等游戏基础模块,都是通过自定义namespace及其对应的handler来集成到游戏里面的,而不需要在业务服调用相关的启动模块。基础模块通过spring解析对应的标签,实例化bean之后调用,达到解耦的目的。换句话来说,只需要在spring中定义对应的xml标签,即可启动相关模块的功能,而不需要在业务项目显示调用基础模块相关代码来启动。
这是一个很好的模块化思想,xml中配置有就有,没有就没有,干干净净,增加模块化功能无须添加代码,而删除现有功能无须删除代码,只需要删除对应xml即可,功能更新的时候更是简单,把对应的xml标签内容更改了即可。
### 注册