编程式的事务管理---
编程式的事务管理可以清楚地控制事务的边界,也就是让您自行实现事务开始时间、撤消操作的时机、结束时间等,可以实现细粒度的事务控制
声明式的事务管理---
Spring声明式的事务管理依赖它的AOP框架来完成
使用声明事务管理的好处是,事务管理不能侵入您所开发的组件
在Spring 2.0里要使用AOP的话,大大的简化了,不管是程序书写还是xml 配置,下面我说一下我的学习和使用过程:
我以日志功能为例子,假如我们写了一个method,然后我们想在这个method执行做一件事,执行后做一件事,或是发生异常时怎么样,有的人在想那还不简单,我直接在在这个方法里加上不就行了,但是再想想当一个项目做大后,用这种方法会给以后的维护带来很多麻烦,因为你已经把这些本不属于程序逻辑的事情也耦合到里面了,如果加的地方多的话,以后麻烦可就大了,而利用Spring AOP 想加这个日志功能就简单了,完全与程序逻辑解耦,以后想添改什么的,单独去处理就方便好多了。在Spring 2.0里不再需要去实现好多的接口去做这些事情,配置也很简单了,
讲解背景如下:
我有一个接口 IHello
public interface IHello {
public void hello(String name);
}
有一个实现类HelloSpeaker
public class HelloSpeaker implements IHello {
public void hello(String name) {
System.out.println("Hello,"+name);
}
}
1.Before Advice
现在我想在hello方法执行前加个log怎么做呢,这里我们要加个class 叫 LogAspect
专门去处理log
public class LogAspect {
private Logger logger=Logger.getLogger(this.getClass().getName());
public void before(JoinPoint jointPoint) {
logger.log(Level.INFO, "method starts..."
+ jointPoint.getSignature().getDeclaringTypeName() + "."
+ jointPoint.getSignature().getName());
}
}
然后建个spring基于xml schema的文件
再写个TestClass
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans-config.xml");
IHello helloSpeaker=(IHello)context.getBean("helloSpeaker");
helloSpeaker.hello("Jack");
}
现在可运行了,但是运行前确保加上asm-*.jar 2.2.1或2.2.2 version ,asm-attrs-2.2.2.jar, asm-commons.jar我的是2.2.的
aspectjweaver-1.5.3.jar commons-logging-1.0.4.jar
spring所需的就不用说了。然后运行结果如下:
2009-2-23 15:38:59 LogAspect before
信息: method starts...IHello.hello
Hello,Jack
2.After Advice
有两种:After和Afterreturning(可以设置目标方法调用的传回值)
现在原LogAspec里加个方法:
public void after(JoinPoint jointPoint){
logger.log(Level.INFO, "method ends..."
+ jointPoint.getSignature().getDeclaringTypeName() + "."
+ jointPoint.getSignature().getName());
}
beans-config.xml里加上:
<aop:after pointcut-ref="logHello" method="after"/>
运行后成了:
2009-2-23 15:42:27 LogAspect before
信息: method starts...IHello.hello
2009-2-23 15:42:27 LogAspect after
信息: method ends...IHello.hello
Hello,Jack
有人说不对啊,这哪里是前和后啊,这里要说明,log是由另一个线程进行操作的,并不影响原有程序的流程。
3.After Throwing Advice:
现在原LogAspec里加个方法:
public void afterThrowing(JoinPoint jointPoint,Throwable throwable){
logger.log(Level.INFO, "Logging that a " + throwable +
"\nException was thrown in..."+
jointPoint.getSignature().getDeclaringTypeName() + "."
+ jointPoint.getSignature().getName());
}
配置文件:
<aop:after-throwing pointcut-ref="logHello" throwing="throwable" method="afterThrowing"/>
现在故意把hello里修改一下让其引发异常:
System.out.println("Hello,"+Integer.parseInt(name));//Testing Exception
运行结果如下:
2009-2-23 15:46:30 LogAspect before
信息: method starts...IHello.hello
2009-2-23 15:46:30 LogAspect after
信息: method ends...IHello.hello
2009-2-23 15:46:30 LogAspect afterThrowing
信息: Logging that a java.lang.NumberFormatException: For input string: "Jack"
Exception was thrown in...IHello.hello
java.lang.NumberFormatException: For input string: "Jack"
4. Around Advice :
这个可以实现在前和后加上log
同样增加一个方法:
public Object invoke(ProceedingJoinPoint jointPoint) throws Throwable{
logger.log(Level.INFO, "method starts..."
+ jointPoint.getSignature().getDeclaringTypeName() + "."
+ jointPoint.getSignature().getName());
Object retVal=jointPoint.proceed();
logger.log(Level.INFO, "method ends..."
+ jointPoint.getSignature().getDeclaringTypeName() + "."
+ jointPoint.getSignature().getName());
return retVal;
}
配置xml文件:
<aop:around pointcut-ref="logHello" method="invoke"/>
运行结果如下:
2009-2-23 15:49:04 LogAspect invoke
信息: method starts...IHello.hello
2009-2-23 15:49:04 LogAspect afterThrowing
信息: Logging that a java.lang.NumberFormatException: For input string: "Jack"
Exception was thrown in...IHello.hello
java.lang.NumberFormatException: For input string: "Jack"
大家一看就会发现啊!后面的log没有执行,这里可以告诉我们用Around实现前和后加入log和分别用Before和After的区别之一了。
前面所说的都是基于XML Schema的,其实还提供了另外的方式
基于 Annotation方式
我先把最后的样子贴出来吧:
大家会看到有@Aspect,·@Before这些就是基于Annotation 的方式了,代表什么意义,大家去查询Spring AOP相关资料去了解还有execution(* IHello.*(..))这些也是。
这样写配置文件就简单了
<!-- <aop:aspectj-autoproxy/> -->
就这一句就行了,其它的都不用写了。
大家可以去思考两种方式各自的优势。
自己的笔记:
spring中的JDBC的生命式事物管理整个服务层:
//一个普遍性的让整个服务层成为事务性
<bean id="jdbcTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="basicDataSource"/> //数据源
</bean>
<aop:config>
//定义了 一个切面,也可以说是一个拦截器
<aop:pointcut id="jdbcMethod" expression="execution(* test.service.*.*(..))"/>
//通知器(advisor)把切面与 'txAdvice' 绑定在一起
<aop:advisor advice-ref="jdbcTransactionAdvice" pointcut-ref="jdbcMethod"/>
</aop:config>
//事务封装
<tx:advice id="jdbcTransactionAdvice" transaction-manager="jdbcTransactionManager">
<tx:attributes>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="remove*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>