将@Transactional添加到BeanPostProcessor引用的Bean

时间:2013-04-11 03:50:58

标签: spring

目前我以这种方式设置了Spring配置:

@Bean
@Autowired
public Manager manager(DataRetriever dataRetriever) { 
    Manager m = new Manager();
    m.setRetriever(dataRetriever); //DataRetriever is a @Component bean
    return m;
}

@Bean
public VendorFactoryBean factory() { 
    final VendorFactoryBean fb = new VendorFactoryBean(); 
    fb.setManager(manager());
    return fb;
}

VendorFactoryBean 要求 Manager个实例。 Manager bean> DataRetriever bean。

DataRetriever内我有一个@Transactional注释,如下所示:

@Component("dataRetriever")
public class DataRetriever { 
    @Transactional public void retrieveStuff() {...} 
}

现在,VendorFactoryBean实现了BeanPostProcessor。这是我遇到问题的地方。

根据this SO question,所有BeanPostProcessors及其直接引用的bean将在启动时实例化,此外:由于AOP自动代理是作为BeanPostProcessor本身实现的,因此没有BeanPostProcessors或直接引用的bean有资格进行自动代理(因此不会将方面'编织'到其中。

实际上,@ Transactal注释被忽略了,我得到一个错误:“没有Hibernate会话绑定到这个线程。”

我试图从VendorFactoryBean移动fb.setManager(manager());调用并使用另一个BeanPostProcessor设置它,但是在这种情况下我不能这样做,因为VendorFactoryBean来自一个库并且它包含一个断言,在实例化时是一个Manager实例必须已经设置。

想问一下这种情况是否有可能解决方案。

修改:具体示例in this SO question

1 个答案:

答案 0 :(得分:0)

我想到了针对这种特定情况的解决方案。

基本上,我们将DataRetriever bean与Manager bean分离

(1)我们按如下方式修改@Configuration类:

@Bean
public Manager manager() { 
    Manager m = new Manager();
    //m.setRetriever(dataRetriever); !IMPT! remove this!
    return m;
}

@Bean
public VendorFactoryBean factory() { 
    final VendorFactoryBean fb = new VendorFactoryBean(); //implements BeanPostProcessor 
    fb.setManager(manager());
    return fb;
}

(2)我们允许Spring初始化bean。首先,VendorFactoryBean bean与Manager bean一起初始化。然后,初始化DataRetriever bean并正确处理其@Transactional注释。

(3)由于我们正在进行编程配置,我们有一个WebApplicationInitializer类,我们可以插入ServletContextListeners,如下所示:

public class AppInitializer implements WebApplicationInitializer {
    public void onStartup(ServletContext container) throws ServletException {
        // Create the 'root' Spring application context
        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
        rootContext.register(SpringAppConfig.class);

        // Manage the lifecycle of the root application context
        container.addListener(new ContextLoaderListener(rootContext));
        // New listener to finalize initialization of Manager.
        container.addListener(new ManagerFinalizerListener(rootContext));
    }
}

(4)最后我们创建了一个ServletContextListener类,将DataRetriever绑定到Manager bean。

public class ManagerFinalizerListener implements ServletContextListener {

    private AnnotationConfigWebApplicationContext ctx;

    public ManagerFinalizerListener(AnnotationConfigWebApplicationContext ctx) {
        this.ctx = ctx;
    }

    public void contextInitialized(ServletContextEvent sce) {
        final Manager mgr = (Manager)ctx.getBean("manager");
        mgr.setRetriever((DataRetriever)ctx.getBean("dataRetriever"));
    }
}

这将在Spring上下文完成初始化之后完成,因此,不会因依赖性而创建过早的bean。