Spring – service中调用内部的其他方法时如何保证事务

Spring - service中调用内部的其他方法时如何保证事务

一、概述

Spring事务的传播属性对开发者相当便利,但是Spring AOP使用wrap而非继承来实现代理,会使得同一个service中,方法A调用方法B时,无法保证方法B的事务传播属性。本文简单总结下有哪些方法可以解决这个问题:

  • expose-proxy=true开启暴露Aop代理得到ThreadLocal支持
  • 使用spring + AspectJ
  • 直接使用bean注入
  • 使用ApplicationContext 及@PostConstruct获取代理
  • 自定义接口并提供类实现BeanPostProcessor接口来注入bean

二、简单介绍

1. expose-proxy=true

xml配置中,做如下配置:

<aop:aspectj-autoproxy expose-proxy="true"/>

然后再service中调用本地方法时,使用如下方式:

((XXXService)AopContext.currentProxy()).xxxMethod(params);

2.使用spring + AspectJ

Spring AOP基于代理模式和装饰器模式,使用包装的方式,将实际的service实例包装到代理类中,因此service方法中的this并不会感知代理对象,而事务是设置在代理对象上的。
不同于AOP,AspectJ使用加载时织入的方式,支持所有的pointcut,因此可以支持内部方法的事务设置。
在xml中作如下配置,即可使Spring使用AspectJ来支持事务:

<tx:annotation-driven mode="aspectj"/>

service代码里面无需任何改动。

3. 直接使用bean注入

使用如下方式手动注入service,然后再service中调用内部方法时,要显式的指定self.xxxmethod()。

public class UserService {

    private UserService self;
    // 不要使用基于构造器的注入
    public void setSelf(UserService self) {
        this.self = self;
    }
<bean id="userService" class="your.package.UserService">
  <property name="self" ref="userService" />
    ...
</bean>

这种方法的缺陷是,只支持singleton的类,不支持原型等其他类型的bean。

4. 使用ApplicationContext 及@PostConstruct获取代理

使用如下方式:

public class SBMWSBL {
   
private SBMWSBL self;
@Autowired 
private ApplicationContext applicationContext;

@PostConstruct
public  void postContruct(){
    self =applicationContext.getBean(SBMWSBL.class);
}

这种方式的缺陷和3是一样的。

5.自定义接口并提供类实现BeanPostProcessor接口来注入bean

这种方式稍微复杂一些,但是支持单例bean的循环依赖问题。
首先,自定义接口,表示需要注入自我bean的service:

public interface BeanSelfAware {  
    void setSelf(Object proxyBean);  
}  

其次,service要实现这个接口,并提供self的设置方法。

@Service  
public class AServiceImpl4 implements AService, BeanSelfAware {
   //此处省略接口定义 
    private AService proxySelf;  
    public void setSelf(Object proxyBean) { //通过InjectBeanSelfProcessor注入自己(目标对象)的AOP代理对象 
        this.proxySelf = (AService) proxyBean;  
    }  
    @Transactional(propagation = Propagation.REQUIRED)  
    public void a() {  
        proxySelf.b();//调用代理对象的方法 这样可以执行事务切面 
    }  
    @Transactional(propagation = Propagation.REQUIRES_NEW)  
    public void b() {  
    }  
}   

最后,提供配置类,实现BeanPostProcessor接口,一旦有bean被初始化,就判断他是否实现了我们的接口,如果实现了就调用设置方法,将代理bena注入进去。

@Component  
public class InjectBeanSelfProcessor2 implements BeanPostProcessor, ApplicationContextAware {
     
    private ApplicationContext context;  
    //① 注入ApplicationContext 
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  
        this.context = applicationContext;  
    }  
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {  
        if(!(bean instanceof BeanSelfAware)) { //② 如果Bean没有实现BeanSelfAware标识接口 跳过 
            return bean;  
        }  
        if(AopUtils.isAopProxy(bean)) { //③ 如果当前对象是AOP代理对象,直接注入 
            ((BeanSelfAware) bean).setSelf(bean);  
        } else {  
            //④ 如果当前对象不是AOP代理,则通过context.getBean(beanName)获取代理对象并注入 
            //此种方式不适合解决prototype Bean的代理对象注入 
            ((BeanSelfAware)bean).setSelf(context.getBean(beanName));  
        }  
        return bean;  
    }  
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  
        return bean;  
    }  
}  

三、参考

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论