Java 如何在没有 XML 的情况下使用 Spring 根据属性在运行时注入不同的服务
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26463393/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to inject different services at runtime based on a property with Spring without XML
提问by Pedro Lopez
I am using Spring Boot for Java standalone application. I have a bean which makes use of a service. I want to inject different implementations of that service at runtime, based on a property in a properties file with Spring (4 for that matter).
我正在将 Spring Boot 用于 Java 独立应用程序。我有一个使用服务的 bean。我想在运行时注入该服务的不同实现,基于 Spring 的属性文件中的属性(就此而言为 4)。
This sounds like the Factory pattern, but Spring also allows using annotations to solve the problem, like this.
这听起来像工厂模式,但 Spring 也允许使用注解来解决问题,就像这样。
@Autowired @Qualifier("selectorProperty") private MyService myService;
Then in the beans.xml file I have an alias, so that I can use the property in the @Qualifier.
然后在 beans.xml 文件中我有一个别名,以便我可以使用@Qualifier 中的属性。
<alias name="${selector.property}" alias="selectorProperty" />
And in my different implementations I would have different qualifiers.
在我的不同实现中,我会有不同的限定符。
@Component("Selector1")
public class MyServiceImpl1
@Component("Selector2")
public class MyServiceImpl2
application.properties
应用程序属性
selector.property = Selector1
selector.property = Selector2
Whereas regarding the factory pattern, in Spring you can use ServiceLocatorFactoryBean to create a factory that would give you the same functionality.
而关于工厂模式,在 Spring 中,您可以使用 ServiceLocatorFactoryBean 创建一个工厂,该工厂将为您提供相同的功能。
<bean
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean"
id="myServiceFactory">
<property
name="serviceLocatorInterface"
value="my.company.MyServiceFactory">
</property>
</bean>
public interface MyServiceFactory
{
MyService getMyService(String selector);
}
And then in your bean you can use something like this to get the right implementation at runtime depending on the value of the property.
然后在你的 bean 中,你可以使用这样的东西在运行时根据属性的值获得正确的实现。
@Value("${selector.property}") private String selectorProperty;
@Autowired private MyServiceFactory myServiceFactory;
private MyService myService;
@PostConstruct
public void postConstruct()
{
this.myService = myServiceFactory.getMyService(selectorProperty);
}
But the problem with this solution is that I could not find a way to avoid using XML to define the factory, and I would like to use only annotations.
但是这个解决方案的问题是我找不到避免使用 XML 定义工厂的方法,我只想使用注释。
So the question would be, is there a way to use the ServiceLocatorFactoryBean (or something equivalent) using only annotations, or am I forced to use the @Autowired @Qualifier way if I do not want to define beans in XML? Or is there any other way to inject different services at runtime based on a property with Spring 4 avoiding XML? If your answer is just use the @Autowired @Qualifier
with the alias, please give a reason why that is better than using a well known factory pattern.
所以问题是,有没有办法只使用注释来使用 ServiceLocatorFactoryBean(或等效的东西),或者如果我不想在 XML 中定义 bean,我是否被迫使用 @Autowired @Qualifier 方式?或者有没有其他方法可以在运行时基于 Spring 4 避免 XML 的属性注入不同的服务?如果您的答案只是使用@Autowired @Qualifier
别名,请说明为什么这比使用众所周知的工厂模式更好。
Using the extra XML is forcing me to use @ImportResource("classpath:beans.xml")
in my Launcher class, which I'd rather not use either.
使用额外的 XML 迫使我@ImportResource("classpath:beans.xml")
在我的 Launcher 类中使用,我也不想使用它。
Thanks.
谢谢。
采纳答案by Pedro Lopez
Actually, you can use ServiceLocatorFactory without XML by declaring it as a bean in your configuration file.
实际上,您可以通过在配置文件中将 ServiceLocatorFactory 声明为 bean 来使用没有 XML 的 ServiceLocatorFactory。
@Bean
public ServiceLocatorFactoryBean myFactoryServiceLocatorFactoryBean()
{
ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(MyServiceFactory.class);
return bean;
}
@Bean
public MyServiceFactory myServiceFactory()
{
return (MyServiceFactory) myFactoryServiceLocatorFactoryBean().getObject();
}
Then you can still use the factory as usual, but no XML is involved.
然后你仍然可以像往常一样使用工厂,但不涉及 XML。
@Value("${selector.property}") private String selectorProperty;
@Autowired @Qualifier("myServiceFactory") private MyServiceFactory myServiceFactory;
private MyService myService;
@PostConstruct
public void postConstruct()
{
this.myService = myServiceFactory.getMyService(selectorProperty);
}
回答by mavarazy
I'm using Spring profiles
我正在使用 Spring 配置文件
For example with dataSources Using it you can define as many dataSources, as you like
例如使用 dataSources 使用它,您可以根据需要定义任意数量的数据源
@Configuration
@Profile("dev")
public class StandaloneDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
@Configuration
@Profile("cloud")
public class CloudDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
And in runtime, by specifying
在运行时,通过指定
-Dspring.profiles.active="myProfile"
-Dspring.profiles.active="myProfile"
you active one or another configuration (All of them must be imported in your main Configuration, they are just ignored based on active profile).
您激活一个或另一个配置(所有这些都必须在您的主配置中导入,它们只是根据活动配置文件被忽略)。
Here is a good article: http://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile/
这是一篇好文章:http: //spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile/