首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Spring代码分析(Bean调用,事宜,AOP)(一)

2012-11-06 
Spring代码分析(Bean调用,事务,AOP)(一)首先要说的是,这篇文章是基于我的另外一篇文章(郁闷系列)让我郁闷

Spring代码分析(Bean调用,事务,AOP)(一)
首先要说的是,这篇文章是基于我的另外一篇文章(郁闷系列)让我郁闷的Spring事务回滚及对其它AOP操作的影响而来的。

在调试了半天后,终于无法忍受时间的流逝以及精神上的折磨(一次又一次的失败),把Spring的源码导进项目工程里,开始调试之路,下面结合Spring的源码说一下Spring事务的运行机理以及如果定义了其他AOP(Spring事务本身就是AOP的around实现)的实现方式。

首先给出一段配置代码:

<aop:config><aop:pointcut id="logPointcut" expression="execution(* *..*Service.save*(..)) || execution(* *..*Service.update*(..)) || execution(* *..*Service.delete*(..))"/> <aop:advisor pointcut="execution(* *..*Service.*(..))" advice-ref="txAdvice" order="2"/><aop:advisor pointcut="execution(* *..*Service.save*(..)) || execution(* *..*Service.update*(..)) || execution(* *..*Service.delete*(..))" advice-ref="logAfterAdvice"/><aop:aspect id="logAspect" ref="logInterceptor" order="1" >          <aop:after-throwing              pointcut-ref="logPointcut"                method="serviceIntercept" />      </aop:aspect></aop:config>

上面的一段是aop的配置代码,其实很简单,第一个aop配置的切入点是Service层以save,update,delete开始的方法,作用是记录日志,当然了,你不想记录的时候,也是可以的,后面会有叙述。第二个aop的advisor就不用说了,事务的配置,第三个是异常拦截并记录日志的aop配置。针对上面的aop配置,我们就假设这里有一个部门的Service:

public interface DepartmentService{public void saveDepartment(departmentVO vo, LogVO LogVO);}

DepartmentService的实现类我们就不写了,另外,我们假设在Action层(无论是struts或者是Webwork或者其他的MVC框架)来调用这个Service里的方法来保存部门的信息,下面我们来看一下整个过程Spring内部是如何运作的。

