Java开源笔记:Spring源代码解析

Java开源笔记:Spring源代码解析

夜半待客客不至,闲敲棋子落灯花; 一个好的开源软件代码就像一卷优雅的棋谱,让我们好好享受一下吧!

2007年6月9日星期六

我们看看Spring中的事务处理的代码,使用Spring管理事务有声明式和编程式两种方式,声明式事务处理通过AOP的实现把事物管理代码作为方面封装来横向插入到业务代码中,使得事务管理 代码和业务代码解藕。在这种方式我们结合IoC容器和Spirng已有的FactoryBean来对事务管理进行属性配置,比如传播行为,隔离级别等。其 中最简单的方式就是通过配置TransactionProxyFactoryBean来实现声明式事物;
在整个源代码分析中,我们可以大致可以看到Spring实现声明式事物管理有这么几个部分:
  • 对在上下文中配置的属性的处理,这里涉及的类是TransactionAttributeSourceAdvisor,这是一个通知器,用它来对属性值进行处理,属性信息放在TransactionAttribute中来使用,而这些属性的处理往往是和对切入点的处理是结合起来的。对属性的处理放在类TransactionAttributeSource中完成。
  • 创建事物的过程,这个过程是委托给具体的事物管理器来创建的,但Spring通过TransactionStatus来传递相关的信息。
  • 对事物的处理通过对相关信息的判断来委托给具体的事物管理器完成。
我们下面看看具体的实现,在TransactionFactoryBean中:
public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean
implements FactoryBean, BeanFactoryAware {
//这里是Spring事务处理而使用的AOP拦截器,中间封装了Spring对事务处理的代码来支持声明式事务处理的实现
private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();

private Pointcut pointcut;

//这里Spring把TransactionManager注入到TransactionInterceptor中去
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionInterceptor.setTransactionManager(transactionManager);
}

//这里把在bean配置文件中读到的事务管理的属性信息注入到TransactionInterceptor中去
public void setTransactionAttributes(Properties transactionAttributes) {
this.transactionInterceptor.setTransactionAttributes(transactionAttributes);
}

.........中间省略了其他一些方法.......

//这里创建Spring AOP对事务处理的Advisor
protected Object createMainInterceptor() {
this.transactionInterceptor.afterPropertiesSet();
if (this.pointcut != null) {
//这里使用默认的通知器
return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);
}
else {
// 使用上面定义好的TransactionInterceptor作为拦截器,同时使用TransactionAttributeSourceAdvisor
return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);
}
}
}
那什么时候Spring的TransactionInterceptor被注入到Spring AOP中成为Advisor中的一部分呢?我们看到在TransactionProxyFactoryBean中,这个方法在IOC初始化bean的时候被执行:
public void afterPropertiesSet() {
.......
//TransactionProxyFactoryBean实际上使用ProxyFactory完成AOP的基本功能。
ProxyFactory proxyFactory = new ProxyFactory();

if (this.preInterceptors != null) {
for (int i = 0; i < this.preInterceptors.length; i++) {
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.preInterceptors[i]));
}
}

//这里是Spring加入通知器的地方
//有两种通知器可以被加入DefaultPointcutAdvisor或者TransactionAttributeSourceAdvisor
//这里把Spring处理声明式事务处理的AOP代码都放到ProxyFactory中去,怎样加入advisor我们可以参考ProxyFactory的父类AdvisedSupport()
//由它来维护一个advice的链表,通过这个链表的增删改来抽象我们对整个通知器配置的增删改操作。
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));

if (this.postInterceptors != null) {
for (int i = 0; i < this.postInterceptors.length; i++) {
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.postInterceptors[i]));
}
}

proxyFactory.copyFrom(this);

//这里创建AOP的目标源
TargetSource targetSource = createTargetSource(this.target);
proxyFactory.setTargetSource(targetSource);

if (this.proxyInterfaces != null) {
proxyFactory.setInterfaces(this.proxyInterfaces);
}
else if (!isProxyTargetClass()) {
proxyFactory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass()));
}

this.proxy = getProxy(proxyFactory);
}

Spring已经定义了一个transctionInterceptor作为拦截器或者AOP advice的实现,在IOC容器中定义的其他属性比如transactionManager和事务管理的属性都会传到已经定义好的TransactionInterceptor那里去进行处理。以上反映了基本的Spring AOP的定义过程,其中pointcut和advice都已经定义好,同时也通过通知器配置到ProxyFactory中去了。
下面让我们回到TransactionProxyFactoryBean中看看
TransactionAttributeSourceAdvisor是怎样定义的,这样我们可以理解具体的属性是怎样起作用,这里我们分析一下类TransactionAttributeSourceAdvisor:
public class TransactionAttributeSourceAdvisor extends AbstractPointcutAdvisor {
//和其他Advisor一样,同样需要定义AOP中的用到的Interceptor和Pointcut
//Interceptor使用传进来的TransactionInterceptor
//而
对于pointcut,这里定义了一个内部类,参见下面的代码
private TransactionInterceptor transactionInterceptor;

private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut();

.........
//定义的PointCut内部类
private class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
.......
//方法匹配的实现,使用了TransactionAttributeSource类
public boolean matches(Method method, Class targetClass) {
TransactionAttributeSource tas = getTransactionAttributeSource();
//这里使用
TransactionAttributeSource来对配置属性进行处理
return (tas != null && tas.getTransactionAttribute(method, targetClass) != null);
}
........省略了equal,hashcode,tostring的代码
}
这里我们看看属性值是怎样被读入的:AbstractFallbackTransactionAttributeSource负责具体的属性读入任务,我们可以有两种读入方式,比如annotation和直接配置.我们下面看看直接配置的读入方式,在Spring中同时对读入的属性值进行了缓存处理,这是一个decorator模式:
public final TransactionAttribute getTransactionAttribute(Method method, Class targetClass) {
//这里先查一下缓存里有没有事务管理的属性配置,如果有从缓存中取得TransactionAttribute
Object cacheKey = getCacheKey(method, targetClass);
Object cached = this.cache.get(cacheKey);
if (cached != null) {
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
return null;
}
else {
return (TransactionAttribute) cached;
}
}
else {
// 这里通过对方法和目标对象的信息来计算事务缓存属性
TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass);
//把得到的事务缓存属性存到缓存中,下次可以直接从缓存中取得。
if (txAtt == null) {
this.cache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
else {
...........
this.cache.put(cacheKey, txAtt);

}
return txAtt;
}
}
别急,基本的处理在computeTransactionAttribute()中:
private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass) {

//这里检测是不是public方法
if(allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}

Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

// First try is the method in the target class.
TransactionAttribute txAtt = findTransactionAttribute(findAllAttributes(specificMethod));
if (txAtt != null) {
return txAtt;
}

// Second try is the transaction attribute on the target class.
txAtt = findTransactionAttribute(findAllAttributes(specificMethod.getDeclaringClass()));
if (txAtt != null) {
return txAtt;
}

if (specificMethod != method) {
// Fallback is to look at the original method.
txAtt = findTransactionAttribute(findAllAttributes(method));
if (txAtt != null) {
return txAtt;
}
// Last fallback is the class of the original method.
return findTransactionAttribute(findAllAttributes(method.getDeclaringClass()));
}
return null;
}
经过一系列的尝试我们可以通过findTransactionAttribute()通过调用findAllAttribute()得到TransactionAttribute的对象,如果返回的是null,这说明该方法不是我们需要事务处理的方法。

