Spring框架源码学习(一) IOC的BEAN加载

Spring的主要思想体现在IOC及AOP上;本文是LZ自己在看Spring源码时的一些记录,大家也可以跟着这个思路看一看Spring的一些源码,从主线上了解其原理;
本文的主线:就是Spring通过加载一个配置文件,生成一个BeanFactory的过程;
这是实现IOC的基础,读取配置,生成BeanFactory。所以不会在意一些小细节或跟主线不重要的类,不然看源码就会很迷糊。因为继承体系复杂,子类复用父类的方法较多,在各个类中跳跃会比较多。现在从源码上捋捋Spring是怎么实现IOC的;
IOC是将对象的创建和依赖关系交给容器,即将对象BEAN的创建及依赖反转到框架中;
如下的例子就是一般我们测试Spring的时候写的小方法:
public class MainApp {、
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
}
}
还有如下的各种形式去生成BeanFactory
BeanFactory beanFactory=newFileSystemXmlApplicationContext("classpath:applicationContext.xml");

       上面的ApplicationContext是BeanFactory的子类,所以其实质是一样的;所以我们的主线就是通过这个FileSystemXmlApplicationContext类,看他是如何加载配置生成Bean的容器的。

首先需要介绍各个重要的类或接口:
BeanFactory类 源码:
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
boolean containsBean(String name); boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException;
Class<?> getType(String name) throws NoSuchBeanDefinitionException; String[] getAliases(String name);
}
BeanFactory 是最上层的接口,也就是俗称的IOC容器的祖宗,各种IOC容器都只是它的实现或者为了满足特别需求的扩展实现,包括我们平时用的最多的ApplicationContext。从上面的方法就可以看出,这些工厂的实现最大的作用就是根据bean的名称亦或类型等等,来返回一个bean的实例。
BeanDefinition接口
   顾名思义,这个便是spring中的bean定义接口,所以其实我们工厂里持有的bean定义,就是一堆这个或者其实现类和子接口。这个接口并非直接的祖宗接口,他所继承的两个接口一个是core下面的AttributeAccessor,继承这个接口就以为这我们的bean定义接口同样具有处理属性的能力,而另外一个是beans下面的BeanMetadataElement,字面翻译这个接口就是bean的元数据元素,它可以获得bean的配置定义的一个元素。在XML文件中来说,就是会持有一个bean标签。
认识了BeanFactory和BeanDefinition,一个是IOC的核心工厂接口,一个是IOC的bean定义接口,上章提到说我们无法让BeanFactory持有一个Map<String,Object>来完成bean工厂的功能,是因为spring的初始化是可以控制的,可以到用的时候才将bean实例化供开发者使用,除非我们将bean的lazy-init属性设置为true,初始化bean工厂时采用延迟加载。
          知道了上述两个接口,我相信不少人甚至不看源码都已经猜到spring是如何做的了。没错,就是让bean工厂持有一个Map<String,BeanDefinition>,这样就可以在任何时候我们想用哪个bean,取到它的bean定义,我们就可以创造出一个新鲜的实例。
          接口当然不可能持有这样一个对象,那么这个对象一定是在BeanFactory的某个实现类或者抽象实现类当中所持有的,来看DefaultListableBeanFactory。
DefaultListableBeanFactory 类是很重要的一个类;
其源码中持有一个beanDefinitionMap来存储配置中读入的各个bean,其getBean的各种重载方法就是用来从map中读取数据,然后通过一系列复杂的判断返回或实例化成对象返回真正的Bean。这个是后续的事,现在先不看。
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
看到这个Map,应该就大体清楚了一切,当然, 下面从代码的角度,看看Spring是如何实现将配置实始化成BeanFactory的
1.从FileSystemXmlApplicationContext类开始,我记录下主要方法,将源码的脉络记录下来。其构造方法中通过传入的配置文档路径生成Bean容器,其中执行refresh()方法
refresh()方法具体实现在 AbstractApplicationContext中,当前类是继承下来的,Spring的继承体系很复杂,但是通过继承关系及类图就可以找到,refresh源码如下:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
 
prepareRefresh();
 
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 
prepareBeanFactory(beanFactory);
try {
 
postProcessBeanFactory(beanFactory);
 
invokeBeanFactoryPostProcessors(beanFactory);
 
registerBeanPostProcessors(beanFactory);
 
initMessageSource();
 
initApplicationEventMulticaster();
 
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
 
finally {
resetCommonCaches();
}
}
}
继续跟进看obtainFreshBeanFactory()方法,具体实现还在 AbstractApplicationContext类中,获取更新过的BeanFactory,那应该在这个方法里面会将原来容器里的bean按新的配置更新,然后返回BeanFactory;
如下:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
AbstractRefreshableApplicationContext类中的refreshBeanFactory(); 更新beanFactory然后获取beanFactory(),接下来看如何更新容器的;
在其子类 AbstractRefreshableApplicationContext中是具体实现,如下:
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);
}
}
如果存在beanFactory,先销毁,然后再清空引用;然后createBeanFactory();创建一个beanFactory,之后进行一些设置并加载BeanDefinitions(),现在看来最重要的应该就是如何加载BeanDefinition了,先看一下,是具体是创建的哪个BeanFactory类的实例,然后再细细分晰各个方法;
createBeanFactory方法里返回的是DefaultListableBeanFactory的实例,那然后就是对这个实例的填充了;
beanFactory.setSerializationId(getId());方法是将这个实例PUT入静态MAP中;
loadBeanDefinitions的具体实现在 AbstractXmlApplicationContext.java中,此类是FileSystemXmlApplicationContext的父类,下面是源码:
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);
}
跟踪代码,最后在XmlBeanDefinitionReader类中实现了loadBeanDefinitions(EncodedResource encodedResource)是加载实现;
 
最终在如下方法实现加载beanDefinition
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
 
具本类DefaultBeanDefinitionDocumentReader.java实现了registerBeanDefinitions方法,其中调用了
doRegisterBeanDefinitions(root)方法,进行加载DOM文件;可后续的方法中可以加载自定义及默认的文件;
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
节点元素分成四类:import,alias,bean,nested_beans
 
在BeanDefinitionReaderUtils.java类中,是对BEAN配置的加载,如下我们看下是怎么实现的;
 
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
 
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
 
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
 
registry其实是之前的beanFactory实例;
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
实现将beanName对应的BeanDefinition注册进Beanfactory;
此方法的实现在DefaultListableBeanFactory.java中,及这个beanFactory实现了很多重要的方法;
最后的bean放在如下的MAP中;
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
所以如上所分析,一个应用中,可以有多个beanFactory,一个beanFactory中有一个MAP来存入BEAN,beanName不能重复,且可配置可否覆盖bean;

猜你喜欢

转载自xiangyuan.iteye.com/blog/2390349