Spring - используя factory beans в конфигурации?
Каков правильный способ использования factory beans в классах @Configuration
?
Предположим, что для SessionFactory
имеет место следующее:
@Bean
public AnnotationSessionFactoryBean sessionFactory() {
AnnotationSessionFactoryBean factory = new AnnotationSessionFactoryBean();
// set up properties etc.
return factory;
}
В настоящий момент этот метод возвращает bean factory, который не реализует SessionFactory
. Если я использую его в другом bean с @Autowired
как SessionFactory
, он отлично работает и получает его от bean factory:
@Controller
public class MyController {
@Autowired SessionFactory sessionFactory;
// ...
}
Я думаю, что ОК.
Однако это становится проблемой, если я хочу использовать SessionFactory
в том же классе конфигурации:
@Bean
public HibernateTransactionManager transactionManager() {
HibernateTransactionManager man = new HibernateTransactionManager();
// Ideal - doesn't work because sessionFactory() return type doesn't match:
// SessionFactory sessionFactory = sessionFactory();
// Is this one correct?
// SessionFactory sessionFactory = (SessionFactory) sessionFactory().getObject();
man.setSessionFactory(sessionFactory);
return man;
}
Что такое правильный способ реализовать подобную зависимость?
Ответы
Ответ 1
Подход @Configuration
по-прежнему относительно свежий и имеет некоторые грубые края. Factory beans являются одним из них. Таким образом, это не правильный путь, по крайней мере, я не знаю никого. Возможно, будущие релизы Spring будут каким-то образом обрабатывать этот случай. На данный момент это мой предпочтительный способ:
@Bean
public AnnotationSessionFactoryBean sessionFactoryBean() {
AnnotationSessionFactoryBean factory = new AnnotationSessionFactoryBean();
// set up properties etc.
return factory;
}
@Bean
public SessionFactory sessionFactory() {
return (SessionFactory) sessionFactoryBean().getObject();
}
И при необходимости используйте метод sessionFactory()
. Если вы хотите вызвать sessionFactoryBean().getObject()
несколько раз по какой-либо причине (например, когда FactoryBean
не возвращает одноточие), не забудьте использовать аннотацию @Scope
. В противном случае Spring убедится, что sessionFactory()
вызывается только один раз и кэширует результат.
Spring достаточно интеллектуальный, чтобы выполнить всю необходимую инициализацию после вызова метода @Bean
и перед возвратом самого bean. Таким образом, InitializingBean
, DisposableBean
, @PostConstruct
и т.д. Все распознаются и обрабатываются должным образом. Фактически, я всегда находил вызов afterPropertiesSet
немного взлома, потому что это ответственность контейнера.
Также есть второй метод, рекомендованный в Spring Документе Datastore - Reference Documentation, который на первый взгляд выглядит немного непоследовательным, но отлично работает:
@Resource
private Mongo mongo;
@Bean
MongoFactoryBean mongo() {
return new MongoFactoryBean();
}
Итак, Factory создается с использованием метода @Bean
, но bean, созданный с помощью Factory, может быть получен с использованием поля с автоподстановкой. Clever.
Ответ 2
Я нашел пример этого на Spring форумах.
@Bean
public SessionFactory sessionFactory() {
AnnotationSessionFactoryBean sessionFactoryBean =
new AnnotationSessionFactoryBean();
// ...configuration code here...
sessionFactoryBean.afterPropertiesSet();
return sessionFactoryBean.getObject();
}
Ответ 3
Вы можете использовать его следующим образом:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class LoginServiceTest {
@Configuration
public static class Config {
@Bean
public HttpInvokerProxyFactoryBean loginServiceProxy() {
HttpInvokerProxyFactoryBean proxy = new HttpInvokerProxyFactoryBean();
proxy.setServiceInterface(LoginService.class);
proxy.setServiceUrl("http://localhost:8080/loginService");
return proxy;
}
}
@Inject
private LoginService loginService;
@Test
public void testlogin() {
loginService.login(...);
}
}