在完成把需要的通知器加到ProxyFactory中去的基础上,我们看看具体的看事务处理代码怎样起作用,在TransactionInterceptor中:
public Object invoke(final MethodInvocation invocation) throws Throwable {
//这里得到目标对象
Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);

//这里同样的通过判断是否能够得到TransactionAttribute来决定是否对当前方法进行事务处理,有可能该属性已经被缓存,
//具体可以参考上面对getTransactionAttribute的分析,同样是通过TransactionAttributeSource
final TransactionAttribute txAttr =
getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
final String joinpointIdentification = methodIdentification(invocation.getMethod());

//这里判断我们使用了什么TransactionManager
if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {
// 这里创建事务,同时把创建事务过程中得到的信息放到TransactionInfo中去
TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);
Object retVal = null;
try {
retVal = invocation.proceed();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}

else {
// 使用的是Spring定义的PlatformTransactionManager同时实现了回调接口,我们通过其回调函数完成事务处理,就像我们使用编程式事务处理一样。
try {
Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,
new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
//同样的需要一个TransactonInfo
TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);
try {
return invocation.proceed();
}
.....这里省去了异常处理和事务信息的清理代码
});
...........
}
}
这里面涉及到事务的创建,我们可以在TransactionAspectSupport实现的事务管理代码:
protected TransactionInfo createTransactionIfNecessary(
TransactionAttribute txAttr, final String joinpointIdentification) {

// If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
public String getName() {
return joinpointIdentification;
}
};
}

TransactionStatus status = null;
if (txAttr != null) {
//这里使用了我们定义好的事务配置信息,有事务管理器来创建事务,同时返回TransactionInfo
status = getTransactionManager().getTransaction(txAttr);
}
return prepareTransactionInfo(txAttr, joinpointIdentification, status);
}
首先通过TransactionManager得到需要的事务,事务的创建根据我们定义的事务配置决定,在AbstractTransactionManager中给出一个标准的创建过程,当然创建什么样的事务还是需要具体的PlatformTransactionManager来决定,但这里给出了创建事务的模板:
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
......

if (definition == null) {
//如果事务信息没有被配置,我们使用Spring默认的配置方式
definition = new DefaultTransactionDefinition();
}

if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}

// Check definition settings for new transaction.
//下面就是使用配置信息来创建我们需要的事务;比如传播属性和同步属性等
//最后把创建过程中的信息收集起来放到TransactionStatus中返回;
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}

// No existing transaction found -> check propagation behavior to find out how to behave.
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"Transaction propagation 'mandatory' but no existing transaction found");
}
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
//这里是事务管理器创建事务的地方,并将创建过程中得到的信息放到TransactionStatus中去,包括创建出来的事务
doBegin(transaction, definition);

boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);
}
else {
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return newTransactionStatus(definition, null, false, newSynchronization, debugEnabled, null);
}
}


接着通过调用prepareTransactionInfo完成事务创建的准备,创建过程中得到的信息存储在TransactionInfo对象中进行传递同时把信息和当前线程绑定;
protected TransactionInfo prepareTransactionInfo(
TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {

TransactionInfo txInfo = new TransactionInfo(txAttr, joinpointIdentification);
if (txAttr != null) {
.....
// 同样的需要把在getTransaction中得到的TransactionStatus放到TransactionInfo中来。
txInfo.newTransactionStatus(status);
}
else {
.......
}


// 绑定事务创建信息到当前线程
txInfo.bindToThread();
return txInfo;
}
将创建事务的信息返回,然后看到其他的事务管理代码:
protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
if (txInfo != null && txInfo.hasTransaction()) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking commit for transaction on " + txInfo.getJoinpointIdentification());
}
this.transactionManager.commit(txInfo.getTransactionStatus());
}
}
通过transactionManager对事务进行处理,包括异常抛出和正常的提交事务,具体的事务管理器由用户程序设定。
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.hasTransaction()) {
if (txInfo.transactionAttribute.rollbackOn(ex)) {
......
try {
this.transactionManager.rollback(txInfo.getTransactionStatus());
}
..........
}
else {
.........
try {
this.transactionManager.commit(txInfo.getTransactionStatus());
}
...........
}

protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
if (txInfo != null && txInfo.hasTransaction()) {
......
this.transactionManager.commit(txInfo.getTransactionStatus());

}
}
Spring通过以上代码对transactionManager进行事务处理的过程进行了AOP包装,到这里我们看到为了方便客户实现声明式的事务处理,Spring还是做了许多工作的。如果说使用编程式事务处理,过程其实比较清楚,我们可以参考书中的例子:
TransactionDefinition td = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(td);
try{
......//这里是我们的业务方法
}catch (ApplicationException e) {
transactionManager.rollback(status);
throw e
}
transactionManager.commit(status);
........
我们看到这里选取了默认的事务配置DefaultTransactionDefinition,同时在创建事物的过程中得到TransactionStatus,然后通过直接调用事务管理器的相关方法就能完成事务处理。
声明式事务处理也同样实现了类似的过程,只是因为采用了声明的方法,需要增加对属性的读取处理,并且需要把整个过程整合到Spring AOP框架中和IoC容器中去的过程。
下面我们选取一个具体的transactionManager - DataSourceTransactionManager来看看其中事务处理的实现:
同样的通过使用AbstractPlatformTransactionManager使用模板方法,这些都体现了对具体平台相关的事务管理器操作的封装,比如commit:
public final void commit(TransactionStatus status) throws TransactionException {
......
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
......
processRollback(defStatus);

return;
}
.......
processRollback(defStatus);

......
}

processCommit(defStatus);
}
通过对TransactionStatus的具体状态的判断,来决定具体的事务处理:
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
boolean globalRollbackOnly = false;
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
}
if (status.hasSavepoint()) {
........
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
......
doCommit(status);

}
.........
}
这些模板方法的实现由具体的transactionManager来实现,比如在DataSourceTransactionManager:
protected void doCommit(DefaultTransactionStatus status) {
//这里得到存在TransactionInfo中已经创建好的事务
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();

//这里得到和事务绑定的数据库连接
Connection con = txObject.getConnectionHolder().getConnection();
........
try {
//这里通过数据库连接来提交事务
con.commit();
}
.......
}

protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
//这里通过数据库连接来回滚事务
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
我们看到在DataSourceTransactionManager中最后还是交给connection来实现事务的提交和rollback。整个声明式事务处理是事务处理在Spring AOP中的应用,我们看到了一个很好的使用Spring AOP的例子,在Spring声明式事务处理的源代码中我们可以看到:
1.怎样封装各种不同平台下的事务处理代码
2.怎样读取属性值和结合事务处理代码来完成既定的事务处理策略
3.怎样灵活的使用SpringAOP框架。
如果能够结合前面的Spring AOP的源代码来学习,理解可能会更深刻些。

2007年6月8日星期五

