3

目前,我以这种方式设置了 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. 这是我遇到问题的地方。

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

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

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

想问一下这种情况有没有可能的解决办法。

编辑:this SO question中的具体示例。

4

1 回答 1

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。首先,VendorFactoryBeanbean 与bean 一起被初始化Manager。然后,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) 最后我们创建一个将 bean 绑定到bean 的ServletContextListener类。DataRetrieverManager

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。

于 2013-04-11T05:01:50.193 回答