Reputation: 3207
Does anyone know if I should be able to use property placeholder as an expression in a Qualifier? I can't seem to get this working.
I am using spring 3.0.4.
@Controller
public class MyController {
@Autowired
@Qualifier("${service.class}")
Service service;
}
@Service
@Qualifier("ServiceA")
ServiceA implements Service {
public void print() {
System.out.println("printing ServiceA.print()");
}
}
@Service
@Qualifier("ServiceB")
ServiceB implements Service {
public void print() {
System.out.println("printing ServiceB.print()");
}
}
XML:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="file:/etc/config.properties"/>
</bean>
config.properties:
config.properties
service.class=serviceB
Upvotes: 28
Views: 40675
Reputation: 76
Just use @ConditionalOnProperty. If you don't need to use both of those services in one context, then your code could be something like:
@Service
@ConditionalOnProperty(value="storage.type", havingValue = "serviceA")
ServiceA implements Service {
public void print() {
System.out.println("printing ServiceA.print()");
}
}
@Service
@ConditionalOnProperty(value="service.class", havingValue = "serviceB")
ServiceB implements Service {
public void print() {
System.out.println("printing ServiceB.print()");
}
}
@Controller
public class MyController {
@Autowired
Service service;
}
Upvotes: 0
Reputation: 1019
As a workarround, you can set the desired Spring service implementation based on its name in your config.properties.
@Controller
public class MyController {
//add a String which will hold the name of the service to implement
@Value("${service.class}")
private String serviceToImplement;
Service service;
// now autowire spring service bean based on int name using setter
@Autowired
public void setService(ApplicationContext context) {
service = (Service) context.getBean(serviceToImplement);
}
}
@Service
@Qualifier("ServiceA")
ServiceA implements Service {
public void print() {
System.out.println("printing ServiceA.print()");
}
}
@Service
@Qualifier("ServiceB")
ServiceB implements Service {
public void print() {
System.out.println("printing ServiceB.print()");
}
}
config.properties
service.class=serviceB
Upvotes: 2
Reputation: 2700
This solution works without XML and with properties file.
Yours classes improved:
MyController.java
:
@Controller
public class MyController {
@Autowired
public MyController(@Qualifier("MyServiceAlias") MyService myService) {
myService.print();
}
}
ServiceA.java
:
@Service("serviceA")
public class ServiceA implements MyService {
@Override
public void print() {
System.out.println("printing ServiceA.print()");
}
}
ServiceB.java
:
@Service("serviceB")
public class ServiceB implements MyService {
@Override
public void print() {
System.out.println("printing ServiceB.print()");
}
}
application.properties
(here you can change which class will be loaded):
service.class=serviceA
And important configuration file AppConfig.java
:
@Configuration
public class AppConfig {
@Autowired
private ApplicationContext context;
@Bean
public MyService MyServiceAlias(@Value("${service.class}") String qualifier) {
return (MyService) context.getBean(qualifier);
}
}
Additional explanations:
@Qualifier
only for field which will be autowired. For services, to specify bean name, use @Service
.@Service
with specyify name. For example, standard bean name for ServiceA is serviceA
(not ServiceA
- see big first letter), so @Service("serviceA")
redundant (@Service
is enough).AppConfig
on this answer: Spring Bean Alias in JavaConfig.Upvotes: 23
Reputation: 25259
I would venture to guess the answer is no, just based on the write ups in a few javadoc pages. For example, see the docs for @Value
:
Notice they make special mention of using expressions in the annotation. For comparison, the docs for @Qualifier
:
Which make no mention of expressions. Obviously not a definitive answer (but spring is generally very good on documentation). Also, if expressions were supported in the @Qualifier
annotation I would expect they work the same way as the @Value
annotation (just based on spring being a very consistent framework).
Spring 3.1 has the new profile bean feature, which seems like it can accomplish something like what you're trying to do. Here's a write up for that:
http://blog.springsource.com/2011/02/14/spring-3-1-m1-introducing-profile/
Upvotes: 4
Reputation: 411
This works. You can leave off the service names if you just use the default spring bean name. serviceA vs ServiceA, etc.
@Controller
class MyController {
@Autowired(required=false)
@Qualifier("Service")
Service service;
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("app-ctx.xml", MyController.class);
for(String s:context.getBeanDefinitionNames()){
System.out.println(s);
for(String t:context.getAliases(s)){
System.out.println("\t" + t);
}
}
context.getBean(MyController.class).service.print();
}
}
public interface Service {
void print();
}
@Service(value="ServiceA")
public class ServiceA implements example.Service {
public void print() {
System.out.println("printing ServiceA.print()");
}
}
@Service(value="ServiceB")
public class ServiceB implements example.Service {
public void print() {
System.out.println("printing ServiceB.print()");
}
}
XML:
<beans>
<alias name="${service.class}" alias="Service"/>
<context:property-placeholder location="example/app.properties"/>
<context:component-scan base-package="example"/>
<beans>
Props:
service.class=ServiceB
Upvotes: 41
Reputation: 9255
Maybe give this a whirl:
@Controller
public class MyController {
private String serviceId;
@Value("${serviceId}")
public void setServiceId(String serviceId) {
this.serviceId = serviceId;
}
@Autowired
@Qualifier(serviceId)
Service service;
}
Upvotes: -6