下面我们来看看Spring的AOP的一些相关代码是怎么得到Proxy的,让我们我们先看看AOP和Spring AOP的一些基本概念:
Advice:
通知,制定在连接点做什么,在Sping中,他主要描述Spring围绕方法调用注入的额外的行为,Spring提供的通知类型有:
before advice,AfterReturningAdvice,ThrowAdvice,MethodBeforeAdvice,这些都是Spring AOP定义的接口类,具体的动作实现需要用户程序来完成。
Pointcut:
切点,其决定一个advice应该应用于哪个连接点,也就是需要插入额外处理的地方的集合,例如,被某个advice作为目标的一组方法。Spring pointcut通常意味着标示方法,可以选择一组方法调用作为pointcut,Spring提供了具体的切点来给用户使用,比如正则表达式切点 JdkRegexpMethodPointcut通过正则表达式对方法名进行匹配,其通过使用 AbstractJdkRegexpMethodPointcut中的对MethodMatcher接口的实现来完成pointcut功能:
public final boolean matches(Method method, Class targetClass) {
// TODO use target class here?
String patt = method.getDeclaringClass().getName() + "." + method.getName();
for (int i = 0; i < this.patterns.length; i++) {
// 这里是判断是否和方法名匹配的代码,当然知道true或者false
boolean matched = matches(patt, i);
if (matched) {
for (int j = 0; j < this.excludedPatterns.length; j++) {
boolean excluded = matchesExclusion(patt, j);
if(excluded) {
return false;
}
}
return true;
}
}
return false;
}
在JDKRegexpMethodPointcut中通过JDK中的正则表达式匹配来完成pointcut的最终锁定
protected boolean matches(String pattern, int patternIndex) {
Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);
return matcher.matches();
}
Advisor:
当 我们完成额外完成的动作设计(advice)和动作插入点的设计(pointcut)以后,我们需要一个对象把他们结合起来,这就是通知器 - advisor,定义应该在哪里应用哪个通知。Advisor的实现有:DefaultPointcutAdvisor他有两个属性advice和 pointcut来让我们配置advice和pointcut。
接着我们就可以通过ProxyFactoryBean来配置我们的代理对象和方面 行为,在ProxyFactoryBean中有interceptorNames来配置已经定义好的通知器-advisor,具体的代理实现通过JDK 的Proxy或者CGLIB的技术来完成。我们可以看看具体的代码实现,在ProxyFactoryBean中我们看看怎样得到Proxy:
public Object getObject() throws BeansException {
initializeAdvisorChain();
if (isSingleton()) {
//根据定义需要生成单件的Proxy
return getSingletonInstance();
}
else {
if (this.targetName == null) {
logger.warn("Using non-singleton proxies with singleton targets is often undesirable." +
"Enable prototype proxies by setting the 'targetName' property.");
}
//根据定义需要生成Prototype的Proxy
return newPrototypeInstance();
}
}
我们看看怎样生成单件的Proxy:
private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = freshTargetSource();
if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
// 这里设置Proxy的接口
setInterfaces(ClassUtils.getAllInterfacesForClass(this.targetSource.getTargetClass()));
}
// Eagerly initialize the shared singleton instance.
super.setFrozen(this.freezeProxy);
// 注意这里的方法会使用ProxyFactory来生成我们需要的Proxy
this.singletonInstance = getProxy(createAopProxy());
// We must listen to superclass advice change events to recache the singleton
// instance if necessary.
addListener(this);
}
return this.singletonInstance;
}

ProxyFactoryBean的父类是AdvisedSupport,Spring使用AopProxyFactory接口把AOP代理的实现与框架的其他部分分离开来;在AdvisedSupport中通过这样的方式来得到AopProxy,这里还需要AopProxyFactory的帮助 - 下面我们看到Spring为我们提供了默认的实现可以帮助我们方便的从JDK或者cglib中得到我们想要的:
protected synchronized AopProxy createAopProxy() {
if (!this.isActive) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
而在ProxyConfig中对使用的AopProxyFactory做了定义:
//这个DefaultAopProxyFactory是Spring用来生成AopProxy的地方,
//当然了它包含JDK和Cglib两种实现方式。
private transient AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory();
其中在DefaultAopProxyFactory中是这样生成AopProxy的:
public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException {
if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass() ||
advisedSupport.getProxiedInterfaces().length == 0) {
if (!cglibAvailable) {
throw new AopConfigException(
"Cannot proxy target class because CGLIB2 is not available. " +
"Add CGLIB to the class path or specify proxy interfaces.");
}
// 这里使用Cglib来生成Proxy,如果target不是接口的实现的话
return CglibProxyFactory.createCglibProxy(advisedSupport);
}
else {
// 这里使用JDK来生成Proxy
return new JdkDynamicAopProxy(advisedSupport);
}
}
于是我们就可以看到其中的Proxy可以有JDK或者Cglib来生成,我们看到JdkDynamicAopProxy类和Cglib2AopProxy都实现的是AopProxy的接口,在JDK实现中我们可以看到Proxy是怎样生成的:
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
Class targetClass = this.advised.getTargetSource().getTargetClass();
logger.debug("Creating JDK dynamic proxy" +
(targetClass != null ? " for [" + targetClass.getName() + "]" : ""));
}
Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//这里我们调用JDK Proxy来生成需要的Proxy实例
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
这样用Proxy包装target之后,对其的调用就被Proxy拦截了,ProxyFactoryBean的getObject()方法得到的实际上已经是Proxy了。

2007年6月7日星期四

