,在Spring中,IOC容器的重要地位我们就不多说了,对于Spring的使用者而言,IOC容器实际上是什么呢?我们可以说BeanFactory就是我们看到的IoC容器,当然了Spring为我们准备了许多种IoC容器来使用,这样可以方便我们从不同的层面,不同的资源位置,不同的形式的定义信息来建立我们需要的IoC容器。
在Spring中,最基本的IOC容器接口是BeanFactory - 这个接口为具体的IOC容器的实现作了最基本的功能规定 - 不管怎么着,作为IOC容器,这些接口你必须要满足应用程序的最基本要求:
public interface BeanFactory {
//这里是对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
//如果需要得到工厂本身,需要转义
String FACTORY_BEAN_PREFIX = "&";
//这里根据bean的名字,在IOC容器中得到bean实例,这个IOC容器就是一个大的抽象工厂。
Object getBean(String name) throws BeansException;
//这里根据bean的名字和Class类型来得到bean实例,和上面的方法不同在于它会抛出异常:如果根据名字取得的bean实例的Class类型和需要的不同的话。
Object getBean(String name, Class requiredType) throws BeansException;
//这里提供对bean的检索,看看是否在IOC容器有这个名字的bean
boolean containsBean(String name);
//这里根据bean名字得到bean实例,并同时判断这个bean是不是单件
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
//这里对得到bean实例的Class类型
Class getType(String name) throws NoSuchBeanDefinitionException;
//这里得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name);
}
在BeanFactory里只对IOC容器的基本行为作了定义,根本不关心你的bean是怎样定义怎样加载的 - 就像我们只关心从这个工厂里我们得到到什么产品对象,至于工厂是怎么生产这些对象的,这个基本的接口不关心这些。如果要关心工厂是怎样产生对象的,应用程序需要使用具体的IOC容器实现- 当然你可以自己根据这个BeanFactory来实现自己的IOC容器,但这个没有必要,因为Spring已经为我们准备好了一系列工厂来让我们使用。比如XmlBeanFactory就是针对最基础的BeanFactory的IOC容器的实现 - 这个实现使用xml来定义IOC容器中的bean。
Spring提供了一个BeanFactory的基本实现,XmlBeanFactory同样的通过使用模板模式来得到对IOC容器的抽象- AbstractBeanFactory,DefaultListableBeanFactory这些抽象类为其提供模板服务。其中通过resource 接口来抽象bean定义数据,对Xml定义文件的解析通过委托给XmlBeanDefinitionReader来完成。下面我们根据书上的例子,简单的 演示IOC容器的创建过程:
ClassPathResource res = new ClassPathResource("beans.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(res);这些代码演示了以下几个步骤:
- 创建IOC配置文件的抽象资源
- 创建一个BeanFactory
- 把读取配置信息的BeanDefinitionReader,这里是XmlBeanDefinitionReader配置给BeanFactory
- 从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成,这样完成整个载入bean定义的过程。我们的IoC容器就建立起来了。在BeanFactory的源代码中我们可以看到:
public class XmlBeanFactory extends DefaultListableBeanFactory {
//这里为容器定义了一个默认使用的bean定义读取器
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
//在初始化函数中使用读取器来对资源进行读取,得到bean定义信息。
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
我们在后面会看到读取器读取资源和注册bean定义信息的整个过程,基本上是和上下文的处理是一样的,从这里我们可以看到上下文和XmlBeanFactory这两种IOC容器的区别,BeanFactory往往不具备对资源定义的能力,而上下文可以自己完成资源定义,从这个角度上看上下文更好用一些。
仔细分析Spring BeanFactory的结构,我们来看看在BeanFactory基础上扩展出的ApplicationContext - 我们最常使用的上下文。除了具备BeanFactory的全部能力,上下文为应用程序又增添了许多便利:
- 可以支持不同的信息源,我们看到ApplicationContext扩展了MessageSource
- 访问资源 , 体现在对ResourceLoader和Resource的支持上面,这样我们可以从不同地方得到bean定义资源
- 支持应用事件,继承了接口ApplicationEventPublisher,这样在上下文中引入了事件机制而BeanFactory是没有的。
ApplicationContext允许上下文嵌套 - 通过保持父上下文可以维持一个上下文体系 - 这个体系我们在以后对Web容器中的上下文环境的分析中可以清楚地看到。对于bean的查找可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的Spring应用提供了一个共享的bean定义环境。这个我们在分析Web容器中的上下文环境时也能看到。
ApplicationContext提供IoC容器的主要接口,在其体系中有许多抽象子类比如AbstractApplicationContext为具体的BeanFactory的实现,比如FileSystemXmlApplicationContext和ClassPathXmlApplicationContext提供上下文的模板,使得他们只需要关心具体的资源定位问题。当应用程序代码实例化FileSystemXmlApplicationContext的时候,得到IoC容器的一种具体表现 - ApplicationContext,从而应用程序通过ApplicationContext来管理对bean的操作。
BeanFactory是一个接口,在实际应用中我们一般使用ApplicationContext来使用IOC容器,它们也是IOC容器展现给应用开发者的使用接口。对应用程序开发者来说,可以认为BeanFactory和ApplicationFactory在不同的使用层面上代表了SPRING提供的IOC容器服务。
下面我们具体看看通过FileSystemXmlApplicationContext是怎样建立起IOC容器的, 显而易见我们可以通过new来得到IoC容器:
ApplicationContext = new FileSystemXmlApplicationContext(xmlPath);
调用的是它初始化代码:
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
this.configLocations = configLocations;
if (refresh) {
//这里是IoC容器的初始化过程,其初始化过程的大致步骤由AbstractApplicationContext来定义
refresh(); }
}
refresh的模板在AbstractApplicationContext:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
synchronized (this.activeMonitor) {
this.active = true;
}
// 这里需要子类来协助完成资源位置定义,bean载入和向IOC容器注册的过程
refreshBeanFactory();
............
}
这个方法包含了整个BeanFactory初始化的过程,对于特定的FileSystemXmlBeanFactory,我们看到定位资源位置由refreshBeanFactory()来实现:
在AbstractXmlApplicationContext中定义了对资源的读取过程,默认由XmlBeanDefinitionReader来读取:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
// 这里使用XMLBeanDefinitionReader来载入bean定义信息的XML文件
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //这里配置reader的环境,其中ResourceLoader是我们用来定位bean定义信息资源位置的
///因为上下文本身实现了ResourceLoader接口,所以可以直接把上下文作为ResourceLoader传递给XmlBeanDefinitionReader
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
initBeanDefinitionReader(beanDefinitionReader);
//这里转到定义好的XmlBeanDefinitionReader中对载入bean信息进行处理
loadBeanDefinitions(beanDefinitionReader); }
转到beanDefinitionReader中进行处理:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
//调用XmlBeanDefinitionReader来载入bean定义信息。
reader.loadBeanDefinitions(configResources); }
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations); }
}
而在作为其抽象父类的AbstractBeanDefinitionReader中来定义载入过程:
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
//这里得到当前定义的ResourceLoader,默认的我们使用DefaultResourceLoader
ResourceLoader resourceLoader = getResourceLoader();
.........//如果没有找到我们需要的ResourceLoader,直接抛出异常
if (resourceLoader instanceof ResourcePatternResolver) {
// 这里处理我们在定义位置时使用的各种pattern,需要ResourcePatternResolver来完成
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int loadCount = loadBeanDefinitions(resources); return loadCount;
}
........
}
else {
// 这里通过ResourceLoader来完成位置定位
Resource resource = resourceLoader.getResource(location);
// 这里已经把一个位置定义转化为Resource接口,可以供XmlBeanDefinitionReader来使用了
int loadCount = loadBeanDefinitions(resource); return loadCount;
}
}
当我们通过ResourceLoader来载入资源,别忘了了我们的GenericApplicationContext也实现了ResourceLoader接口:
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
public Resource getResource(String location) {
//这里调用当前的loader也就是DefaultResourceLoader来完成载入
if (this.resourceLoader != null) {
return this.resourceLoader.getResource(location); }
return super.getResource(location);
}
.......
}
而我们的FileSystemXmlApplicationContext就是一个DefaultResourceLoader - GenericApplicationContext()通过DefaultResourceLoader:
public Resource getResource(String location) {
//如果是类路径的方式,那需要使用ClassPathResource来得到bean文件的资源对象
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// 如果是URL方式,使用UrlResource作为bean文件的资源对象
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// 如果都不是,那我们只能委托给子类由子类来决定使用什么样的资源对象了
return getResourceByPath(location); }
}
}
我们的FileSystemXmlApplicationContext本身就是是DefaultResourceLoader的实现类,他实现了以下的接口:
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
//这里使用文件系统资源对象来定义bean文件
return new FileSystemResource(path); }
这样代码就回到了FileSystemXmlApplicationContext中来,他提供了FileSystemResource来完成从文件系统得到配置文件的资源定义。这样,就可以从文件系统路径上对IOC配置文件进行加载 - 当然我们可以按照这个逻辑从任何地方加载,在Spring中我们看到它提供的各种资源抽象,比如ClassPathResource,URLResource,FileSystemResource等来供我们使用。上面我们看到的是定位Resource的一个过程,而这只是加载过程的一部分 - 我们回到AbstractBeanDefinitionReaderz中的loadDefinitions(resource)来看看得到代表bean文件的资源定义以后的载入过程,默认的我们使用XmlBeanDefinitionReader:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
.......
try {
//这里通过Resource得到InputStream的IO流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//从InputStream中得到XML的解析源
InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//这里是具体的解析和注册过程
return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); }
finally {
//关闭从Resource中得到的IO流
inputStream.close();
}
}
.........
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
//通过解析得到DOM,然后完成bean在IOC容器中的注册,解析过程和注册过程是分开的。
Document doc = this.documentLoader.loadDocument( inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware); return registerBeanDefinitions(doc, resource); }
.......
}
我们看到先把定义文件解析为DOM对象,这里的Document参数是从用户定义的xml文件解析出来的DOM得到的,Spring把解析和注册过程分开了 - 这个document只是对xml文件解析的结果,还要结合Spring中对bean定义的规则来生成BeanDefinition,而这个过程是在parseBeanDefinitions(root, delegate)里完成的。
root的信息主要是用户定义的bean信息,而delegate中这对这些信息结合Spring的规则进行解析和注册。
然后进行具体的注册过程:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 这里定义解析器,使用XmlBeanDefinitionParser来解析xml方式的bean定义文件 - 现在的版本不用这个解析器了,使用的是XmlBeanDefinitionReader
if (this.parserClass != null) {
XmlBeanDefinitionParser parser =
(XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass);
return parser.registerBeanDefinitions(this, doc, resource);
}
// 具体的注册过程,首先得到XmlBeanDefinitionReader,来处理xml的bean定义文件
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getBeanFactory().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getBeanFactory().getBeanDefinitionCount() - countBefore;
}
具体的在BeanDefinitionDocumentReader中完成对,下面是一个简要的注册过程来完成bean定义文件的解析和IOC容器中bean的初始化
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())) {
//这里得到xml文件的子节点,比如各个bean节点
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)) {
//这里是解析过程的调用,对缺省的元素进行分析比如bean元素
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
} else {
delegate.parseCustomElement(root);
}
}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//这里对元素Import进行处理
if (DomUtils.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (DomUtils.nodeNameEquals(ele, ALIAS_ELEMENT)) {
String name = ele.getAttribute(NAME_ATTRIBUTE);
String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
getReaderContext().getReader().getBeanFactory().registerAlias(name, alias);
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
//这里对我们最熟悉的bean元素进行处理
else if (DomUtils.nodeNameEquals(ele, BEAN_ELEMENT)) {
//委托给BeanDefinitionParserDelegate来完成对bean元素的处理,这个类包含了具体的bean解析的过程。
//把解析bean文件得到的信息放到BeanDefinition里,他是bean信息的主要载体,也是IOC容器的管理对象。
//这个delegate完成了从DOM中取得信息,然后转换成BeanDefinition的过程,同时把BeanDefinition放到BeanDefinitionHolder中等到向IOC容器注册时使用
//BeanDefinition是Spring对bean信息的内部表示,我们看到一直都是以这个名字为主线。
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
// 这里是向IOC容器注册,实际上是放到IOC容器的一个map里
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); // 这里向IOC容器发送事件,表示解析和注册完成。
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
}
我们看到在parseBeanDefinition中对具体bean元素的解析式交给
BeanDefinitionParserDelegate来完成的。下面我们看看解析完的bean是怎样在IOC容器中注册的:
在BeanDefinitionReaderUtils调用的是:
public static void registerBeanDefinition(
BeanDefinitionHolder bdHolder, BeanDefinitionRegistry beanFactory) throws BeansException {
// 这里得到需要注册bean的名字;
String beanName = bdHolder.getBeanName();
//这是调用IOC来注册的bean的过程,需要得到BeanDefinition
beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition()); // 别名也是可以通过IOC容器和bean联系起来的进行注册
String[] aliases = bdHolder.getAliases();
if (aliases != null) {
for (int i = 0; i < aliases.length; i++) {
beanFactory.registerAlias(beanName, aliases[i]);
}
}
}
我们看看XmlBeanFactory中的注册实现:
//---------------------------------------------------------------------
// 这里是IOC容器对BeanDefinitionRegistry接口的实现
//---------------------------------------------------------------------
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
.....//这里省略了对BeanDefinition的验证过程
//先看看在容器里是不是已经有了同名的bean,如果有抛出异常。
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
...........
}
else {
//把bean的名字加到IOC容器中去
this.beanDefinitionNames.add(beanName); }
//这里把bean的名字和Bean定义联系起来放到一个HashMap中去,IOC容器通过这个Map来维护容器里的Bean定义信息。
this.beanDefinitionMap.put(beanName, beanDefinition); removeSingleton(beanName);
}
这样就完成了Bean定义在IOC容器中的注册,就可被IOC容器进行管理和使用了。
从上面的代码来看,我们总结一下IOC容器初始化的基本步骤:
- 初始化的入口在容器实现中的refresh()调用来完成
- 对bean定义载入IOC容器使用的方法是loadBeanDefinition,其中的大致过程如下:通过ResourceLoader来完成资源文件位置的定位,DefaultResourceLoader是默认的实现,同时上下文本身就给出了ResourceLoader的实现,可以从类路径,文件系统,URL等方式来定为资源位置。如果是XmlBeanFactory作为IOC容器,那么需要为它指定bean定义的资源,也就是说bean定义文件时通过抽象成Resource来被IOC容器处理的,容器通过BeanDefinitionReader来完成定义信息的解析和Bean信息的注册,往往使用的是XmlBeanDefinitionReader来解析bean的xml定义文件 - 实际的处理过程是委托给BeanDefinitionParserDelegate来完成的,从而得到bean的定义信息,这些信息在Spring中使用BeanDefinition对象来表示 - 这个名字可以让我们想到loadBeanDefinition,RegisterBeanDefinition这些相关的方法 - 他们都是为处理BeanDefinitin服务的,IoC容器解析得到BeanDefinition以后,需要把它在IOC容器中注册,这由IOC实现BeanDefinitionRegistry接口来实现。注册过程就是在IOC容器内部维护的一个HashMap来保存得到的BeanDefinition的过程。这个HashMap是IoC容器持有bean信息的场所,以后对bean的操作都是围绕这个HashMap来实现的。
- 然后我们就可以通过BeanFactory和ApplicationContext来享受到Spring IOC的服务了.
在使用IOC容器的时候,我们注意到除了少量粘合代码,绝大多数以正确IoC风格编写的应用程序代码完全不用关心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在一起。基本的策略是把工厂放到已知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。Spring本身提供了对声明式载入web应用程序用法的应用程序上下文,并将其存储在ServletContext中的框架实现。具体可以参见以后的文章。
在使用Spring IOC容器的时候我们还需要区别两个概念:
Beanfactory 和Factory bean,其中BeanFactory指的是IOC容器的编程抽象,比如ApplicationContext, XmlBeanFactory等,这些都是IOC容器的具体表现,需要使用什么样的容器由客户决定但Spring为我们提供了丰富的选择。而FactoryBean只是一个可以在IOC容器中被管理的一个bean,是对各种处理过程和资源使用的抽象,Factory bean在需要时产生另一个对象,而不返回FactoryBean本身,我们可以把它看成是一个抽象工厂,对它的调用返回的是工厂生产的产品。所有的Factory bean都实现特殊的org.springframework.beans.factory.FactoryBean接口,当使用容器中factory bean的时候,该容器不会返回factory bean本身,而是返回其生成的对象。Spring包括了大部分的通用资源和服务访问抽象的Factory bean的实现,其中包括:
对JNDI查询的处理,对代理对象的处理,对事务性代理的处理,对RMI代理的处理等,这些我们都可以看成是具体的工厂,看成是SPRING为我们建立好的工厂。也就是说Spring通过使用抽象工厂模式为我们准备了一系列工厂来生产一些特定的对象,免除我们手工重复的工作,我们要使用时只需要在IOC容器里配置好就能很方便的使用了。
现在我们来看看在Spring的事件机制,Spring中有3个标准事件,ContextRefreshEvent, ContextCloseEvent,RequestHandledEvent他们通过ApplicationEvent接口,同样的如果需要自定义时间 也只需要实现ApplicationEvent接口,参照ContextCloseEvent的实现可以定制自己的事件实现:
public class ContextClosedEvent extends ApplicationEvent {
public ContextClosedEvent(ApplicationContext source) { super(source); } public ApplicationContext getApplicationContext() {
return (ApplicationContext) getSource();
}
}
可 以通过显现ApplicationEventPublishAware接口,将事件发布器耦合到ApplicationContext这样可以使用 ApplicationContext框架来传递和消费消息,然后在ApplicationContext中配置好bean就可以了,在消费消息的过程中,接受者通过实现ApplicationListener接收消息。
比如可以直接使用Spring的ScheduleTimerTask和TimerFactoryBean作为定时器定时产生消息,具体可以参见《Spring框架高级编程》。
TimerFactoryBean是一个工厂bean,对其中的ScheduleTimerTask进行处理后输出,参考ScheduleTimerTask的实现发现它最后调用的是jre的TimerTask:
public void setRunnable(Runnable timerTask) {
this.timerTask = new DelegatingTimerTask(timerTask); }
在书中给出了一个定时发送消息的例子,当然可以可以通过定时器作其他的动作,有两种方法:
1.定义MethodInvokingTimerTaskFactoryBean定义要执行的特定bean的特定方法,对需要做什么进行封装定义;
2.定义TimerTask类,通过extends TimerTask来得到,同时对需要做什么进行自定义
然后需要定义具体的定时器参数,通过配置ScheduledTimerTask中的参数和timerTask来完成,以下是它需要定义的具体属性,timerTask是在前面已经定义好的bean
private TimerTask timerTask;
private long delay = 0;
private long period = 0;
private boolean fixedRate = false;
最后,需要在ApplicationContext中注册,需要把ScheduledTimerTask配置到FactoryBean - TimerFactoryBean,这样就由IOC容器来管理定时器了。参照
TimerFactoryBean的属性,可以定制一组定时器。
public class TimerFactoryBean implements FactoryBean, InitializingBean, DisposableBean {
protected final Log logger = LogFactory.getLog(getClass());
private ScheduledTimerTask[] scheduledTimerTasks; private boolean daemon = false;
private Timer timer;
...........
}
如果要发送时间我们只需要在定义好的ScheduledTimerTasks中publish定义好的事件就可以了。具体可以参考书中例子的实现,这里只是结合FactoryBean的原理做一些解释。如果结合事件和定时器机制,我们可以很方便的实现heartbeat(看门狗),书中给出了这个例子,这个例子实际上结合了Spring事件和定时机制的使用两个方面的知识 - 当然了还有IOC容器的知识(任何Spring应用我想都逃不掉IOC的魔爪 :)
下面是对在贴子发表后相关问题的讨论:
Q:Spring对于资源加载,还是不能很优雅的处理,classpath下,WEB-INF下,文件系统下. 跟环境耦合啊!
A:从代码上看,Spring把这些处理都交给具体的ResourceLoader在做,比如ClassPath,FileSystem这些,然后实际的处理都放在那些ClassPathResource,FileSystemResource中去实现具体的加载过程. 而这些处理都不是在通用的框架代码里面做的 - 类似的还有BeanDefinitionReader的处理这些解析和注册的过程。
我想要做到和环境完全的解耦比较困难,因为毕竟IOC容器需要的bean信息得有一个具体的地方存放。
bennyparlo 写道
Q:
代码
- 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);
- }
这里的定义的Document参数对象有何用途?
A:
我觉得这里的Document参数是bean定义xml文件解析出来的DOM,Spring把解析和注册过程分开了 - 这个document只是对xml文件解析的结果,还要结合Spring中对bean定义的规则来生成BeanDefinition,而这个过程是在 parseBeanDefinitions(root, delegate)里完成的。
root的信息主要是用户定义的bean信息,而delegate中这对这些信息结合Spring的规则进行解析。
具体的Spring规则解析是在BeanDefinitionParserDelegate中完成的,这是一个helper类,具体看看它的代码 就会比较清楚,这里就不贴上来了。它的任务是根据用户的配置信息和Spring的bean定义规则来生成BeanDefinition,然后准备注册到 IOC容器中去。
Q:
org.springframework.context.support.AbstractRefreshableApplicationContext 中定义DefaultListableBeanFactory成员,同时又通过父类实现DefaultResourceLoader接口,是否意味 ApplicationContext本身不是容器的概念,但是他携带或者说绑定了1个BeanFactory的实现.同时 ApplicationContext自己本身也是1个Resource资源的解析器?
A:
我想BeanFactory和ApplicationContext还是有区别的,对ApplicationContext,要实现 ResourceLoader,MessageSource, ApplicationEventPublisher等这些附加的上下文功能,而BeanFactory就没有这么复杂,因为它不打算为应用提供一个上下 文环境,它只提供最基本的IOC服务。
ApplicationContext不仅仅是容器的概念,它还提供了其他的服务 - 他当然他本身的核心功能是BeanFactory的功能。这个可以从它持有DefaultListableBeanFactory可以看到,它的IOC容 器功能是通过持有的这个BeanFactory来实现的,另外再加上继承的DefaultResourceLoader,使得它具备了 ResourceLoader的功能。
可以认为ApplicationContext对IOC容器作了进一步的封装,使得应用程序更好使用。
Q:
同意上述的说法
不过还有1点,可能也是BeanFactory实现与ApplicationContext实现比较明显的区别,BeanFactory在容器启动 过程中不会加载所有定义的bean,而ApplicationContext则将加载所有定义的bean和bean所依赖的bean以及他父 context.
这个区别在实现上来看,可以从那些代码中体现??
A:
BeanFactory和ApplicationContext的差别体现在对bean的加载处理上,因为它们的具体实现不尽相同,但是不管是BeanFactory还是ApplicationContext,他们的加载处理都是由BeanDefinitionReader来完成,这个BeanDefinitionReader完成信息的读取,解析后,最后一步向IOC容器的注册是通过向持有的BeanFactory回调来完成的 - 向IOC容器注册需要实现BeanDefinitionRegistry接口。这个接口在DefaultListBeanFactory里被实现,也就是说ApplicationContext是通过DefaultListBeanFactory来完成向自己持有的IOC容器注册的 - 当然这个IOC容器就是DefaultListBeanFactory。
下面再看加载过程,对ApplicationContext中而言,往往在初始化的时候就加载bean,比如在refresh的时候AbstractApplicationContext:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
// 这里使用XMLBeanDefinitionReader来载入bean定义信息的XML文件
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//这里配置reader的环境,其中ResourceLoader是我们用来定位bean定义信息资源位置的
///因为上下文本身实现了ResourceLoader接口,所以可以直接把上下文作为ResourceLoader传递给XmlBeanDefinitionReader
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
initBeanDefinitionReader(beanDefinitionReader);
//这里转到定义好的XmlBeanDefinitionReader中对载入bean信息进行处理
loadBeanDefinitions(beanDefinitionReader);
}
这里就为ApplicationContext指定了XmlBeanDefinitionReader,然后是加载过程 - 我们看到这个XmlBeanDefinitionReader持有一个beanFactory, 这个beanFactory是在refresh中得到的:
protected final void refreshBeanFactory() throws BeansException {
.........
// 这里初始化一个DefaultListBeanFactory并持有,然后把这个beanFactory传到BeanDefinitionReader中让它回调对BeanDefinition在IOC中进行注册。
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
.......
}
而这些步骤往往在XmlBeanFactory中是这样实现的:
public class XmlBeanFactory extends DefaultListableBeanFactory {
//这里定义需要的BeanDefinitionReader,我们看到的初始化函数中的this,就是把一个DefaultListableBeanFactory放到XmlBeanDefinitionReader中让它回调。
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
//在初始化函数中调用BeanDefinitionReader来进行加载。
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
}
这些步骤我们在DefaultListableBeanFactory中是看不到的,所以如果要使用DefaultListableBeanFactory的话,需要编程式的指明BeanDefinitionReader和调用:
ClassPathResource res = new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
如果比较XmlBeanFactory和ApplicationContext,除了XmlBeanFactory不具备那些ResourceLoader和ApplicationEvent这些特定的上下文能力之外,他们持有的IOC容器和加载过程是一样的。
Q:
BeanDefinitionParserDelegate对所做的,是将xml中的定义封装到BeanDefinition实例对象.那对bean的具体初始化操作以及具体的依赖检查等操作,在哪里做了处理?
A:
具体要到BeanDefinitionParserDelegate中去看看,这个BeanDefinitionParserDelegate是根据Spring的bean定义规则生成BeanDefinition的主要地方:
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
//这里从已经解析好的DOM中取得element的属性,确定bean实例化使用的类名。
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE);
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
this.parseState.push(new BeanEntry(beanName));
//这里是根据得到的类名,去实例化bean对象的地方,注意bean对象不等于BeanDefinition,bean对象只是使用简单的反射机制去得到的一个普通JAVA对象,而BeanDefinition还持有了许多定义信息,bean对象是BeanDefinition持有信息的一部分。
AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition(
parent, className, getReaderContext().getReader().getBeanClassLoader());
//下面是许多对Spring定义的bean定义规则的处理,处理结果会反映在BeanDefinition对象中,比如Singleton等各种配置属性
// 最后返回一个BeanDefinition,这是IOC容器操作bean的基本单元,也就是bean在IOC容器中的基本表现。
if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
// Spring 2.0 "scope" attribute
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Specify either 'scope' or 'singleton', not both", ele);
}
}
else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
// Spring 1.x "singleton" attribute
bd.setSingleton(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)));
}
.........
return bd;
.........
}
至于具体的bean的JAVA对象,只是在BeanDefinition中的一个属性,这个对象的生成是在生成BeanDefinition的时候生成的:
public static AbstractBeanDefinition createBeanDefinition(
String parent, String className, ClassLoader classLoader) throws ClassNotFoundException {
//这里确定BeanDefinition的两种类型,RootBeanDefinition或者是ChildBeanDefinition
AbstractBeanDefinition bd = null;
if (parent != null) {
bd = new ChildBeanDefinition(parent);
}
else {
bd = new RootBeanDefinition();
}
if (className != null) {
if (classLoader != null) {
//这里通过定义的class名字和类装载器通过反射机制生成需要的JAVA对象。
//把这个对象保存到BeanDefinition中去,然后返回生成的BeanDefinition
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
这个BeanDefnition持有了和bean相关的所有配置信息,对于依赖检查和DI的实现, 现在还搞不太明白,也就是说Spring IOC容器是怎样管理bean之间的依赖关系的?这个应该是IOC的核心功能了。
- 这个要好好看看代码才行。
Q:
这里的property value只是个从xml中解析来的Element(字符流的封装),具体到了装配的时候,确可以被识别不同类型的对象 (BeanDefinitionValueResolver.resolveValueIfNecessary()),这个包装的过程,在哪里实现的呢?
A:
我想这里是处理对Property的依赖关系解析,比如如果是一个"bean ref",就会生成一个RunTimeReferenceBean,在代码里可以看到:
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
String elementName = (propertyName != null) ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element";
//这里取得这个Element的子元素值
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
if (nl.item(i) instanceof Element) {
Element candidateEle = (Element) nl.item(i);
//下面是对取得的子元素作的具体处理,取得subElement
if (DESCRIPTION_ELEMENT.equals(candidateEle.getTagName())) {
// Keep going: we don't use this value for now.
}
else {
// Child element is what we're looking for.
if (subElement != null && !META_ELEMENT.equals(subElement.getTagName())) {
error(elementName + " must not contain more than one sub-element", ele);
}
subElement = candidateEle;
}
}
}
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute)) && subElement != null) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
//如果这里取得的subElement是Ref bean,生成一个RuntimeBeanReference持有这个bean的名字,但是并不持有ref bean对象。
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
else if (hasValueAttribute) {
return ele.getAttribute(VALUE_ATTRIBUTE);
}
if (subElement == null) {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
}
//这里处理其他类型的属性,比如我们看到的List,Map,Set等等
return parsePropertySubElement(subElement, bd);
}
在这里对定义的依赖关系已经在BeanDefinition里面持有了,但是还没有对依赖进行注入。
在AbstractFactoryBean中的getBean调用createBean
public Object getBean(String name, Class requiredType, final Object[] args) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean = null;
........
// 这里检查是不是在父工厂里这个bean
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
//这里调用父工厂的getBean
return ((AbstractBeanFactory) parentBeanFactory).getBean(nameToLookup, requiredType, args);
}
else if (args == null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
throw new NoSuchBeanDefinitionException(beanName,
"Cannot delegate to parent BeanFactory because it does not supported passed-in arguments");
}
}
this.alreadyCreated.add(beanName);
final RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition(beanName, false);
checkMergedBeanDefinition(mergedBeanDefinition, beanName, args);
//这里调用bean的创建过程,也是依赖注入的地方,依赖解析已经完成放在BeanDefinition里面了
if (mergedBeanDefinition.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
try {
return createBean(beanName, mergedBeanDefinition, args);
}
.........
}
那看来bean的依赖注入是在第一次向容器请求bean的时候完成的。这个createBean和getBean要好好看看。
我们大致可以看到Spring把loadBean和依赖注入分成两个基本的过程,一个是在启动容器的时候完成,建立起一系列 BeanDefinition,这些定义里面同时都包含了对bean依赖关系的描述,不过这里并没有对bean进行实例化,真正实例化的时候是在客户通过 容器使用这些bean的时候 - 也就是getbean的时候。这个时候IOC容器根据需要会建立起一系列bean的实例和完成依赖注入。所以下面我们会详细的分析这个依赖注入的过程。
让我们先简单回顾IOC容器启动时候载入bean定义文件的过程,这个过程是通过BeanDefinitionReader来完成的,其中通过loadBeanDefinition()来对定义文件进行解析和根据Spring定义的bean规则进行处理 - 事实上和Spring定义的bean规则相关的处理是在BeanDefinitionParserDelegate中完成的,完成这个处理需要得到用户定义的bean定义信息,这是在前面已经通过解析已经完成了的,这个处理过程的结果就是得到了一系列的BeanDefinition,这里不但包含了对bean定义信息解析后的表示,同时还把和这个bean相关的依赖信息也保存了下来。那以后这些信息怎样被取得和被使用呢?在完成了这一系列的解析和处理之后,接着需要对对BeanFactoryRegistry的接口做一个回调,把这些BeanDefinition都放到一个HashMap - beanDefinitionMap里面,以后对这些信息的操作通过这个HashMap来完成。这样就完成了IOC启动过程中最重要的部分 - 依赖关系的解析,处理和建立,下面就是使用IOC容器的时候完成的依赖注入的行为。在AbstractBeanDefinition中我们看到有这些和依赖关系处理紧密相关的属性:
public abstract class AbstractBeanDefinition extends AttributeAccessorSupport implements BeanDefinition {
.......
private Object beanClass;
......
//这个属性持有的这个bean的依赖属性,比如各种property的定义信息
private MutablePropertyValues propertyValues;
......
}
很显然这些值都是在BeanDefinitionParserDelegate中设置的:
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
//这里得到定义的类的名字 - 通过属性Class
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE);
}
.........
try {
this.parseState.push(new BeanEntry(beanName));
//这里是生成BeanDefinition的地方,也是生成根据定义的class属性来生成bean JAVA对象的地方
//以前我们已经分析过了,这里生成一个BeanDefinition , 它里面已经持有了生成的bean Java对象。
AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition(
parent, className, getReaderContext().getReader().getBeanClassLoader());
//下面是一系列对beandefinition的属性设置,依据是用户定义的bean定义信息,类似于用户定义的bean定义信息的读入
if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
// Spring 2.0 "scope" attribute
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Specify either 'scope' or 'singleton', not both", ele);
}
}
else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
// Spring 1.x "singleton" attribute
bd.setSingleton(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)));
}
..........
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
//注意这里是对依赖关系,也就是bean对象属性的处理!
parsePropertyElements(ele, bd);
bd.setResourceDescription(getReaderContext().getResource().getDescription());
bd.setSource(extractSource(ele));
return bd;
................
}
在辅助类BeanDefinitionReaderUtils中对bean对象做了原始的创建:
public static AbstractBeanDefinition createBeanDefinition(
String parent, String className, ClassLoader classLoader) throws ClassNotFoundException {
AbstractBeanDefinition bd = null;
if (parent != null) {
bd = new ChildBeanDefinition(parent);
}
else {
bd = new RootBeanDefinition();
}
//这里创建了bean的JAVA对象,同时在beandefinition中持有
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
下面我们分析parsePropertyElements(ele, bd)看看那些属性依赖关系是怎样处理的:
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
//这里取得所有定义的属性定义
NodeList nl = beanEle.getChildNodes();
//很显然,需要一个一个的处理
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && DomUtils.nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);
}
}
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//这里得到属性定义的名字
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
//判断是不是已经有重名的属性定义
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
//这里根据属性的名字对属性的定义进行具体的解析,结果放在PropertyValue对象里面
//这里包含了对各种属性类型的解析,比如List,Map,Set,当然还有reference bean等等,
//这个propertyValue是连接和表示各个bean之间依赖关系的桥梁
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
//最后把这个解析的结果,也就是生成的Property对象放到beanDefinition里面去
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
我们看看具体的对各种属性定义的解析,在parsePropertyValue函数中:
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
String elementName = (propertyName != null) ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element";
// Should only have one child element: ref, value, list, etc.
//这里根据DOM取得需要分析的属性定义
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
if (nl.item(i) instanceof Element) {
Element candidateEle = (Element) nl.item(i);
if (DESCRIPTION_ELEMENT.equals(candidateEle.getTagName())) {
// Keep going: we don't use this value for now.
}
else {
// Child element is what we're looking for.
if (subElement != null && !META_ELEMENT.equals(subElement.getTagName())) {
error(elementName + " must not contain more than one sub-element", ele);
}
subElement = candidateEle;
}
}
}
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute)) && subElement != null) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
if (hasRefAttribute) {
//这里是对ref属性的解析,称称一个RuntimeBeanReference对象,
//这个RuntimeBeanReference很简单,只是保持beanName,toParent,source三个属性信息,对记录依赖关系已经足够了
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
else if (hasValueAttribute) {
return ele.getAttribute(VALUE_ATTRIBUTE);
}
if (subElement == null) {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
}
//这里对各种其他属性值进行处理,比如List,Map,Set等等
return parsePropertySubElement(subElement, bd);
}
比如我们举个例子看看对List属性的解析,也是生成一个对应的List对象,里面记录了相关的定义信息,
public List parseListElement(Element collectionEle, BeanDefinition bd) {
String defaultTypeClassName = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
NodeList nl = collectionEle.getChildNodes();
ManagedList list = new ManagedList(nl.getLength());
list.setSource(extractSource(collectionEle));
list.setMergeEnabled(parseMergeAttribute(collectionEle));
//这里对List的各个子属性进行处理,结果放到这个List,然后返回到beandefinition中以后可以使用。
for (int i = 0; i < nl.getLength(); i++) {
if (nl.item(i) instanceof Element) {
Element ele = (Element) nl.item(i);
list.add(parsePropertySubElement(ele, bd, defaultTypeClassName));
}
}
return list;
}
这样就把相应的属性对象都解析完,而且都放到了beandefinition中去,这个beandefinition是以后容器进行依赖注入和依赖管理的最主要的数据结构。
有了上面的准备,我们可以看看依赖关系是怎样被注入的,我们知道IOC容器在loadBeanDefinition的时候只是完成了bean定义的解析和向IOC容器的注册,这个时候并没有对依赖的实现注入,具体的注入过程是在客户程序向IOC容器要求bean的时候完成的,具体的过程我们在代码中可以看到 - 在AbstractFactoryBean中:
public Object getBean(String name, Class requiredType, final Object[] args) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean = null;
// Eagerly check singleton cache for manually registered singletons.
// 这里先从缓存中去取,处理那些已经被创建过的单件模式的bean,对这种bean的请求不需要重复的去创建
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null) {
if (isSingletonCurrentlyInCreation(beanName)) {
.......
}
else {
.......
}
if (containsBeanDefinition(beanName)) {
RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition(beanName, false);
bean = getObjectForBeanInstance(sharedInstance, name, mergedBeanDefinition);
}
else {
bean = getObjectForBeanInstance(sharedInstance, name, null);
}
}
else {
// Fail if we're already creating this singleton instance:
// We're assumably within a circular reference.
if (isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
//这里检查是否能在当前的工厂中取到我们需要的bean,如果在当前的工厂中取不到,则到父工厂取,如果一直取不到
//那就顺着工厂链一直向上查找
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
// 这里调用父工厂的getbean取需要的bean
// 这里有一个迭代,在父工厂中也会重复这么一个getbean的过程。
if (parentBeanFactory instanceof AbstractBeanFactory) {
// Delegation to parent with args only possible for AbstractBeanFactory.
return ((AbstractBeanFactory) parentBeanFactory).getBean(nameToLookup, requiredType, args);
}
else if (args == null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
throw new NoSuchBeanDefinitionException(beanName,
"Cannot delegate to parent BeanFactory because it does not supported passed-in arguments");
}
}
//把这个已经被要求过的bean记录下来,因为第一次要求bean的时候往往就是依赖被容器对bean进行注入的时候。
this.alreadyCreated.add(beanName);
final RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition(beanName, false);
checkMergedBeanDefinition(mergedBeanDefinition, beanName, args);
// Create bean instance.
//这里是根据beandefinition来创建bean和完成依赖注入的地方。
if (mergedBeanDefinition.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
try {
//注意这个createBean,是创建bean同时完成依赖注入的地方。
return createBean(beanName, mergedBeanDefinition, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, mergedBeanDefinition);
}
//这里是处理prototype类型的bean请求的地方
else if (mergedBeanDefinition.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
//每次请求,直接通过createBean来创建
prototypeInstance = createBean(beanName, mergedBeanDefinition, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, mergedBeanDefinition);
}
........
return bean;
}
具体的bean创建过程和依赖关系的注入在createBean中,这个方法在AbstractAutowireCapableBeanFactory中给出了实现:
protected Object createBean(String beanName, RootBeanDefinition mergedBeanDefinition, Object[] args)
throws BeanCreationException {
// Guarantee initialization of beans that the current one depends on.
// 这里对取得当前bean的所有依赖bean,确定能够取得这些已经被确定的bean,如果没有被创建,那么这个createBean会被这些IOC
// getbean时创建这些bean
if (mergedBeanDefinition.getDependsOn() != null) {
for (int i = 0; i < mergedBeanDefinition.getDependsOn().length; i++) {
getBean(mergedBeanDefinition.getDependsOn()[i]);
}
}
........
// 这里是实例化bean对象的地方,注意这个BeanWrapper类,是对bean操作的主要封装类
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mergedBeanDefinition, args); }
Object bean = instanceWrapper.getWrappedInstance();
......
//这个populate方法,是对已经创建的bean实例进行依赖注入的地方,会使用到在loadBeanDefinition的时候得到的那些propertyValue来对bean进行注入。
if (continueWithPropertyPopulation) {
populateBean(beanName, mergedBeanDefinition, instanceWrapper);
}
//这里完成客户自定义的对bean的一些初始化动作
Object originalBean = bean;
bean = initializeBean(beanName, bean, mergedBeanDefinition);
// Register bean as disposable, and also as dependent on specified "dependsOn" beans.
registerDisposableBeanIfNecessary(beanName, originalBean, mergedBeanDefinition);
return bean;
}
.........
}
我们看看是用的wrapper类是怎样被创建的,这个对象被创建的时候已经为我们的bean创建了JAVA对象:
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mergedBeanDefinition, Object[] args)
throws BeansException {
BeanWrapper instanceWrapper = null;
//这里使用BeanWrapper的不同创建方法
if (mergedBeanDefinition.getFactoryMethodName() != null) {
instanceWrapper = instantiateUsingFactoryMethod(beanName, mergedBeanDefinition, args);
}
else if (mergedBeanDefinition.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
mergedBeanDefinition.hasConstructorArgumentValues() ) {
instanceWrapper = autowireConstructor(beanName, mergedBeanDefinition);
}
else {
// No special handling: simply use no-arg constructor.
// 这是最正常的创建,使用Spring默认的BeanWrapper实现BeanWrapperImp
instanceWrapper = instantiateBean(beanName, mergedBeanDefinition);
}
return instanceWrapper;
}
protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mergedBeanDefinition)
throws BeansException {
//这里是创建bean对象的地方,同时把这个bean对象放到BeanWrapper中去
Object beanInstance = getInstantiationStrategy().instantiate(mergedBeanDefinition, beanName, this);
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
我们注意到在这里定义的实例化的策略是
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
一般而言可以直接实例化也可以通过cglib来完成bean对象的重新实例化,在CglibSubclassingInstantiationStrategy中:
public Object instantiate(
RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
// 这里是重新实例化bean对象的地方,返回后放到BeanWrapper对象当中去
if (beanDefinition.getMethodOverrides().isEmpty()) {
return BeanUtils.instantiateClass(beanDefinition.getBeanClass());
}
else {
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(beanDefinition, beanName, owner);
}
}
这里我们看到对bean的JAVA对象的创建过程,如果没有什么依赖关系的话,那主要的bean创建过程已经完成了,但是如果存在依赖关系的话,这些依赖关系还要进行注入,
回到AbstractAutowireCapableBeanFactory中的populate方法,这里是处理bean的依赖注入的地方:
protected void populateBean(String beanName, RootBeanDefinition mergedBeanDefinition, BeanWrapper bw)
throws BeansException {
//首先取得我们在loadBeanDefinition中取得的依赖定义propertyValues
PropertyValues pvs = mergedBeanDefinition.getPropertyValues();
......
checkDependencies(beanName, mergedBeanDefinition, filteredPds, pvs);
//主要地依赖注入处理在这里
applyPropertyValues(beanName, mergedBeanDefinition, bw, pvs);
}
private void applyPropertyValues(
String beanName, RootBeanDefinition mergedBeanDefinition, BeanWrapper bw, PropertyValues pvs)
throws BeansException {
if (pvs == null) {
return;
}
BeanDefinitionValueResolver valueResolver =
new BeanDefinitionValueResolver(this, beanName, mergedBeanDefinition);
// Create a deep copy, resolving any references for values.
// 这里把那些相关的有依赖关系的property内容copy过来
MutablePropertyValues deepCopy = new MutablePropertyValues();
PropertyValue[] pvArray = pvs.getPropertyValues();
for (int i = 0; i < pvArray.length; i++) {
PropertyValue pv = pvArray[i];
//这个队property的resolve过程包含了一个对依赖bean的迭代解析和创建
Object resolvedValue =
valueResolver.resolveValueIfNecessary("bean property '" + pv.getName() + "'", pv.getValue());
deepCopy.addPropertyValue(pvArray[i].getName(), resolvedValue);
}
// 这里把copy过来的propertyValue置入到BeanWrapper中去,这个set其实并不简单,它通过wrapper完成了实际的依赖注入
try {
// Synchronize if custom editors are registered.
// Necessary because PropertyEditors are not thread-safe.
if (!getCustomEditors().isEmpty()) {
synchronized (this) {
bw.setPropertyValues(deepCopy);
}
}
else {
bw.setPropertyValues(deepCopy);
}
}
.........
}
这里有一个迭代的解析和bean依赖的创建,注入:
Object resolvedValue = valueResolver.resolveValueIfNecessary("bean property '" + pv.getName() + "'", pv.getValue());
在BeanDefinitionValueResolver中是这样实现这个resolve的:
public Object resolveValueIfNecessary(String argName, Object value) throws BeansException {
if (value instanceof BeanDefinitionHolder) {
// Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.
BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
return resolveInnerBeanDefinition(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
}
else if (value instanceof BeanDefinition) {
// Resolve plain BeanDefinition, without contained name: use dummy name.
BeanDefinition bd = (BeanDefinition) value;
return resolveInnerBeanDefinition(argName, "(inner bean)", bd);
}
else if (value instanceof RuntimeBeanNameReference) {
String ref = ((RuntimeBeanNameReference) value).getBeanName();
if (!this.beanFactory.containsBean(ref)) {
throw new BeanDefinitionStoreException(
"Invalid bean name '" + ref + "' in bean reference for " + argName);
}
return ref;
}
else if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
}
else if (value instanceof ManagedList) {
// May need to resolve contained runtime references.
return resolveManagedList(argName, (List) value);
}
else if (value instanceof ManagedSet) {
// May need to resolve contained runtime references.
return resolveManagedSet(argName, (Set) value);
}
else if (value instanceof ManagedMap) {
// May need to resolve contained runtime references.
return resolveManagedMap(argName, (Map) value);
}
else if (value instanceof ManagedProperties) {
Properties copy = new Properties();
copy.putAll((Properties) value);
return copy;
}
else if (value instanceof TypedStringValue) {
// Convert value to target type here.
TypedStringValue typedStringValue = (TypedStringValue) value;
try {
Class resolvedTargetType = resolveTargetType(typedStringValue);
return this.beanFactory.doTypeConversionIfNecessary(
this.typeConverter, typedStringValue.getValue(), resolvedTargetType, null);
}
catch (Throwable ex) {
// Improve the message by showing the context.
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Error converting typed String value for " + argName, ex);
}
}
else {
// No need to resolve value...
return value;
}
}
这里可以看到对各种依赖类型的resolve,我们看看怎样解析reference bean的,非常清楚的向IOC容器去请求 - 也许会触发下一层依赖的bean的创建和依赖注入过程:
private Object resolveReference(String argName, RuntimeBeanReference ref) throws BeansException {
..........
try {
//向父工厂请求bean
return this.beanFactory.getParentBeanFactory().getBean(ref.getBeanName());
}
else {
//向自己所在的工厂请求bean
Object bean = this.beanFactory.getBean(ref.getBeanName());
if (this.beanDefinition.isSingleton()) {
this.beanFactory.registerDependentBean(ref.getBeanName(), this.beanName);
}
return bean;
}
}
.........
}
假设我们经过穷推,已经到最后一层的bean的依赖创建和注入,这个具体的注入过程,也就是依赖注入过程要依靠BeanWrapperImp的实现我们回到applyPropertyValues中来,这里是已经迭代对依赖进行完解析的地方,也就是需要对依赖进行注入的地方 - 注意这个token是在wrapper中已经对属性做过处理了:
private void setPropertyValue(PropertyTokenHolder tokens, Object newValue) throws BeansException {
String propertyName = tokens.canonicalName;
if (tokens.keys != null) {
// Apply indexes and map keys: fetch value for all keys but the last one.
PropertyTokenHolder getterTokens = new PropertyTokenHolder();
getterTokens.canonicalName = tokens.canonicalName;
getterTokens.actualName = tokens.actualName;
getterTokens.keys = new String[tokens.keys.length - 1];
System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1);
//这里取得需要的propertyValue, 这个getPropertyValue同样不简单,这个getProperty实际上已经取出了bean对象中的属性引用
//所以下面可以直接把依赖对象注入过去
Object propValue = null;
try {
propValue = getPropertyValue(getterTokens);
}
........
// 如果根据token取不到propertyValue,直接抛出异常
String key = tokens.keys[tokens.keys.length - 1];
if (propValue == null) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
"Cannot access indexed value in property referenced " +
"in indexed property path '" + propertyName + "': returned null");
} //这里处理属性是List的注入
else if (propValue.getClass().isArray()) {
.....
Array.set(propValue, Integer.parseInt(key), convertedValue);
.......
} //这里处理对List的注入
else if (propValue instanceof List) {
........
List list = (List) propValue;
........
if (index < list.size()) {
list.set(index, convertedValue);
}
else if (index >= list.size()) {
for (int i = list.size(); i < index; i++) {
try {
list.add(null);
}
}
list.add(convertedValue);
.......
} //这里处理对Map的注入
else if (propValue instanceof Map) {
......
Map map = (Map) propValue;
......
map.put(convertedMapKey, convertedMapValue);
}
........
}
//这里是通过对一般属性进行注入的地方
else {
PropertyDescriptor pd = getPropertyDescriptorInternal(propertyName);
if (pd == null || pd.getWriteMethod() == null) {
PropertyMatches matches = PropertyMatches.forProperty(propertyName, getRootClass());
throw new NotWritablePropertyException(
getRootClass(), this.nestedPath + propertyName,
matches.buildErrorMessage(), matches.getPossibleMatches());
}
//得到需要的set/get方法
Method readMethod = pd.getReadMethod();
Method writeMethod = pd.getWriteMethod();
Object oldValue = null;
........
try {
Object convertedValue = this.typeConverterDelegate.convertIfNecessary(oldValue, newValue, pd);
//千辛万苦,这里是通过set方法对bean对象的依赖属性进行注入
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(this.object, new Object[] {convertedValue});
...........
}
这里比较重要的是propValue的取得,我们看看getPropertyValue的实现:
private Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException {
....
//这里取得对property的读取方法,然后取得在bean对象中的属性引用
Method readMethod = pd.getReadMethod();
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(this.object, (Object[]) null);
if (tokens.keys != null) {
......
}//这里处理Array属性
else if (value.getClass().isArray()) {
value = Array.get(value, Integer.parseInt(key));
}//这里处理List属性
else if (value instanceof List) {
List list = (List) value;
value = list.get(Integer.parseInt(key));
}//这里处理Set属性
else if (value instanceof Set) {
// Apply index to Iterator in case of a Set.
Set set = (Set) value;
int index = Integer.parseInt(key);
}
......
}//这里处理Map属性
else if (value instanceof Map) {
Map map = (Map) value;
Class mapKeyType = null;
if (JdkVersion.isAtLeastJava15()) {
mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(
pd.getReadMethod(), tokens.keys.length);
}
.....
Object convertedMapKey = this.typeConverterDelegate.convertIfNecessary(
null, null, key, mapKeyType);
// Pass full property name and old value in here, since we want full
// conversion ability for map values.
value = map.get(convertedMapKey);
}
........
return value;
}
这就是整个依赖注入的处理过程,在这个过程中起主要作用的是WrapperImp ,这个Wrapper不是一个简单的对bean对象的封装,因为它需要处理在beanDefinition中的信息来迭代的处理依赖注入。
从上面可以看到两个明显的迭代过程,一个是迭代的在上下文体系中查找需要的bean和创建没有被创建的bean - 根据依赖关系为线索,另一个迭代实在依赖注入的时候,如果依赖没有创建,因为是一个向容器取得bean的过程 - 其中的IOC工厂的getbean方法被迭代的调用,中间又迭代的对需要创建的bean进行了创建和依赖注入,这样根据依赖关系,一层一层的创建和注入直至顶层被要求的bean创建和依赖注入完成 - 这样最后得到一个依赖创建和注入完成的最顶层bean被用来交给客户程序使用。