目前我以这种方式设置了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。
答案 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。