下面我们对Spring MVC框架代码进行分析,对于webApplicationContext的相关分析可以参见以前的文档,我们这里着重分析Spring Web MVC框架的实现.我们从分析DispatcherServlet入手:
//这里是对DispatcherServlet的初始化方法,根据名字我们很方面的看到对各个Spring MVC主要元素的初始化
protected void initFrameworkServlet() throws ServletException, BeansException {
initMultipartResolver();
initLocaleResolver();
initThemeResolver();
initHandlerMappings();
initHandlerAdapters();
initHandlerExceptionResolvers();
initRequestToViewNameTranslator();
initViewResolvers();
}
看 到注解我们知道,这是DispatcherSerlvet的初始化过程,它是在WebApplicationContext已经存在的情况下进行的,也就 意味着在初始化它的时候,IOC容器应该已经工作了,这也是我们在web.xml中配置Spring的时候,需要把DispatcherServlet的 load-on-startup的属性配置为2的原因。
对于具体的初始化过程,很容易理解,我们拿initHandlerMappings()来看看:
private void initHandlerMappings() throws BeansException {
if (this.detectAllHandlerMappings) {
// 这里找到所有在上下文中定义的HandlerMapping,同时把他们排序
// 因为在同一个上下文中可以有不止一个handlerMapping,所以我们把他们都载入到一个链里进行维护和管理
Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
getWebApplicationContext(), HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList(matchingBeans.values());
// 这里通过order属性来对handlerMapping来在list中排序
Collections.sort(this.handlerMappings, new OrderComparator());
}
}
else {
try {
Object hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}

//如果在上下文中没有定义的话,那么我们使用默认的BeanNameUrlHandlerMapping
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(HandlerMapping.class);
........
}
}
怎样获得上下文环境,可以参见我们前面的对IOC容器在web环境中加载的分析。 DispatcherServlet把定义了的所有HandlerMapping都加载了放在一个List里待以后进行使用,这个链的每一个元素都是一个handlerMapping的配置,而一般每一个handlerMapping可以持有一系列从URL请求到 Spring Controller的映射,比如SimpleUrl
HandlerMaaping中就定义了一个map来持有这一系列的映射关系。
DisptcherServlet通过HandlerMapping使得Web应用程序确定一个执行路径,就像我们在HanderMapping中看到的那样,HandlerMapping只是一个借口:
public interface HandlerMapping {
public static final String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE =
Conventions.getQualifiedAttributeName(HandlerMapping.class, "pathWithinHandlerMapping");
//实际上维护一个HandlerExecutionChain,这是典型的Command的模式的使用,这个执行链里面维护handler和拦截器
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
他的具体实现只需要实现一个接口方法,而这个接口方法返回的是一个HandlerExecutionChain,实际上就是一个执行链,就像在Command模式描述的那样,这个类很简单,就是一个持有一个Interceptor链和一个Controller:
public class HandlerExecutionChain {

private Object handler;

private HandlerInterceptor[] interceptors;

........
}
而这些Handler和Interceptor需要我们定义HandlerMapping的时候配置好,比如对具体的SimpleURLHandlerMapping,他要做的就是根据URL映射的方式注册Handler和Interceptor,自己维护一个放映映射的handlerMap,当需要匹配Http请求的时候需要使用这个表里的信息来得到执行链。这个注册的过程在IOC容器初始化SimpleUrlHandlerMapping的时候就被完成了,这样以后的解析才可以用到map里的映射信息,这里的信息和bean文件的信息是等价的,下面是具体的注册过程:
protected void registerHandlers(Map urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
}
else {
//这里迭代在SimpleUrlHandlerMapping中定义的所有映射元素
Iterator it = urlMap.keySet().iterator();
while (it.hasNext()) {
//这里取得配置的url
String url = (String) it.next();
//这里根据url在bean定义中取得对应的handler
Object handler = urlMap.get(url);
// Prepend with slash if not already present.
if (!url.startsWith("/")) {
url = "/" + url;
}
//这里调用AbstractHandlerMapping中的注册过程
registerHandler(url, handler);
}
}
}
在AbstractMappingHandler中的注册代码:
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
//试图从handlerMap中取handler,看看是否已经存在同样的Url映射关系
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
........
}

//如果是直接用bean名做映射那就直接从容器中取handler
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName)) {
handler = getApplicationContext().getBean(handlerName);
}
}
//或者使用默认的handler.
if (urlPath.equals("/*")) {
setDefaultHandler(handler);
}
else {
//把url和handler的对应关系放到handlerMap中去
this.handlerMap.put(urlPath, handler);
........
}
}
handlerMap是持有的一个HashMap,里面就保存了具体的映射信息:
private final Map handlerMap = new HashMap();
而SimpleUrlHandlerMapping对接口HandlerMapping的实现是这样的,这个getHandler根据在初始化的时候就得到的映射表来生成DispatcherServlet需要的执行链
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//这里根据request中的参数得到其对应的handler,具体处理在AbstractUrlHandlerMapping中
Object handler = getHandlerInternal(request);
//如果找不到对应的,就使用缺省的handler
if (handler == null) {
handler = this.defaultHandler;
}
//如果缺省的也没有,那就没办法了
if (handler == null) {
return null;
}
// 如果handler不是一个具体的handler,那我们还要到上下文中取
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
//生成一个HandlerExecutionChain,其中放了我们匹配上的handler和定义好的拦截器,就像我们在HandlerExecutionChain中看到的那样,它持有一个handler和一个拦截器组。
return new HandlerExecutionChain(handler, this.adaptedInterceptors);
}
我们看看具体的handler查找过程:
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
//这里的HTTP Request传进来的参数进行分析,得到具体的路径信息。
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
.......//下面是根据请求信息的查找
return lookupHandler(lookupPath, request);

}

protected Object lookupHandler(String urlPath, HttpServletRequest request) {
// 如果能够直接能在SimpleUrlHandlerMapping的映射表中找到,那最好。
Object handler = this.handlerMap.get(urlPath);
if (handler == null) {
// 这里使用模式来对map中的所有handler进行匹配,调用了Jre中的Matcher类来完成匹配处理。
String bestPathMatch = null;
for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
String registeredPath = (String) it.next();
if (this.pathMatcher.match(registeredPath, urlPath) &&
(bestPathMatch == null || bestPathMatch.length() <= registeredPath.length())) {
//这里根据匹配路径找到最象的一个
handler = this.handlerMap.get(registeredPath);

bestPathMatch = registeredPath;
}
}

if (handler != null) {
exposePathWithinMapping(this.pathMatcher.extractPathWithinPattern(bestPathMatch, urlPath), request);
}
}
else {
exposePathWithinMapping(urlPath, request);
}
//
return handler;
}
我 们可以看到,总是在handlerMap这个HashMap中找,当然如果直接找到最好,如果找不到,就看看是不是能通过Match Pattern的模式找,我们一定还记得在配置HnaderMapping的时候是可以通过ANT语法进行配置的,其中的处理就在这里。
这样可以清楚地看到整个HandlerMapping的初始化过程 - 同时,我们也看到了一个具体的handler映射是怎样被存储和查找的 - 这里生成一个ExecutionChain来储存我们找到的handler和在定义bean的时候定义的Interceptors.
让我们回到DispatcherServlet,初始化完成以后,实际的对web请求是在doService()方法中处理的,我们知道DispatcherServlet只是一个普通的Servlet:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
.......
//这里把属性信息进行保存
Map attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
logger.debug("Taking snapshot of request attributes before include");
attributesSnapshot = new HashMap();
Enumeration attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DispatcherServlet.class.getName())) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}

// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

try {
//这里使实际的处理入口
doDispatch(request, response);
}
finally {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
我们看到,对于请求的处理实际上是让doDispatch()来完成的 - 这个方法很长,但是过程很简单明了:
protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
//这是从handlerMapping中得到的执行链
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;

........
try {
//我们熟悉的ModelAndView开始出现了。
ModelAndView mv = null;
try {
processedRequest = checkMultipart(request);

// 这是我们得到handler的过程
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}

// 这里取出执行链中的Interceptor进行前处理
if (mappedHandler.getInterceptors() != null) {
for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {
HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
return;
}
interceptorIndex = i;
}
}

//在执行handler之前,用HandlerAdapter先检查一下handler的合法性:是不是按Spring的要求编写的。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

// 这里取出执行链中的Interceptor进行后处理
if (mappedHandler.getInterceptors() != null) {
for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
}
}
}

........

// Did the handler return a view to render?
//这里对视图生成进行处理
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
}
.......
}
我们很清楚的看到和MVC框架紧密相关的代码,比如如何得到和http请求相对应的执行链,怎样执行执行链和怎样把模型数据展现到视图中去。
先看怎样取得Command对象,对我们来说就是Handler - 下面是getHandler的代码:
protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
//在ServletContext取得执行链 - 实际上第一次得到它的时候,我们把它放在ServletContext进行了缓存。
HandlerExecutionChain handler =

(HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
if (handler != null) {
if (!cache) {
request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
}
return handler;
}
//这里的迭代器迭代的时在initHandlerMapping中载入的上下文所有的HandlerMapping
Iterator it = this.handlerMappings.iterator();
while (it.hasNext()) {
HandlerMapping hm = (HandlerMapping) it.next();
.......
//这里是实际取得handler的过程,在每个HandlerMapping中建立的映射表进行检索得到请求对应的handler
handler = hm.getHandler(request);

//然后把handler存到ServletContext中去进行缓存
if (handler != null) {
if (cache) {
request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
}
return handler;
}
}
return null;
}
如果在ServletContext中可以取得handler则直接返回,实际上这个handler是缓冲了上次处理的结果 - 总要有第一次把这个handler放到ServletContext中去:
如 果在ServletContext中找不到handler,那就通过持有的handlerMapping生成一个,我们看到它会迭代当前持有的所有的 handlerMapping,因为可以定义不止一个,他们在定义的时候也可以指定顺序,直到找到第一个,然后返回。先找到一个 handlerMapping,然后通过这个handlerMapping返回一个执行链,里面包含了最终的Handler和我们定义的一连串的 Interceptor。具体的我们可以参考上面的SimpleUrlHandlerMapping的代码分析知道getHandler是怎样得到一个 HandlerExecutionChain的。
得到HandlerExecutionChain以后,我们通过HandlerAdapter对这个Handler的合法性进行判断:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
Iterator it = this.handlerAdapters.iterator();
while (it.hasNext()) {
//同样对持有的所有adapter进行匹配
HandlerAdapter ha = (HandlerAdapter) it.next();
if (ha.supports(handler)) {
return ha;
}
}
........
}
通过判断,我们知道这个handler是不是一个Controller接口的实现,比如对于具体的HandlerAdapter - SimpleControllerHandlerAdapter:
public class SimpleControllerHandlerAdapter implements HandlerAdapter {

public boolean supports(Object handler) {
return (handler instanceof Controller);
}
.......
}
简单的判断一下handler是不是实现了Controller接口。这也体现了一种对配置文件进行验证的机制。
让我们再回到DispatcherServlet看到代码:
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
这个就是对handle的具体调用!相当于Command模式里的Command.execute();理所当然的返回一个ModelAndView,下面就是一个对View进行处理的过程:
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
}
调用的是render方法:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
throws Exception {response.setLocale(locale);

View view = null;
//这里把默认的视图放到ModelAndView中去。
if (!mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}

if (mv.isReference()) {
// 这里对视图名字进行解析
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
.......
}
else {
// 有可能在ModelAndView里已经直接包含了View对象,那我们就直接使用。
view = mv.getView();
........
}

//得到具体的View对象以后,我们用它来生成视图。
view.render(mv.getModelInternal(), request, response);
}
从整个过程我们看到先在ModelAndView中寻找视图的逻辑名,如果找不到那就使用缺省的视图,如果能够找到视图的名字,那就对他进行解析得到 实际的需要使用的视图对象。还有一种可能就是在ModelAndView中已经包含了实际的视图对象,这个视图对象是可以直接使用的。
不管怎样,得到一个视图对象以后,通过调用视图对象的render来完成数据的显示过程,我们可以看看具体的JstlView是怎样实现的,我们在JstlView的抽象父类 AbstractView中找到render方法:
public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
......
// 这里把所有的相关信息都收集到一个Map里
Map mergedModel = new HashMap(this.staticAttributes.size() + (model != null ? model.size() : 0));
mergedModel.putAll(this.staticAttributes);
if (model != null) {
mergedModel.putAll(model);
}

