【问题标题】:Adding @Transactional to a Bean that's referenced by a BeanPostProcessor将 @Transactional 添加到 BeanPostProcessor 引用的 Bean
【发布时间】:2013-04-03 03:44:48
【问题描述】:

目前我有一个这样设置的 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豆>DataRetriever豆。

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

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

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

根据this SO question,所有 BeanPostProcessor 及其直接引用的 bean 都会在启动时被实例化,另外:由于 AOP 自动代理是作为 BeanPostProcessor 本身实现的,因此没有 BeanPostProcessor 或直接引用的 bean 符合条件自动代理(因此不会将方面“编织”到其中。

事实上,@Transactional 注释被忽略了,我得到一个错误:“No Hibernate session bound to this thread。”

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

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

编辑:具体示例in this SO question

【问题讨论】:

    标签: spring


    【解决方案1】:

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

    基本上,我们将 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。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-17
      • 2016-02-11
      相关资源
      最近更新 更多