众所周知的,Spring对接口是使用jdk动态代理的,当我们从Action里调用Service里方法时,Spring会去执行DepartmentService的动态代理类JdkDynamicAopProxy,关于动态代理方面的知识,最近到处都是,都快审美疲劳了,看来什么都可以流行啊,如果那位不是很明白,可以网上搜索一下。ok,继续,看里面的invoke方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {.......// Get the interception chain for this method.List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// Check whether we have any advice. If we don't, we can fallback on direct// reflective invocation of the target, and avoid creating a MethodInvocation.if (chain.isEmpty()) {// We can skip creating a MethodInvocation: just invoke the target directly// Note that the final invoker must be an InvokerInterceptor so we know it does// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);}else {// We need to create a method invocation...invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);// Proceed to the joinpoint through the interceptor chain.retVal = invocation.proceed();}.........}
从上面这段代码可以看出,getInterceptorsAndDynamicInterceptionAdvice是得到作用于要拦截方法(saveDepartment)上的所有的拦截器,并封装成一个List列表,而下面紧接会做一判断,如果没有配置拦截器,则不会创建一个方法调用,而直接调用目标代码执行,而如果有作用于改方法上的拦截器,则创建一个拦截方法调用,即
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
实际上代码
retVal = invocation.proceed();
中调用的方法proceed()即为ReflectiveMethodInvocation里的方法,我们看一下这个方法有什么作用:

public Object proceed() throws Throwable {//We start with an index of -1 and increment early.if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();}Object interceptorOrInterceptionAdvice =    this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {// Evaluate dynamic method matcher here: static part will already have// been evaluated and found to match.InterceptorAndDynamicMethodMatcher dm =    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {return dm.interceptor.invoke(this);}else {// Dynamic matching failed.// Skip this interceptor and invoke the next in the chain.return proceed();}}else {// It's an interceptor, so we just invoke it: The pointcut will have// been evaluated statically before this object was constructed.return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}}

稍微一了解,就能看出,这是一个AOP拦截器的匹配操作,如果匹配上,则进行调用操作,通过该方法就能够得到所有配置的aop中那些是真正作用于该方法,另外注意的是代码:return proceed();明显的递归操作,会递归调用所有符合条件的拦截器操作。

仍旧以我们的代码为例,按照我们上面配置的,首先会是事务的拦截器被调用,即TransactionInterceptor,看此类的invoke方法:

public Object invoke(final MethodInvocation invocation) throws Throwable {// Work out the target class: may be <code>null</code>.// The TransactionAttributeSource should be passed the target class// as well as the method, which may be from an interface.Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);// If the transaction attribute is null, the method is non-transactional.final TransactionAttribute txAttr =getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);final String joinpointIdentification = methodIdentification(invocation.getMethod());if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {// Standard transaction demarcation with getTransaction and commit/rollback calls.TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);Object retVal = null;try {// This is an around advice: Invoke the next interceptor in the chain.// This will normally result in a target object being invoked.retVal = invocation.proceed();}catch (Throwable ex) {// target invocation exceptioncompleteTransactionAfterThrowing(txInfo, ex);throw ex;}finally {cleanupTransactionInfo(txInfo);}commitTransactionAfterReturning(txInfo);return retVal;}.....}

这个方法就是使用AOP进行声明式事务的实现代码,这里需要说明的是,首先是createTransactionIfNecessary方法,这个方法看名字就知道,在必要的情况下创建事务,我们进入这个方法会看到这样的一行代码:
status = getTransactionManager().getTransaction(txAttr);
这行代码说明从事务管理器得到一个事务,这里其实说来简单,其实深入的看一下代码还是蛮复杂的,假设我们项目使用的Spring+hibernate,那么自然我们使用Hibernate的事务,下面我们会就代码说明如何取的一个Hibernate事务,首先我们看AbstractPlatformTransactionManager的

getTransaction方法,这里提醒一下,如果你想弄懂Spring的事务的话,请先把这个方法里的代码搞明白,下面我们在这个方法里用中文将一些代码的含义标识出来:

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {Object transaction = doGetTransaction();// Cache debug flag to avoid repeated checks.boolean debugEnabled = logger.isDebugEnabled();if (debugEnabled) {logger.debug("Using transaction object [" + transaction + "]");}if (definition == null) {// Use defaults if no transaction definition given.definition = new DefaultTransactionDefinition();}if (isExistingTransaction(transaction)) {//这里判断当前是否存在事务// Existing transaction found -> check propagation behavior to find out how to behave.//在下面方法里,由于当前存在事务,那么需要判断当前拦截的方法在Spring配置文件中配置的propagation的值//如果是Never的话,则会抛出异常,因为当前已经存在事务//如果是REQUIRES_NEW,则会挂起当前事务,重新开启一个新事务//如果是NESTED,则创建一个savepoint,以便事务回滚的时候,可以回滚到该savepointreturn handleExistingTransaction(definition, transaction, debugEnabled);}// Check definition settings for new transaction.if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {//判断事务的失效时间(如果设置的话,默认是-1,不失效)throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());}// No existing transaction found -> check propagation behavior to find out how to proceed.//没有事务存在,则检查propagation,目前基本都是使用的默认值,即REQUIREDif (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");}//如果下面条件满足,则新建一个事务else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||    definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {Object suspendedResources = suspend(null);if (debugEnabled) {logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);}doBegin(transaction, definition);//新建一个事务boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);}else {// Create "empty" transaction: no actual transaction, but potentially synchronization.boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);return newTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);}}
实际上AbstractPlatformTransactionManager类是个平台事务的抽象类,如果你使用的是Hibernate,那么取事务的时候得到就是Hiberante的事务,即通过

HibernateTransactionManager里doBegin方法得到一个Hibernate事务。看HibernateTransactionManager里doBegin方法里的一段代码:

// Register transaction timeout.int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {if (hibernateSetTimeoutAvailable) {// Use Hibernate's own transaction timeout mechanism on Hibernate 3.1// Applies to all statements, also to inserts, updates and deletes!hibTx = session.getTransaction();hibTx.setTimeout(timeout);hibTx.begin();}else {// Use Spring query timeouts driven by SessionHolder on Hibernate 3.0// Only applies to Hibernate queries, not to insert/update/delete statements.hibTx = session.beginTransaction();txObject.getSessionHolder().setTimeoutInSeconds(timeout);}}else {// Open a plain Hibernate transaction without specified timeout.hibTx = session.beginTransaction();}
是不是很熟悉啊,相信用过Hiberante的大侠,对session.beginTransaction();再熟悉不过了。至于启动事务,然后将该事务设置到一个session holder,可以看一下此方法下面的代码。

ok,至此,我们成功的开启一个事务,那么让我们回到TransactionInterceptor,既然事务已经开启,而事务aop又是一个around advice,那么下面就会调用目标代码执行:

retVal = invocation.proceed();


目标代码执行的过程中,如果没有异常抛出,则会提交事务:
commitTransactionAfterReturning(txInfo);
如果有异常抛出,则会根据情况来处理事务:
completeTransactionAfterThrowing(txInfo, ex);此方法里实现的即为我们经常提到的,如果是runtime异常事务会回滚,如果不是,则事务仍然会提交,看代码:

protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {if (txInfo != null && txInfo.hasTransaction()) {if (logger.isDebugEnabled()) {logger.debug("Completing transaction for [" + txInfo.getJoinpointIdentification() +"] after exception: " + ex);}if (txInfo.transactionAttribute.rollbackOn(ex)) {try {this.transactionManager.rollback(txInfo.getTransactionStatus());}catch (RuntimeException ex2) {logger.error("Application exception overridden by rollback exception", ex);throw ex2;}catch (Error err) {logger.error("Application exception overridden by rollback error", ex);throw err;}}else {// We don't roll back on this exception.// Will still roll back if TransactionStatus.isRollbackOnly() is true.try {this.transactionManager.commit(txInfo.getTransactionStatus());}catch (RuntimeException ex2) {logger.error("Application exception overridden by commit exception", ex);throw ex2;}catch (Error err) {logger.error("Application exception overridden by commit error", ex);throw err;}}}}


热点排行