// Expose RequestContext?
if (this.requestContextAttribute != null) {
mergedModel.put(this.requestContextAttribute, createRequestContext(request, mergedModel));
}
//这是实际的展现模型数据到视图的调用。
renderMergedOutputModel(mergedModel, request, response);
}
注 解写的很清楚了,先把所有的数据模型进行整合放到一个Map - mergedModel里,然后调用renderMergedOutputModel();这个renderMergedOutputModel是一个模 板方法,他的实现在InternalResourceView也就是JstlView的父类:
protected void renderMergedOutputModel(
Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {

// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, request);

// Expose helpers as request attributes, if any.
exposeHelpers(request);

// 这里得到InternalResource定义的内部资源路径。
String dispatcherPath = prepareForRendering(request, response);

//这里把请求转发到前面得到的内部资源路径中去。
RequestDispatcher rd = request.getRequestDispatcher(dispatcherPath);

if (rd == null) {
throw new ServletException(
"Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR");
}
.......
}
首 先对模型数据进行处理,exposeModelAsRequestAttributes是在AbstractView中实现的,这个方法把 ModelAndView中的模型数据和其他request数据统统放到ServletContext当中去,这样整个模型数据就通过 ServletContext暴露并得到共享使用了:
protected void exposeModelAsRequestAttributes(Map model, HttpServletRequest request) throws Exception {
Iterator it = model.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
..........
String modelName = (String) entry.getKey();

Object modelValue = entry.getValue();
if (modelValue != null) {
request.setAttribute(modelName, modelValue);
...........
}
else {
request.removeAttribute(modelName);
.......
}
}
}
让我们回到数据处理部分的exposeHelper();这是一个模板方法,其实现在JstlView中实现:
public class JstlView extends InternalResourceView {

private MessageSource jstlAwareMessageSource;


protected void initApplicationContext() {
super.initApplicationContext();
this.jstlAwareMessageSource =
JstlUtils.getJstlAwareMessageSource(getServletContext(), getApplicationContext());
}

protected void exposeHelpers(HttpServletRequest request) throws Exception {
JstlUtils.exposeLocalizationContext(request, this.jstlAwareMessageSource);
}

}
在JstlUtils中包含了对于其他而言jstl特殊的数据处理和设置。
过程是不是很长?我们现在在哪里了?呵呵,我们刚刚完成的事MVC中View的render,对于InternalResourceView的render过程比较简单只是完成一个资源的重定向处理。需要做的就是得到实际view的internalResource路径,然后转发到那个资源中去。怎样得到资源的路径呢通过调用:
protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)
throws Exception {

return getUrl();
}
那这个url在哪里生成呢?我们在View相关的代码中没有找到,实际上,他在ViewRosolve的时候就生成了,在UrlBasedViewResolver中:
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
view.setUrl(getPrefix() + viewName + getSuffix());
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
return view;
}
这里是生成View的地方,自然也把生成的url和其他一些和view相关的属性也配置好了。
那这个ViewResolve是什么时候被调用的呢?哈哈,我们这样又要回到DispatcherServlet中去看看究竟,在DispatcherServlet中:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
throws Exception {

........
View view = null;

// 这里设置视图名为默认的名字
if (!mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}

if (mv.isReference()) {
//这里对视图名进行解析,在解析的过程中根据需要生成实际需要的视图对象。
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
..........
}
......
}
下面是对视图名进行解析的具体过程:
protected View resolveViewName(String viewName, Map model, Locale locale, HttpServletRequest request)
throws Exception {
//我们有可能不止一个视图解析器
for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {
ViewResolver viewResolver = (ViewResolver) it.next();
//这里是视图解析器进行解析并生成视图的过程。
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
这 里调用具体的ViewResolver对视图的名字进行解析 - 除了单纯的解析之外,它还根据我们的要求生成了我们实际需要的视图对象。具体的viewResolver在bean定义文件中进行定义同时在initViewResolver()方法中被初始化到viewResolver变量中,我们看看具体的 InternalResourceViewResolver是怎样对视图名进行处理的并生成V视图对象的:对resolveViewName的调用模板在AbstractCachingViewResolver中,
public View resolveViewName(String viewName, Locale locale) throws Exception {
//如果没有打开缓存设置,那创建需要的视图
if (!isCache()) {
logger.warn("View caching is SWITCHED OFF -- DEVELOPMENT SETTING ONLY: This can severely impair performance");
return createView(viewName, locale);
}
else {
Object cacheKey = getCacheKey(viewName, locale);
// No synchronization, as we can live with occasional double caching.
synchronized (this.viewCache) {
//这里查找缓存里的视图对象
View view = (View) this.viewCache.get(cacheKey);
if (view == null) {
//如果在缓存中没有找到,创建一个并把创建的放到缓存中去
view = createView(viewName, locale);
this.viewCache.put(cacheKey, view);
........
}
return view;
}
}
}
关于这些createView(),loadView(),buildView()的关系,我们看看Eclipse里的call hiearchy
然后我们回到view.render中完成数据的最终对httpResponse的写入,比如在AbstractExcelView中的实现:
protected final void renderMergedOutputModel(
Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
.........
// response.setContentLength(workbook.getBytes().length);
response.setContentType(getContentType());
ServletOutputStream out = response.getOutputStream();
workbook.write(out);
out.flush();
}
这样就和我们前面的分析一致起来了:DispatcherServlet在解析视图名的时候就根据要求生成了视图对象,包括在InternalResourceView中需要使用的url和其他各种和HTTP response相关的属性都会写保持在生成的视图对象中,然后就直接调用视图对象的render来完成数据的展示。
这就是整个Spring Web MVC框架的大致流程,整个MVC流程由DispatcherServlet来控制。MVC的关键过程包括:
配置到handler的映射关系和怎样根据请求参数得到对应的handler,在Spring中,这是由handlerMapping通过执行链来完成的,而具体的映射关系我们在bean定义文件中定义并在HandlerMapping载入上下文的时候就被配置好了。然后DispatcherServlet调用HandlerMapping来得到对应的执行链,最后通过视图来展现模型数据,但我们要注意的是视图对象是在解析视图名的时候生成配置好的。这些作为核心类的HanderMapping,ViewResolver,View,Handler的紧密协作实现了MVC的功能。

2007年6月5日星期二

下面我们看看Spring JDBC相关的实现,
在Spring中,JdbcTemplate是经常被使用的类来帮助用户程序操作数据库,在JdbcTemplate为用户程序提供了许多便利的数据库操作方法,比如查询,更新等,而且在Spring中,也许多类似JdbcTemplate的Template,比如HibernateTemplate等等 - 看来这是Rod.Johnson的惯用手法,一般而言这种Template中都是通过回调函数CallBack类的使用来重新对需要客户订制的行为进行定制,比如使用客户想要用的SQL语句(Spring又不是神仙,它只是做好模板来减少用户程序的工作量,至于具体要做什么要查询删除什么,那还得劳您大驾),一般来说回调函数的用法采用一下这种一个匿名类的方式,比如:
JdbcTemplate = new JdbcTemplate(datasource);
jdbcTemplate.execute(new CallBack(){
public CallbackInterface(){
......
//用户定义的代码或者说Spring替我们实现的代码
}
}
实际在Template的过程中,用户定义的代码就是模板代码的实现,在模板中嵌入客户代码把模板客户化事项客户程序要求的功能。下面让我们具体看看在JdbcTemplate中的代码是怎样具体完成自己的使命的,我们举出JdbcTemplate.execute()为例,这个方法是在JdbcTemplate中被其他方法调用的基本方法之一来执行基本的SQL语句:
public Object execute(ConnectionCallback action) throws DataAccessException {
//这里得到数据库联接
Connection con = DataSourceUtils.getConnection(getDataSource());
try {
Connection conToUse = con;
//有些特殊的数据库需要我们使用特别的方法取得datasource
if (this.nativeJdbcExtractor != null) {
// Extract native JDBC Connection, castable to OracleConnection or the like.
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}
else {
// Create close-suppressing Connection proxy, also preparing returned Statements.
conToUse = createConnectionProxy(con);
}
//这里调用的是传递进来的匿名类的方法,也就是用户程序需要实现CallBack接口的地方。
return action.doInConnection(conToUse);
}
catch (SQLException ex) {
//如果捕捉到数据库异常,把数据库联接释放,同时抛出一个经过Spring转换过的Spring数据库异常,
//我们知道,Spring做了一个有意义的工作是把这些数据库异常统一到自己的异常体系里了。
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);
}
finally {
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
对于JdbcTemplate中给出的其他方法,比如query,update,execute,我们看看query的基本处理:
public Object query(
PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse)
throws DataAccessException {
..........
//这里调用了我们上面看到的execute()基本方法;这里基本上是Spring为我们代劳
return execute(psc, new PreparedStatementCallback() {
public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {
ResultSet rs = null;
try {
if (pss != null) {
pss.setValues(ps);
}
//这里执行的SQL查询
rs = ps.executeQuery();
ResultSet rsToUse = rs;
if (nativeJdbcExtractor != null) {
rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
}
//返回需要的记录集合
return rse.extractData(rsToUse);
}
finally {
JdbcUtils.closeResultSet(rs);
if (pss instanceof ParameterDisposer) {
((ParameterDisposer) pss).cleanupParameters();
}
}
}
});
}

其中同过辅助类DataSourceUtils来对数据库连接进行管理,比如打开和关闭等操作。
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");
//把对数据库连接放到事务管理里面进行管理
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(dataSource.getConnection());
}
return conHolder.getConnection();
}
// Else we either got no holder or an empty thread-bound holder here.

logger.debug("Fetching JDBC Connection from DataSource");
Connection con = dataSource.getConnection();

if (TransactionSynchronizationManager.isSynchronizationActive()) {
logger.debug("Registering transaction synchronization for JDBC Connection");
// Use same Connection for further JDBC actions within the transaction.
// Thread-bound object will get removed by synchronization at transaction completion.
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
holderToUse = new ConnectionHolder(con);
}
else {
holderToUse.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}

return con;
}
那我们实际的DataSource对象是怎样得到的?我们对JdbcTemplate配置DataSource,它是在JdbcTemplate的父类中被定义的:
public abstract class JdbcAccessor implements InitializingBean {

protected final Log logger = LogFactory.getLog(getClass());

/** Used to obtain connections throughout the lifecycle of this object */
private DataSource dataSource;

/** Helper to translate SQL exceptions to DataAccessExceptions */
private SQLExceptionTranslator exceptionTranslator;

private boolean lazyInit = true;

........
}
而对于DataSource, 我们可以通过定义Apache Jakarta Commons DBCP或者C3P0提供的DataSource实现来完成,然后只要让JdbcTemplate保持对它的引用就可以直接使用了。 JdbcTemplate提供了简单查询和更新功能,但是可能需要更高层次的抽象,以及更面向对象的方法来访问数据库。这种功能是由 org.springframework.jdbc.object来提供的,包含了SqlQuery,SqlMappingQuery, SqlUpdate和StoredProcedure类,这些类都是Spring JDBC应用程序的主要类,使用这些类,用户需要为他们配置好一个JdbcTemplate,因为他们在内部使用JdbcTemplate用于数据库访 问。
比如说我们使用MappingSqlQuery来将表数据直接映射到一个对象集合:
1.我们需要建立DataSource和sql语句并建立持有这些对象的MappingSqlQuery对象
2.然后我们需要定义传递的SqlParameter,具体的实现我们在MappingSqlQuery的父类RdbmsOperation中可以找到:
public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException {
if (isCompiled()) {
throw new InvalidDataAccessApiUsageException("Cannot add parameters once query is compiled");
}
this.declaredParameters.add(param);
}
而这个declareParameters定义的是一个:
/** List of SqlParameter objects */
private List declaredParameters = new LinkedList();
在以后compile的过程中我们会使用到他。
3.然后实现MappingSqlQuery的mapRow接口,将具体的ResultSet数据生成我们需要的对象,这是我们迭代使用的方法。1,2,3步实际上为我们定义好了一个操作模板。
4.在应用程序,我们直接调用execute()方法得到我们需要的对象列表,列表中的每一个对象的数据来自于执行SQL语句得到记录集的每一条记录,事实上执行的方法在父类SqlQuery中:
public List executeByNamedParam(Map paramMap, Map context) throws DataAccessException {
validateNamedParameters(paramMap);
Object[] parameters = NamedParameterUtils.buildValueArray(getSql(), paramMap);
RowMapper rowMapper = newRowMapper(parameters, context);
String sqlToUse = NamedParameterUtils.substituteNamedParameters(getSql(), new MapSqlParameterSource(paramMap));
//我们又看到了JdbcTemplate,这里使用JdbcTemplate来完成对数据库的查询操作,所以我们说JdbcTemplate是基本的操作类。
return getJdbcTemplate().query(newPreparedStatementCreator(sqlToUse, parameters), rowMapper);
}
在这里我 们可以看到template模式的精彩应用和对JdbcTemplate的灵活使用。通过使用它,我们免去了手工迭代ResultSet并将其中的数据转 化为对象列表的重复过程。在这里我们只需要定义SQL语句和SqlParameter - 如果需要的话,往往SQL语句就常常能够满足我们的要求了。
Spring还为其他数据库操作提供了许多服务,比如使用SqlUpdate插入和更新数据库,使用UpdatableSqlQuery更新ResultSet,生成主键,调用存储过程等。
书中还给出了对BLOB数据和CLOB数据进行数据库操作的例子:
对BLOB数据的操作通过LobHander来完成,通过调用JdbcTemplate和RDBMS都可以进行操作:
在JdbcTemplate中,具体的调用可以参考书中的例子 - 是通过以下调用起作用的:
public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException {
return execute(new SimplePreparedStatementCreator(sql), action);
}
然后通过对实现PreparedStatementCallback接口的AbstractLobCreatingPreparedStatementCallback的回调函数来完成:
public final Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
LobCreator lobCreator = this.lobHandler.getLobCreator();
try {
setValues(ps, lobCreator);
return new Integer(ps.executeUpdate());
}
finally {
lobCreator.close();
}
}

