使用AOP处理事务重试

以下摘自<pojo in action>备查. 算是比较有用的东西,以后可能用到.

Using an AOP interceptor to retry transactions
A Spring application can use a custom AOP interceptor to catch the Concurrency-
FailureException and automatically retry the transaction. Here is a custom
Spring AOP interceptor that does this:
public class TransactionRetryInterceptor
implements MethodInterceptor {
protected int maxRetryCount = 3;
public void setMaxRetryCount(int maxRetryCount) {
this.maxRetryCount = maxRetryCount;
}
public Object invoke(MethodInvocation invocation)
throws Throwable {
int retryCount = 0;
while (true)
try {
ReflectiveMethodInvocation inv =
(ReflectiveMethodInvocation) invocation;
MethodInvocation anotherInvocation = inv.invocableClone();
return anotherInvocation.proceed();
} catch (ConcurrencyFailureException e) {
if (retryCount++ > maxRetryCount)
throw e;
else {
continue;
}
}
}
}
This class has a maxRetryCount property whose default value is 3. The invoke()
method catches the ConcurrencyFailureException and loops at most maxRetry-
Count times, retrying the transaction. Because proceed() can only be called once,
invoke() clones the MethodInvocation before calling it. The TransactionRetry-
Interceptor is a good example of the power and flexibility of the Spring framework.

mindmap – spring事务配置

SimpleFormController的流程图

放到这里留作备份.还有一个pdf版的更详细.

spring的tip

发现用spring有一个问题,如果某个人忘记加入一个类文件,而这个依赖是在spring中体现的,那么在编译时不会报错, 而显然是不好的.这种错误应该在编译的时候就应该报出来. 考虑在编译时检查一次bean依赖, 最简单就是初始化一次application-context.

Spring SimpleFormController Cheatsheet

pdf下载

acegi整合struts-menu时filter-mapping的问题

今天在整合acegi和struts-menu的时候老是有问题,跟进源代码发现struts-menu的RolesPermissionsAdapter 中:

public boolean isAllowed(MenuComponent menu) {
if (menu.getRoles() == null) {
return true; // no roles define, allow everyone
} else {
// Get the list of roles this menu allows
String[] allowedRoles = delimiters.split(menu.getRoles());
for (int i=0; i < allowedRoles.length; i++) {
if (request.isUserInRole(allowedRoles[i])) {
return true;
}
}
}
return false;
}

request.isUserInRole(allowedRoles[i])返回是false. – 因为这个request是sitemesh的pageinforequestWrapper!!!
(之前找了各种可能都没有发现异常,试了无数种 可能的原因)

为什么会这样,之前做的也是完全一样啊?仔细比较了以前写的和现在的,发现 filter-mapping的顺序不一样:

之前写的是acegi的filter-mapping在第一个,试着把现在的filtermapping 放到第一个.成功!

原来filter-mapping的顺序也是有讲究的.如果先mapping sitemesh的filter,request就会被sitemesh的pageinforequestwrapper(?) 包裹,然后就不能被acegi的filter包装成SecurityContextHolderAwareRequestWrapper.

这样在调用isUserInRole时候就出问题了,因为使用的不是acegi的实现.

记录下来.

SpringIDE的误导

用springide验证了一下xml文件,得到这个错误

Destroy-method ‘shutdown’ not found in bean class ‘org.enhydra.jdbc.pool.StandardXAPoolDataSource’

bean的配置如下:

也就是说springide认为这个bean没有shutdown的方法.查了一下xapool的source,发现是有的,但是是shutdown(boolean).destroy-method属性的定义:

Attribute : destroy-method
The name of the custom destroy method to invoke on bean factory shutdown. The method must
have no arguments, but may throw any exception. Note: Only invoked on beans whose lifecycle is
under the full control of the factory – which is always the case for singletons, but not guaranteed
for any other scope.

在来看spring的source终于发现玄机所在:

