本章内容:
Bean组件、Context组件解析BeanFactory的创建初始化Bean实例之前的操作Spring Bean 的创建是典型的工厂模式, 它的顶级接口是BeanFactory。
Bean工厂的类层次关系图:
4个接口,共同定义了Bean 的集合、Bean 之间的关系和Bean 的行为。
Bean定义的类层次关系图: Bean 的定义完整地描述了在Spring 的配置文件中你定义的< /bean>节点中所有的信息,包括各种子节点。
ApplicationContext 继承了BeanFactory,这也说明了Spring 容 器中运行的主体对象是Bean 。另外ApplicationContext 继承了Re sourceLoader 接口,使得ApplicationContext 可以访问到任何外部资源。
ApplicationContext 必须要完成以下几件事情:
标识一个应用环境。利用Bean Factory 创建Bean 对象。保存对象关系表。能够捕获各种事件。refresh方法(位于AbstractApplicationContext),整个Spring Bean加载的核心,构建了Bean关系网(也就是Bean Factory)
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 为刷新准备新的context prepareRefresh(); // 刷新所有BeanFactory子容器 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //创建BeanFactory prepareBeanFactory(beanFactory); try { //设置BeanFactoy的后置处理 postProcessBeanFactory(beanFactory); //调用BeanFactory的后处理器,这些后处理器是在Bean定义中向容器注册的 invokeBeanFactoryPostProcessors(beanFactory); //注册Bean的后处理器,在Bean创建过程中调用。 registerBeanPostProcessors(beanFactory); //对上下文中的消息源进行初始化 initMessageSource(); //初始化上下文中的事件机制 initApplicationEventMulticaster(); //初始化其他的特殊Bean onRefresh(); //检查监听Bean并且将这些Bean向容器注册 registerListeners(); //实例化所有的(non-lazy-init)单件 finishBeanFactoryInitialization(beanFactory); //发布容器事件,结束Refresh过程 finishRefresh(); } catch (BeansException ex) { //为防止Bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件Bean destroyBeans(); // 重置 'active'标志 cancelRefresh(ex); throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }首先refresh()方法有几点是值得我们学习的:
1、方法是加锁的,这么做的原因是避免多线程同时刷新Spring上下文
2、尽管加锁可以看到是针对整个方法体的,但是没有在方法前加synchronized关键字,而使用了对象锁startUpShutdownMonitor,这样做有两个好处:
(1)refresh()方法和close()方法都使用了startUpShutdownMonitor对象锁加锁,这就保证了在调用refresh()方法的时候无法调用close()方法,反之亦然,避免了冲突
(2)另外一个好处不在这个方法中体现,但是提一下,使用对象锁可以减小了同步的范围,只对不能并发的代码块进行加锁,提高了整体代码运行的效率
3、方法里面使用了每个子方法定义了整个refresh()方法的流程,使得整个方法流程清晰易懂。
refresh方法主要包含了以下步骤: ( 1 )构建BeanFactory ,以便于产生所需的”演员” 。 ( 2 )注册可能感兴趣的事件。 ( 3 )创建Bean 实例对象。 ( 4 )触发被监听的事件。
创建和配置BeanFactory的核心代码(在obtainFreshBeanFactory()方法中被调用,位于AbstractRefreshableApplicationContext类中)
protected final void refreshBeanFactory() throws BeansException { //这里判断,如果已经建立了BeanFactory,则销毁并关闭该BeanFactory if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } //这里是创建并设置持有的DefaultListableBeanFactor的地方同时调用 //loadBeanDefinitions再载入BeanDefinition的信息 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 XML document for " + getDisplayName(), ex); } } //这就是在上下文中创建DefaultListableBeanFactory的地方,而getInternalParentBeanFactory() //的具体实现可以 //参看AbstractApplicationContext中的实现,会根据容器已有的双亲IoC容器的信息来生成 // DefaultListableBeanFactory的双亲IoC容器 protected DefaultListableBeanFactory createBeanFactory() { return new DefaultListableBeanFactory(getInternalParentBeanFactory()); }BeanDefinition的载入和解析(以AbstractXmlApplicationContext为例)
//这里是使用BeanDefinitionReader载入Bean定义的地方,因为允许有多种载入方式,虽然用得 //最多的是XML定义的形式,这里通过一个抽象函数把具体的实现委托给子类来完成 protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException, BeansException; //以AbstractXmlApplicationContext为例 //这里是实现loadBeanDefinitions的地方 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException { //创建XmlBeanDefinitionReader,并通过回调设置到BeanFactory中去,创建BeanFactory //的过程可以参考上文对编程式使用IoC容器的相关分析,这里和前面一样,使用的也是 DefaultListableBeanFactory XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinition Reader(beanFactory); //这里设置XmlBeanDefinitionReader,为XmlBeanDefinitionReader配 //ResourceLoader,因为DefaultResourceLoader是父类,所以this可以直接被使用 beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); //这是启动Bean定义信息载入的过程 initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }接着就是loadBeanDefinitions调用的地方,首先得到BeanDefinition信息的Resource定位,然后直接调用 XmlBeanDefinitionReader来读取,具体的载入过程是委托给BeanDefinitionReader完成的。因为这里的BeanDefinition是通过XML文件定义的,所以这里使用XmlBeanDefinitionReader来载入BeanDefinition到容器中。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { String[] configLocations = getConfigLocations(); if (configLocations != null) { for (String configLocation : configLocations) { reader.loadBeanDefinitions(configLocation); } } }getConfigLocations会先去找父类中定义的默认配置文件,如果没有,则调用实现类中的getDefaultConfigLocations()方法
protected String[] getDefaultConfigLocations() { if (getNamespace() != null) { return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX}; } else { return new String[] {DEFAULT_CONFIG_LOCATION}; } }上面的DEFAULT_CONFIG_LOCATION就是/WEB-INF/applicationContext.xml
public int loadBeanDefinitions(String location, Set actualResources) throws BeanDefinitionStoreException { //这里取得 ResourceLoader,使用的是DefaultResourceLoader ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } //这里对Resource的路径模式进行解析,比如我们设定的各种Ant格式的路径定义,得到需要的 //Resource集合,这些Resource集合指向我们已经定义好的BeanDefinition信息,可以是多个文件 if (resourceLoader instanceof ResourcePatternResolver) { try { //调用DefaultResourceLoader的getResource完成具体的Resource定位 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 { // 调用DefaultResourceLoader的getResource完成具体的Resource定位 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; } }Resource的定位
//对于取得Resource的具体过程,我们可以看看DefaultResourceLoader是怎样完成的 public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); //这里处理带有classpath标识的Resource if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_ URL_PREFIX.length()), getClassLoader()); } else { try { // 这里处理URL标识的Resource定位 URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { //如果既不是classpath,也不是URL标识的Resource定位,则把getResource的 //重任交给getResourceByPath,这个方法是一个protected方法,默认的实现是得到 //一个ClassPathContextResource,这个方法常常会用子类来实现 return getResourceByPath(location); } } }getResourceByPath会被子类FileSystemXmlApplicationContext实现,这个方法返回的是一个 FileSystemResource对象,通过这个对象,Spring可以进行相关的I/O操作,完成BeanDefinition的定位。
这个过程是在registerBeanDefinitions(doc, resource)中完成的。具体的过程是由BeanDefinitionDocumentReader来完成的,这个registerBeanDefinition还对载入的Bean的数量进行了统计。
具体的Spring BeanDefinition的解析是在BeanDefinitionParserDelegate(是一个类)中完成的。这个类里包含了对各种Spring Bean定义规则的处理。解析完成以后,会把解析结果放到BeanDefinition对象中并设置到BeanDefinitionHolder中去。
beanClass、description、lazyInit这些属性都是在配置bean时经常碰到的,都集中在这里。这个BeanDefinition是IoC容器体系中非常重要的核心数据结构。通过解析以后,这些数据已经做好在IoC容器里大显身手的准备了。
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); //这里只读取定义的<bean>中设置的class名字,然后载入到BeanDefinition中去,只是做个 //记录,并不涉及对象的实例化过程,对象的实例化实际上是在依赖注入时完成的 String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } //这里生成需要的BeanDefinition对象,为Bean定义信息的载入做准备 AbstractBeanDefinition bd = createBeanDefinition(className, parent); //这里对当前的Bean元素进行属性解析,并设置description的信息 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); //从名字可以清楚地看到,这里是对各种<bean>元素的信息进行解析的地方 parseMetaElements(ele, bd); parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); //解析<bean>的构造函数设置 parseConstructorArgElements(ele, bd); //解析<bean>的property设置 parsePropertyElements(ele, bd); parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } //下面这些异常是在配置Bean出现问题时经常会看到的,原来是在这里抛出的这些检查是在 //createBeanDefinition时进行的,会检查Bean的class设置是否正确,比如这个类是否能找到 catch (ClassNotFoundException ex) { error("Bean class [" + className + "] not found", ele, ex); } catch (NoClassDefFoundError err) { error("Class that bean class [" + className + "] depends on not found", ele, err); } catch (Throwable ex) { error("Unexpected failure during bean definition parsing", ele, ex); } finally { this.parseState.pop(); } return null; }BeanDefinition在IoC容器中的注册 在DefaultListableBeanFactory中,是通过一个HashMap来持有载入的BeanDefinition的,这个HashMap的定义在DefaultListableBeanFactory中可以看到,如下所示。
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);有用到synchronized关键字,当有Bean正在创建,对beanDefinitionMap加锁,再进行bean定义写入
完成了BeanDefinition的注册,就完成了IoC容器的初始化过程。此时,在使用的IoC容器DefaultListableBeanFactory中已经建立了整个Bean的配置信息,而且这些BeanDefinition已经可以被容器使用了,它们都在beanDefinitionMap里被检索和使用。容器的作用就是对这些信息进行处理和维护。这些信息是容器建立依赖反转的基础,有了这些基础数据,下面我们看一下在IoC容器中,依赖注入是怎样完成的。
PrepareBeanFactory方法:
如果自定义的bean中没有名为”systemProperties”和”systemEnvironment”的Bean,则注册两个Bean,Key为”systemProperties”和”systemEnvironment”,Value为Map,这两个Bean就是一些系统配置和系统环境信息。
invokeBeanFactoryPostProcessors方法:
这个是整个Spring流程中非常重要的一部分,是Spring留给用户的一个非常有用的扩展点,BeanPostProcessor接口针对的是每个Bean初始化前后做的操作而BeanFactoryPostProcessor接口针对的是所有Bean实例化前的操作,注意用词,初始化只是实例化的一部分,表示的是调用Bean的初始化方法,BeanFactoryPostProcessor接口方法调用时机是任意一个自定义的Bean被反射生成出来前。
通常用法是在BeaFactory完成依赖注入后做一些后续处理工作。(http://blog.csdn.net/wubai250/article/details/8239921)
有一些选项在设定好后通常就不会去变更,而有一些选项可能得随时调整,这时候如果能提供一个更简洁的设定,提供一些常用选项在其中随时更改,这样的程序在使用时会更有弹性一种情况是,XML定义档中定义了一些低权限程序使用人员可以设定的选项,然而高权限的程序管理员可以透过属性文件的设定,来推翻低权限程序使用人员的设定,以完成高权限管理的统一性。 List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); List<String> orderedPostProcessorNames = new ArrayList<String>(); List<String> nonOrderedPostProcessorNames = new ArrayList<String>(); for (String ppName : postProcessorNames) { if (processedBeans.contains(ppName)) { // skip - already processed in first phase above } else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); } else { nonOrderedPostProcessorNames.add(ppName); } }这里分出了三个List,表示开发者可以自定义BeanFactoryPostProcessor的调用顺序,具体为调用顺序为:
如果BeanFactoryPostProcessor实现了PriorityOrdered接口(PriorityOrdered接口是Ordered的子接口,没有自己的接口方法定义,只是做一个标记,表示调用优先级高于Ordered接口的子接口),是优先级最高的调用,调用顺序是按照接口方法getOrder()的实现,对返回的int值从小到大进行排序,进行调用如果BeanFactoryPostProcessor实现了Ordered接口,是优先级次高的调用,将在所有实现PriorityOrdered接口的BeanFactoryPostProcessor调用完毕之后,依据getOrder()的实现对返回的int值从小到大排序,进行调用不实现Ordered接口的BeanFactoryPostProcessor在上面的BeanFactoryPostProcessor调用全部完毕之后进行调用,调用顺序就是Bean定义的顺序registerBeanPostProcessors方法
整体代码思路和invokeBeanFactoryPostProcessors方法类似,但是这里不会调用BeanPostProcessor接口的方法,而是把每一个BeanPostProcessor接口实现类实例化出来并按照顺序放入一个List中,到时候按顺序进行调用。
具体代码思路可以参考invokeBeanFactoryPostProcessors,这里就根据代码总结一下BeanPostProcessor接口的调用顺序:
优先调用PriorityOrdered接口的子接口,调用顺序依照接口方法getOrder的返回值从小到大排序其次调用Ordered接口的子接口,调用顺序依照接口方法getOrder的返回值从小到大排序接着按照BeanPostProcessor实现类在配置文件中定义的顺序进行调用最后调用MergedBeanDefinitionPostProcessor接口的实现Bean,同样按照在配置文件中定义的顺序进行调用initMessageSource方法
initMessageSource方法用于初始化MessageSource,MessageSource是Spring定义的用于实现访问国际化的接口
initApplicationEventMulticaster方法
初始化上下文事件广播器
onRefresh方法
一个模板方法,重写它的作用是添加特殊上下文刷新的工作,在特殊Bean的初始化时、初始化之前被调用。
把loc 容器比作一个箱子,在这个箱子里有若干个球的模子,可以用这些模子来造很多种不同的球,还有一个造这些球模的机器,这个机器可以产生球模。那么它们的对应关系就是BeanFactory ,即那个造球模的机器;球模就是Bean ,而球模造出来的球就是Bean 的实例。前面所说的几个扩展点又在什么地方呢? BeanFactoryPostProcessor 对应到当造球模被造出来时, 此时你将有机会对其做出适当的修正,也就是它可以帮你修改球模。 而InitializingBean 和DisposableBean 是在球模造球的开始和结束阶段,你可以完成一些预备和扫尾工作。 BeanPostProcessor 可以让你对球模造出来的球做出适当的修正。最后还有一个FactoryBean ,它可是一个神奇的球模。这个球模不是预先就定型的,而是由你来确定它的形状。既然你可以确定这个球模型的形状,那么它造出来的球肯定就是你想要的球了,这样在这个箱子里面可以发现所有你想要的球。