protected abstract void setValues(PreparedStatement ps, LobCreator lobCreator)
throws SQLException, DataAccessException;
而我们注意到setValues()是一个需要实现的抽象方法,应用程序通过实现setValues来定义自己的操作 - 在setValues中调用lobCreator.setBlobAsBinaryStrem()。让我们看看具体的BLOB操作在LobCreator是怎样完 成的,在DefaultLobCreator中的实现:
public void setBlobAsBinaryStream(
PreparedStatement ps, int paramIndex, InputStream binaryStream, int contentLength)
throws SQLException {
//通过JDBC来完成对BLOB数据的操作,对Oracle,Spring提供了OracleLobHandler来支持BLOB操作。
ps.setBinaryStream(paramIndex, binaryStream, contentLength);
if (logger.isDebugEnabled()) {
logger.debug(binaryStream != null ? "Set binary stream for BLOB with length " + contentLength :
"Set BLOB to null");
}
}
书中还提到关于execute和update方法之间的区别,update方法返回的是受影响的记录数目的 一个计数,并且如果传入参数的话,使用的是java.sql.PreparedStatement,而execute方法总是使用 java.sql.Statement,不接受参数,而且他不返回受影响记录的计数,更适合于创建和丢弃表的语句,而update方法更适合于插入,更新 和删除操作,这也是我们在使用时需要注意的。

2007年6月3日星期日

上面我们分析了IOC容器本身的实现,下面我们看看在典型的web环境中,Spring IOC容器是怎样起作用的。
对于一个Spring激活的J2EE web应用程序,可以通过使用Spring代码声明式的指定在web应用程序启动时载入应用程序上下文 (WebApplicationContext),Spring的ContextLoader是提供这样性能的类,我们可以使用 ContextLoaderServlet或者ContextLoaderListener的启动时载入的Servlet来实例化Spring IOC容器 - 为什么会有两个不同的类来装载它呢,这是因为它们的使用需要区别不同的Servlet容器支持的Serlvet版本。但不管是ContextLoaderSevlet还是 ContextLoaderListener都使用ContextLoader来完成实际的WebApplicationContext的初始化工作。这个ContextLoder就像是Spring Web应用程序在Web容器中的booter。当然这些Servlet的具体使用我们都要借助web容器中的部署描述符来进行相关的定义。
下面我们使用ContextLoaderListener作为载入器作一个详细的分析,首先从中得到ServletContext,然后可以读到配置好的web.xml的中的各个属性值,包括bean配置文件的 位置等,然后ContextLoder实例化WebApplicationContext完成其载入。当 ApplicationContext载入后,他被绑定到web应用程序的ServletContext上。任何需要访问该ApplicationContext的应用程序代码都可以从 WebApplicationContextUtils类通过调用静态方法来得到它:
WebApplicationContext getWebApplicationContext(ServletContext sc)
以Tomcat作为Servlet容器为例,下面是具体的步骤:
1.Tomcat启动时需要从web.xml中读取启动参数,在web.xml中我们需要对ContextLoaderListener进行配置,对于在web应用启动入口是在ContextLoaderListener中的初始化部分;从Spring MVC上看,实际上在web容器中维护了一系列的IOC容器,其中在ContextLoader中载入的IOC容器作为根上下文而存在于ServletContext中。
//这里对根上下文进行初始化。
public void contextInitialized(ServletContextEvent event) {
//这里创建需要的ContextLoader
this.contextLoader = createContextLoader();
//这里使用ContextLoader对根上下文进行载入和初始化
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
通过ContextLoader建立起根AppplicationContext并载入ApplicationContext,我们可以在ContextLoader中看到:
public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
throws IllegalStateException, BeansException {
//这里先看看是不是已经在ServletContext中存在WebApplicationContext,如果有说明前面已经被载入过,或者是配置文件有错误。
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
//直接抛出异常
.........
}

...............
try {
// 这里载入ApplicationContext的父上下文
ApplicationContext parent = loadParentContext(servletContext);

//这里创建根WebApplicationContext作为整个应用的上下文同时把它存到ServletContext中去,注意这里使用的ServletContext的属性值是
//ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,以后都是根据这个属性值来取得根上下文的。
this.context = createWebApplicationContext(servletContext, parent);
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
..........

return this.context;
}
............
}

建立根WebApplicationContext的父上下文使用的是下面的代码,取决于在web.xml中定义的参数:locatorFactorySelector,这是一个可选参数:
protected ApplicationContext loadParentContext(ServletContext servletContext)
throws BeansException {

ApplicationContext parentContext = null;

String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);

