服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

spring事务处理及其aop框架的内幕


  ***注:非入门者读***
  
  spring框架中成功吸引人的一点就是容器事务的管理,提供了一个轻量级的容器事务处理,针对的对象是普通的java类,使用spring事务管理的话,你可以按照自己的业务把一些相关的方法纳入其事务管理里面,这就避免了程序员在处理事务的过程中繁琐的工作.同时这些也是ejb2.x规范里面吸引人的一点,这在spring里面都很好的提供.虽然在跨容器的事务管理,spring里面并没有提供,但是对于一般的web程序来说,也不需要仅仅为了那些功能而不得不使用ejb.不过,最近jboss的嵌入式的ejb容器也可以做的更小了,也是开源中的选择之一.无论技术是怎样发展的,当前,我们先来研究其中aop实现的方法.
  
  事实上,spring中的事务处理是通过aop思想来实现的,spring aop与aspect j和jboss具有很大的不同,首先,使用spring aop框架的用户要记住的一点是,spring aop针对的是方法层次上的实现,而其他两者对字段也提供了支持.说到spring aop的内幕,其实也不难,对于有接口的类,使用的是java内部类提供的proxy;而对于那些不实现接口的类,使用的是cglib库,动态创建一个子类来实现.
  
  在spring aop中提供了4种处理切入类型:around,before,after,introduction.顾名思义,
  
  1)around是针对具体的某个切入点的方法(比如,现在有个orderbook方法,around的切入类型是就这个方法的内部调用,是通过java的元数据,在运行时通过method.invoke来调用,具有返回值,当发生意外的时候会终止.记住的一点是,返回值.);
  
  2)before是在方法调用前调用(在orderbook方法前调用,但是没有返回值,同时在通常意外情况下,会继续运行下一步方法.记住的一点是没有返回值);
  
  3)after和before刚好相反,没有什么特别的地方.
  
  4)introduction是一个更加特殊的,但功能更加强大的切入类型.比如(你现在有book对象,computer对象,还有几十个这种业务对象,现在你希望在每个这样的对象中都加入一个记录最后修改的时间.但是你又不希望对每个类都进行修改,因为太麻烦了,同时更重要的一点,破坏了对象的完整性,说不定你以后又不需要这个时间数据了呢...这时怎么办呢?spring aop就为你专门实现这种思想提供了一个切入处理,那就是introduction.introduction可以为你动态加入某些方法,这样可以在运行时,强制转换这些对象,进行插入时间数据的动作,更深的内幕就是c++虚函数中的vtable思想).不过这种动态是以性能作为代价的,使用之前要慎重考虑,这里我们谈的是技术,所以就认为他是必需的.
  
  好,现在我们就拿第四种来进行举例说明spring aop的强大之处:
  
  1)假设创建了一个bookservice接口及其实现方法(你自己的业务对象):
  
  //$id:bookservice.java created:2005-11-6 by kerluse benn
  package com.osiris.springaop;
  
  public interface bookservice {
  public string ordercomputermagazine(string username,string bookname);
  public string orderbook(string username,string bookname);
  }
  
  //$id:bookserviceimpl.java created:2005-11-6 by kerluse benn
  package com.osiris.springaop;
  
  public class bookserviceimpl implements bookservice{
  public string orderbook(string name,string bookname) {
  // todo add your codes here
  string result=null;
  result="订购"+bookname+"成功";
  return result;
  }
  
  public string ordercomputermagazine(string username, string bookname) {
  // todo add your codes here
  string result=null;
  result="订购"+bookname+"成功";
  return result;
  }
  }
  
  2)事实上你还有很多这样的对象,现在我们希望在每个对象中添加我们的功能最后修改的时间,功能如下:
  
  //$id:iauditable.java created:2005-11-7 by kerluse benn
  package com.osiris.springaop.advices.intruduction;
  
  import java.util.date;
  
  public interface iauditable {
  void setlastmodifieddate(date date);
  date getlastmodifieddate();
  }
  
  3)因为我们使用的切入类型是introduction,spring aop为我们提供了一个描述这种类型的接口introductioninterceptor,所以我们的切入实现处理,也需要实现这个接口:
  
  //$id:auditablemixin.java created:2005-11-7 by kerluse benn
  package com.osiris.springaop.advices.intruduction;
  
  import java.util.date;
  
  import org.aopalliance.intercept.methodinvocation;
  import org.springframework.aop.introductioninterceptor;
  
  public class auditablemixin implements iauditable,introductioninterceptor{
  private date lastmodifieddate;
  
  public object invoke(methodinvocation m) throws throwable {
  // todo add your codes here
  if(implementsinterface(m.getmethod().getdeclaringclass())){
  return m.getmethod().invoke(this,m.getarguments());
  //invoke introduced mthod,here is iauditable
  }else{
  return m.proceed(); //delegate other method
  }
  }
  
  public date getlastmodifieddate() {
  // todo add your codes here
  return lastmodifieddate;
  }
  
  public void setlastmodifieddate(date date) {
  // todo add your codes here
  lastmodifieddate=date;
  }
  
  public boolean implementsinterface(class cls) {
  // todo add your codes here
  return cls.isassignablefrom(iauditable.class);
  }
  
  }
  
  4)ok,现在业务对象bookservice类有了,自己希望添加的处理也有了iauditable,那就剩下使用spring aop框架的问题了,配置bean.xml文件:
  
  <?xml version="1.0" encoding="utf-8"?>
  <!doctype beans public "-//spring//dtd bean//en" "http://www.springframework.org/dtd/spring-beans.dtd">
  <beans>
  <!-- beans -->
  <bean id="bookservicetarget" class="com.osiris.springaop.bookserviceimpl" singleton="false"/>
  
  <!-- introduction advice -->
  <bean id="auditablemixin" class="com.osiris.springaop.advices.intruduction.auditablemixin" singleton="false"/>
  
  <!-- introduction advisor -->
  <bean id="auditableadvisor" class="org.springframework.aop.support.defaultintroductionadvisor"
  singleton="false">
  <constructor-arg>
  <ref bean="auditablemixin"/>
  </constructor-arg>
  </bean>
  
  <bean id="bookservice" class="org.springframework.aop.framework.proxyfactorybean">
  <property name="target">
  <ref bean="bookservicetarget"/>
  </property>
  
  <property name="singleton">
  <value>false</value>
  </property>
  
  <!-- force to use cglib -->
  <property name="proxytargetclass">
  <value>true</value>
  </property>
  
  <!-- introduction methods -->
  <property name="proxyinterfaces">
  <value>com.osiris.springaop.advices.intruduction.iauditable</value>
  </property>
  
  <property name="interceptornames">
  <list>
  <value>auditableadvisor</value>
  </list>
  </property>
  </bean>
  
  </beans>
  
  以上就是配置文件,现在我们假设使用业务对象如下,这里是一个简单测试类:
  
  //$id:mainapp.java created:2005-11-6 by kerluse benn
  package com.osiris.springaop;
  
  import java.util.date;
  
  import org.springframework.beans.factory.beanfactory;
  import org.springframework.beans.factory.xml.xmlbeanfactory;
  import org.springframework.core.io.filesystemresource;
  
  import com.osiris.springaop.advices.intruduction.iauditable;
  
  public class mainapp {
  /**
  * @param args
  * @author kerluse benn
  */
  public static void main(string[] args) throws exception{
  // todo add your codes here
  beanfactory factory=new xmlbeanfactory(new filesystemresource("bean.xml"));
  bookservice bookservice=(bookservice)factory.getbean("bookservice");
  iauditable auditable=(iauditable)bookservice;
  system.out.print(bookservice.orderbook("kerluse benn","professional c#"));
  auditable.setlastmodifieddate(new date());
  system.out.println(" 订购时间为"+auditable.getlastmodifieddate());
  thread.sleep(10000);
  system.out.print(bookservice.orderbook("kerluse benn","expert j2ee one-on-one"));
  auditable.setlastmodifieddate(new date());
  system.

扫描关注微信公众号