DisposableBeanAdapter的invokeCustomDestroyMethod 方法:

Class[] paramTypes = destroyMethod.getParameterTypes();
if (paramTypes.length > 1) {
logger.error("Method '" + destroyMethodName + "' of bean '" + this.beanName +
"' has more than one parameter - not supported as destroy method");
}
else if (paramTypes.length == 1 && !paramTypes[0].equals(boolean.class)) {
logger.error("Method '" + destroyMethodName + "' of bean '" + this.beanName +
"' has a non-boolean parameter - not supported as destroy method");
}
else {
Object[] args = new Object[paramTypes.length];
if (paramTypes.length == 1) {
args[0] = Boolean.TRUE;
}

if (!Modifier.isPublic(destroyMethod.getModifiers()) ||
!Modifier.isPublic(destroyMethod.getDeclaringClass().getModifiers())) {
destroyMethod.setAccessible(true);
}

红色标记的地方就看到了,这个方法只会接受一个无参数方法或者参数唯一并且是boolean的方法.boolean值得意义就是force

附方法的注释:
This implementation invokes a no-arg method if found, else checking
for a method with a single boolean argument (passing in “true”,
assuming a “force” parameter), else logging an error.

显然SpringIDE没有考虑到这点.

Spring的FactoryBean和InitializingBean

刚刚“领略”了一些spring-modules的源代码看到了FactoryBean和InitiliazingBean的用处。记在这里

  • InitializingBean.afterPropertiesSet()

这个方法比较容易理解,是在spring根据配置文件设置了bean的属性后调用的,一般这里会做一些初始化的工作。比如,xml配置中只是提供了resource的路径,在这个方法里 就要根据resource的路径初始化一些成员变量(那些不能被注入或者没有被注入的成员变量)。

public void afterPropertiesSet() throws Exception {
if (this.definitionLocation == null) {
throw new FatalBeanException(“Property [definitionLocation] of class [”
+ ProcessDefinitionFactoryBean.class.getName()
+ “] is required.”);
}

InputStream inputStream = null;
try {
inputStream = this.definitionLocation.getInputStream();
this.processDefinition = ProcessDefinition.parseXmlInputStream(inputStream);
//       这里就是初始化的地方,spring的配置文件中只配置了definitionLocation的信息,其他的事就在这里做

}
finally {
if (inputStream != null) {
inputStream.close();
}
}
}

  • FactoryBean.getObject & getObjectType

在比较配置文件和源代码的时候我很疑惑,为什么类型都不对头,怎么可以注入的?答案就在FactoryBean的这两个方法里

public Object getObject() throws Exception {
return this.processDefinition;
}

public Class getObjectType() {
return (processDefinition == null) ? ProcessDefinition.class : processDefinition.getClass();
}

getObject返回的是成员变量processDefinition,getObjectType返回的也不是this.class,而是processDefinition.getClass();spring在加载这样的factorybean的时候就会根据这两个方法来决定类型和实例。比如在xml中配置了ref=”someFactoryBean”的 时候,实际的引用可以是任意的,只要你提供了这两个方法。-好像有点表达不清楚。

AOP也是注入?

如果IOC是运行时对象依赖关系的注入的话,那么AOP是不是就是运行期执行流的注入呢?

每个注入点(point cut)就对应IOC中的sette或构造函数参数.每个处理方法(advice)就对应IOC中的一个对象

啥是Spring

(最初发布在JavaResearch.org,现在整理好放到自己的地方.)

有人说spring aop+ spring ioc, 才~~~是spring
简单一点,是一个容器. 什么容器,容纳什么?是对象,或者说bean的容器.
那为什么叫轻量级容器呢?相对于EJB container,使用spring不需要写符合容器规范的代码,即容器不会”侵入”了你的代码.

这 个容器会提供你的应用(程序)中用到的所有对象,并对这些对象进行统一的生命周期管理和组装.在通常的开发中,我们在需要某个对象的时候只是 new MyObject(). 在Java中,这样没有什么不好,因为gc会打理好”善后”工作,是系统级的. 而用spring,在需要某个对象 时,只要向容器请求相应的对象,spring会找到并准备好这些对象并提供给你.她也会打理好”善后”工作,但是是在应用级的.
另一方面,spring还会帮助你打理对象之间的依赖关系.
比如原来的做法:

class A{
}
class B{
A a ;
public B(){ a = new A();}
}

而使用spring的做法
class A{
}
class B{
A a;
public B(){}
void setA(A a){this.a=a}
A getA(){return this.a}
}
(希望你不要单纯地认为spring会写很多代码)
但 从前一个方面,你可能觉得spring只是一个对象容器.从这里你就应该看出,spring是bean容器,因为spring需要你的类符合bean规 范:相应于每一个成员域,都需要提供setter和getter方法.spring要使用这些方法来注入依赖关系,也就是 dependence injection, 或者inversion of control. 我个人觉得还是di更容易理解,直到现在我还是要考虑 怎么去向别人很好的解释ioc.控制反转(倒转),我的理解是就如同上面的两个例子里看到的,依赖(控制)不在体现在代码逻辑里(如第一个例子),而是在 配置文件里,而在代码中我们只提供注入点(也就是setter和getter).

希望我对IoC的概念的讲解能够给你一些启发.
你 可能要问了,为什么我要这样做呢?原来的做法有什么不妥的地方么?没有什么不妥,只是两种理念而已,没有绝对的好还是不好,但我还是给你我的解释–我理 解的IoC的好处,希望有所帮助.通常在程序设计的时候,我们在需要某些功能时,会相应的去设计一些方法,然后根据OO去将方法和一成员变量组成一个类. 实际上,我们最终设计出的程序是:一组类的实例互相交互完成某个特定的任务.
除了一些核心的业务方法,以外我们还要做组装对象的工作.比如我有了 一个工厂,里面有很多机器,机器在开动时要装配相应的模具.那么在工厂的生产过程中, 首先我要有工厂,机器,模具这样三个类.然后我的”动作”有:装 配,开机.通常的做法我们要做装配,然后再去开机.而用spring,我们只是专注于开机.这样我们就把装配这个动作抽离出了核心的”生产过程”.当某些 机器改变了装配模具时,不在需要修改核心业务代码.这就是解耦.如:

public class Production{
public static void main(String[] args){
Factory factory = (Factory)BeanFactory.getBean(“factory”);
factory.launchProduction();
}
}

class Factory{
Machine machine1,machine2;
void launchProduction(){
machine1.start(); machine2.start();
}
// setters and getters
}

class Machine{
Tool tool;
void start(){
}
// setters and getters
}

在launchProduction()方法中只需要开动每台机器即可.而不需要每次都装配机器.装配的工作交给了别人.现在只要按下start按钮.生产就开始了!要是原来:

void launchProduction(){
machine1 = new MachineA();
machine1.setTool(new ToolA());
machine2 = new MachineB();
machine2.setTool(new ToolB());
machine1.start();
machine2.start();
}

这就是工作分工,是不是感觉轻松了许多?从此以后,我们都是面向构件去开发,而不需要过多地在代码中体现构件之间的依赖关系.

AOP
推荐你看一下<<effective enterprise java>>的第一章,对AOP有很清晰,易懂的解释.其实AOP并非很艰深晦涩的概念,但是从架构角度去理解她的重要性可能不是我这样的new fish一时半会儿可以领悟到的.
我这里只是想说,有些概念你要知道是怎么回事,但理解到多深,除了天赋以外更多的是经验和悟.所以不要心急.–像是在自我解嘲.
也许在不知不觉中你就使用了很多AOP的概念,比如servlet里的filter,比如在写一个command类时,给她的调用类在每次调用command时前后加上:preProcess和postProcess…
我不想解释太多,<<eej>>的解释已经足够.

Hope helps.