if (locatorFactorySelector != null) {
BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
........
//得到根上下文的父上下文的引用
this.parentContextRef = locator.useBeanFactory(parentContextKey);
//这里建立得到根上下文的父上下文
parentContext = (ApplicationContext) this.parentContextRef.getFactory();
}

return parentContext;
}
得到根上下文的父上下文以后,就是根上下文的创建过程:
protected WebApplicationContext createWebApplicationContext(
ServletContext servletContext, ApplicationContext parent) throws BeansException {
//这里需要确定我们载入的根WebApplication的类型,由在web.xml中配置的contextClass中配置的参数可以决定我们需要载入什么样的ApplicationContext,
//如果没有使用默认的。
Class contextClass = determineContextClass(servletContext);
.........
//这里就是WebApplicationContext的创建过程
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
//这里保持对父上下文和ServletContext的引用到根上下文中
wac.setParent(parent);
wac.setServletContext(servletContext);

//这里从web.xml中取得相关的初始化参数
String configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocation != null) {
wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation,
ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
//这里对WebApplicationContext进行初始化,我们又看到了熟悉的refresh调用。
wac.refresh();
return wac;
}
初始化根ApplicationContext后将其存储到SevletContext中去以后,这样就建立了一个全局的关于整个应用的上下文。这个根上下文会被以后的DispatcherServlet初始化自己的时候作为自己ApplicationContext的父上下文。这个在对DispatcherServlet做分析的时候我们可以看看到。

3.完成对ContextLoaderListener的初始化以后,Tomcat开始初始化DispatchServlet,- 还记得我们在web.xml中队载入次序进行了定义。DispatcherServlet会建立自己的ApplicationContext,同时建立这个自己的上下文的时候会从ServletContext中得到根上下文作为父上下文,然后再对自己的上下文进行初始化,并最后存到ServletContext中去供以后检索和使用。
可以从DispatchServlet的父类FrameworkServlet的代码中看到大致的初始化过程,整个ApplicationContext的创建过程和ContextLoder创建的过程相类似:

protected final void initServletBean() throws ServletException, BeansException {
.........
try {
//这里是对上下文的初始化过程。
this.webApplicationContext = initWebApplicationContext();
//在完成对上下文的初始化过程结束后,根据bean配置信息建立MVC框架的各个主要元素
initFrameworkServlet();
}
........
}
对initWebApplicationContext()调用的代码如下:
protected WebApplicationContext initWebApplicationContext() throws BeansException {
//这里调用WebApplicationContextUtils静态类来得到根上下文
WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext());

//创建当前DispatcherServlet的上下文,其上下文种类使用默认的在FrameworkServlet定义好的:DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
WebApplicationContext wac = createWebApplicationContext(parent);
........
if (isPublishContext()) {
//把当前建立的上下文存到ServletContext中去,注意使用的属性名是和当前Servlet名相关的。
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
其中我们看到调用了WebApplicationContextUtils的静态方法得到根ApplicationContext:
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
//很简单,直接从ServletContext中通过属性名得到根上下文
Object attr = sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
.......
return (WebApplicationContext) attr;
}
然后创建DispatcherServlet自己的WebApplicationContext:
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)
throws BeansException {
.......
//这里使用了BeanUtils直接得到WebApplicationContext,ContextClass是前面定义好的DEFAULT_CONTEXT_CLASS =
//XmlWebApplicationContext.class;
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass());

//这里配置父上下文,就是在ContextLoader中建立的根上下文
wac.setParent(parent);

//保留ServletContext的引用和相关的配置信息。
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());

//这里得到ApplicationContext配置文件的位置
if (getContextConfigLocation() != null) {
wac.setConfigLocations(
StringUtils.tokenizeToStringArray(
getContextConfigLocation(), ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

//这里调用ApplicationContext的初始化过程,同样需要使用refresh()
wac.refresh();
return wac;
}

4.然后就是DispatchServlet中对Spring MVC的配置过程,首先对配置文件中的定义元素进行配置 - 请注意这个时候我们的WebApplicationContext已经建立起来了,也意味着DispatcherServlet有自己的定义资源,可以需要从web.xml中读取bean的配置信息,通常我们会使用单独的xml文件来配置MVC中各个要素定义,这里和web容器相关的加载过程实际上已经完成了,下面的处理和普通的Spring应用程序的编写没有什么太大的差别,我们先看看MVC的初始化过程:
protected void initFrameworkServlet() throws ServletException, BeansException {
initMultipartResolver();
initLocaleResolver();
initThemeResolver();
initHandlerMappings();
initHandlerAdapters();
initHandlerExceptionResolvers();
initRequestToViewNameTranslator();
initViewResolvers();
}
5.这样MVC的框架就建立起来了,DispatchServlet对接受到的HTTP Request进行分发处理由doService()完成,具体的MVC处理过程我们在doDispatch()中完成,其中包括使用Command模式建立执行链,显示模型数据等,这些处理我们都可以在DispatcherServlet的代码中看到:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
......
try {
doDispatch(request, response);
}
.......
}
实际的请求分发由doDispatch(request,response)来完成:
protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {
.......
// 这是Spring定义的执行链,里面放了映射关系对应的handler和定义的相关拦截器。
HandlerExecutionChain mappedHandler = null;

......
try {
//我们熟悉的ModelAndView在这里出现了。
ModelAndView mv = null;
try {
processedRequest = checkMultipart(request);

//这里更具request中的参数和映射关系定义决定使用的handler
mappedHandler = getHandler(processedRequest, false);

......
//这里是handler的调用过程,类似于Command模式中的execute.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

.......
//这里将模型数据通过视图进行展现
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
}
........
}
这样具体的MVC模型的实现就由bean配置文件里定义好的view resolver,handler这些类来实现用户代码的功能。
总结上面的过程,我们看到在web容器中,ServletContext可以持有一系列的web上下文,而在整个web上下文中存在一个根上下文来作为其它Servlet上下文的父上下文。这个根上下文是由ContextLoader载入并进行初始化的,对于我们的web应用,DispatcherSerlvet载入并初始化自己的webApplication,这个上下文的父上下文是根上下文,并且我们也能从ServletContext中根据Servlet的名字来检索到我们需要的web上下文,但是根上下文的名字是由Spring唯一确定的。
具体的web请求处理在上下文体系建立完成以后由DispactcherServlet来完成,上面对MVC的运作做了一个大致的描述,下面我们会具体就SpringMVC的框架实现作一个详细的分析。

2007年6月2日星期六

,在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);
这些代码演示了以下几个步骤:
  1. 创建IOC配置文件的抽象资源
  2. 创建一个BeanFactory
  3. 把读取配置信息的BeanDefinitionReader,这里是XmlBeanDefinitionReader配置给BeanFactory
  4. 从定义好的资源位置读入配置信息,具体的解析过程由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:
代码
  1. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  2. this.readerContext = readerContext;
  3. logger.debug("Loading bean definitions");
  4. Element root = doc.getDocumentElement();
  5. BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
  6. preProcessXml(root);
  7. parseBeanDefinitions(root, delegate);
  8. postProcessXml(root);
  9. }

这里的定义的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被用来交给客户程序使用。








博客归档