Spring Qualifier and property placeholder - java

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

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

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:
Use #Qualifier only for field which will be autowired. For services, to specify bean name, use #Service.
If you want standard bean name you don't need to use #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).
I based AppConfig on this answer: Spring Bean Alias in JavaConfig.
This solution is better than this Spring Qualifier and property placeholder, because you don't need XML.
Tested on Spring Boot 1.5.7.

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:
http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/beans/factory/annotation/Value.html
Notice they make special mention of using expressions in the annotation. For comparison, the docs for #Qualifier:
http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/beans/factory/annotation/Qualifier.html
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/

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

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;
}

Related

Spring: Mixing of wiring in JavaConfig with wiring using #Autowired

I am totally confused about mixing of "wiring in JavaConfig" and "wiring using #Autowired". I will tell you my problems in 4 scenarios:
(I am ok with mixing of #Autowired and stereotype annotations and I don't have any question about that. my problem is Javaconfig and #autowired)
Scenario 1:
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer() {
cd = new CompactDisc() {
#Override
public void play() {
System.out.println("123456");
}
};
}
#Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer();
}
}
For Example in this scenario, I see that #Autowired is effectless and cannot make Spring to invoke and use the parameterized constructor and no-arg constructor will be executed (because it is invoked in the #Bean method) and the output is the text "123456".
=================================================================
SCENARIO 2:
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer(compactDisc);
}
}
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
we wired those two beans in the config file. and I know that we do not need #Autowired at all.
=================================================================
SCENARIO 3:
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean()
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer() {
return new CDPlayer();
}
}
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
#Autowired
public void setCd(CompactDisc cd) {
this.cd = cd;
}
}
I know that if #Autowired is above of parameterized constructor, that constructor will not be executed but now that is above of setCd(), this method will be executed.
=================================================================
SCENARIO 4:
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer(compactDisc);
}
}
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer() {
}
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
#Autowired
public void doSomething(CompactDisc cd) {
this.cd = new CompactDisc() {
#Override
public void play() {
System.out.println("AAAAA");
}
};
}
}
and in this scenario, Although that we wired those two beans together, but #Autowired makes spring to execute the doSomething()method.
What is happening?! I can't see the Big Picture. I can't understand the pattern that is going on.
sometimes #Autowired works and sometimes doesn't work. what is the general pattern? do we need #Autowired at all when we wire beans together in JavaConfig file?
An autowired constructor is invoked if spring invokes the constructor by reflection, typically because you declare the bean using component scanning or XML config. If you manually invoke a constructor in a #Bean method, that constructor executes, and #Autowired has no effect.
An autowired method is invoked after the bean has been created, irrespective of how the bean was created.
The reason is that, in Java, each constructor call creates a new object, making it impossible to call two constructors for the same object. That's why Spring can't call a second constructor if you have already called a different one. In contrast, it is possible to call many methods on the same object, so Spring does support method autowiring just fine.
To conclude, you can use autowiring with JavaConfig, but you should autowire fields or methods rather than constructors. Or you can do without autowiring, and pass everything explicitly in your #Bean method. Or any mixture of the two.
The Spring Context contains all the beans you need in your program, and Spring do the rest of the job for you. But something to understand is that your beans comes from many parts of your application :
Internal beans (POJO from your domain).
External beans (POJO from other libraries or third partie classes).
Reading this from the spring documentation, you can find all the differents sources of beans :
#SpringBootApplication is a convenience annotation that adds all of
the following:
#Configuration: Tags the class as a source of bean definitions for the
application context.
#EnableAutoConfiguration: Tells Spring Boot to start adding beans
based on classpath settings, other beans, and various property
settings. For example, if spring-webmvc is on the classpath, this
annotation flags the application as a web application and activates
key behaviors, such as setting up a DispatcherServlet.
#ComponentScan: Tells Spring to look for other components,
configurations, and services in the com/example package, letting it
find the controllers.
Follow these rules :
In your domain classes (Controller, Service) : use #Autowired in your constructor. It is the recommanded way to inject your dependencies.
You want to use external classes : implements a Java Configuration with #Configuration annotation, to instanciate your external classes as beans.
You want to create custom utilities classes : decorate it with #Component.
When you have more than on implementation, use #Qualifier and define your beans in a #Configuration class.

SpringBoot application doesn't autowire field

I do have ServiceImpl which looks like this:
#Service
#RequiredArgsConstructor
public class ServiceAImpl implements ServiceA {
private final String fieldA;
#Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
And I would like to inject a field value to fieldA in an Application.java from application.yml like this:
#EnableSwagger2
#SpringBootApplication
public class Application {
#Value("${fieldA}")
private String fieldA;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServiceA serviceA() {
return new ServiceAImpl(fieldA);
}
But I receive the following error when running SpringBoot app:
Error creating bean with name 'serviceAImpl' defined in URLNo qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Do you have any solution for that?
You annotated your class with #Service and defined it manually as a bean with the #Bean annotation. I do think the second is the way you planned to use it.
The #Service annotation will make this class get picked up by Spring's component scan and additionally create an instance of it.
Of course it tries to resolve the parameters and fails when it tries to find a matching "bean" for the String field because there is no simple String bean (and should not :) ).
Remove the #Service annotation and everything should work as expected.
Try this
#Service
public class ServiceAImpl implements ServiceA {
private final String fieldA;
#Autowire
public ServiceAImpl(#Value("${fieldA}") String fieldA){
this.fieldA = fieldA;
}
#Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
}
and this
#EnableSwagger2
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
You should not use #Service and #Bean for the same class!
Spring is not so smart :)
You should annotate your bean like:
#RequiredArgsConstructor
public class ServiceAImpl {
#Value("${fieldA}")
private final String something;
...
But I'm not sure it will work with the #RequiredFieldsConstructor, it would be simpler for you write down the constructor annotated with #Autowired and using the #Value annotation for the String parameter:
#Autowired
public ServiceAImpl(#Value("${aProp}") String string) {
You're using two bean declaration mechanisms:
You're registering your bean using #Service
You're registering a bean using #Bean
This means that your service will be created twice. The one defined using #Bean works properly, since it uses the #Value annotation to inject the proper value in your service.
However, the service created due to #Service doesn't know about the #Value annotation and will try to find any bean of type String, which it can't find, and thus it will throw the exception you're seeing.
Now, the solution is to pick either one of these. If you want to keep the #Bean configuration, you should remove the #Service annotation from ServiceAImpl and that will do the trick.
Alternatively, if you want to keep the #Service annotation, you should remove the #Bean declaration, and you should write your own constructor rather than relying on Lombok because this allows you to use the #Value annotation within the constructor:
#Service
public class ServiceAImpl implements ServiceA {
private final String fieldA;
/**
* This constructor works as well
*/
public ServiceAImpl(#Value("${fieldA}") String fieldA) {
this.fieldA = fieldA;
}
#Override
public boolean isFieldA(String text){
return fieldA.equals(text);
}
}
If you want to declare ServiceAImpl as a Spring bean in your Java Configuration file, you should remove the #Service annotation from the class declaration. These annotations doesn't work well together.
ServiceAImpl.java
import org.springframework.beans.factory.annotation.Autowired;
public class ServiceAImpl implements ServiceA {
private final String fieldA;
#Autowired
public ServiceAImpl(String fieldA) {
this.fieldA = fieldA;
}
#Override
public boolean isFieldA(String text) {
return fieldA.equals(text);
}
}
Application.java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
public class Application {
#Value("${fieldA}")
private String fieldA;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServiceA serviceA() {
return new ServiceAImpl(fieldA);
}
}
Your application.properties
fieldA=value
The below implementation works well for me. You have two issues, first you have to choose between #Service and #Bean and the other issue I've seen in your code was the #Value annotation, you have to use only to inject a value from the properties.
#SpringBootApplication
public class TestedValueApplication {
#Autowired
void printServiceInstance(ServiceA service) {
System.out.println("Service instance: " + service);
System.out.println("value==value? " + service.isFieldA("value"));
}
public static void main(String[] args) {
SpringApplication.run(TestedValueApplication.class, args);
}
#Bean
public ServiceA serviceA(#Value("${fieldA}") String fieldA) {
return new ServiceAImpl(fieldA);
}
}
Service:
public class ServiceAImpl implements ServiceA {
private String fieldA;
ServiceAImpl(String fieldA) {
this.fieldA = fieldA;
}
public boolean isFieldA(String text) {
return fieldA.equals(text);
}
}
application.properties:
fieldA=value

How to extend #Service?

Here is a sample class below:
#Service("testService")
public class TestService {
public String something() {
return "abc";
}
}
I want to extend the class and let the container know that it needs to pick up my extended class from now.
#Service("extendedTestService")
public class ExtendedTestServiceMock extends TestService {
#Override
public String something() {
return "xyz";
}
}
Test class:
public class TestClass extends SpringTest {
#Autowired
#Qualifier("extendedTestService")
private ExtendedTestService testService;
public void testMethod() {
......
}
}
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [TestService] is defined: expected single matching bean but found 2: ExtendedTestServiceMock,testService
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:865) ~[spring-beans-3.2.8.RELEASE.jar:3.2.8.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:770) ~[spring-beans-3.2.8.RELEASE.jar:3.2.8.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:489) ~[spring-beans-3.2.8.RELEASE.jar:3.2.8.RELEASE]
... 91 common frames omitted
How to resolve it?
Try using interfaces.
public interface TestService {
String something();
}
Implementations:
#Service
#Qualifier("testService")
public class TestServiceImpl implements TestService { ... }
#Service
#Qualifier("testServiceMock")
public class TestServiceMockImpl implements TestService { ... }
And the test class:
public class TestClass extends SpringTest {
#Autowired
#Qualifier("extendedTestService")
private TestService testService;
...
}
One solution that would work in your case is the #Primary annotation.
Your TestServiceMockImpl would look like:
#Service("extendedTestService ")
#Primary
public class ExtendedTestServiceMock extends TestService {
#override
public String something() {
return "xyz";
}
}
Check out this for more details on #Primary
I however suggest that you don't follow the above solution (since this will get out of hand very quick if you start using #Primary everywhere), that you instead take a look at Spring Profiles
There are a lot of way you could create your Spring configuration using profiles, but regardless of how you end up configuring the beans, the end result would be a more clean design.
If you have an identifier to help you decide which service to initialize, then you can use ConditionlOnProperty annotation
Ex:
#Service
#ConditionlOnProperty(value = "test.service.extension.enabled")
public class TestService {
}
#Service
#ConditionlOnProperty(value = "test.service.extension.enabled", havingValue = "false")
public class ExtendedTestServiceMock extends TestService {
}
If you want to use the extended test service, you can set the property test.service.extension.enabled=true in your application.properties
It depends on your definition order if your service define on the xml file.
Otherwise, you could use a BeanFactoryPostProcessor to do this, which is only registered in the test scenarios that you want this mocked.
public class SystemTestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
// put your custom code in here
}
}
Or you can use #DependsOn to make sure the parent bean should be deploy firstly then your extend bean
#Service("testService")
#DependsOn("testService")
public class ExtendedTestService extends TestService {
}
Hope this helps.

Spring Autowiring a property within an autowired bean

I'm a newbie to Spring. I'm facing a problem with Spring-Boot. I'm trying to autowire a field from an external config file into an autowired bean. I have the following classes
App.java
public class App {
#Autowired
private Service service;
public static void main(String[] args) {
final SpringApplication app = new SpringApplication(App.class);
//app.setShowBanner(false);
app.run();
}
#PostConstruct
public void foo() {
System.out.println("Instantiated service name = " + service.serviceName);
}
}
AppConfig.java
#Configuration
#ConfigurationProperties
public class AppConfig {
#Bean
public Service service() {
return new Service1();
}
}
Service Interface
public interface Service {
public String serviceName ="";
public void getHistory(int days , Location location );
public void getForecast(int days , Location location );
}
Service1
#Configurable
#ConfigurationProperties
public class Service1 implements Service {
#Autowired
#Value("${serviceName}")
public String serviceName;
//Available in external configuration file.
//This autowiring is not reflected in the main method of the application.
public void getHistory(int days , Location location)
{
//history code
}
public void getForecast(int days , Location location )
{
//forecast code
}
}
I'm unable to display the service name variable in the postconstruct method of the App class. Am I doing this right?
You can load properties in different ways:
Imagine the following application.properties which is automatically loaded by spring-boot.
spring.app.serviceName=Boot demo
spring.app.version=1.0.0
Inject values using #Value
#Service
public class ServiceImpl implements Service {
#Value("${spring.app.serviceName}")
public String serviceName;
}
Inject values using #ConfigurationProperties
#ConfigurationProperties(prefix="spring.app")
public class ApplicationProperties {
private String serviceName;
private String version;
//setters and getters
}
You can access to this properties from another class using #Autowired
#Service
public class ServiceImpl implements Service {
#Autowired
public ApplicationProperties applicationProperties;
}
As you can notice the prefix will be spring.app then spring-boot will match the properties prefix with that and look for serviceName and version and values will be injected.
Considering you have you class App annotated with #SpringBootApplication and App class in the top package You can put your serviceName inside application.properties and inject it using #Value("${serviceName}"). Do not use #Component on a class if you are already using #Bean on configuration it will clash, and so #Autowired with #Value
See docs for more info
You will end with something like
#Service // #Component specialization
public class Service1 implements Service {
#Value("${serviceName}")
public String serviceName;
//Available in external configuration file.
//This autowiring is not reflected in the main method of the application.
public void getHistory(int days , Location location)
{
//history code
}
public void getForecast(int days , Location location )
{
//forecast code
}
}
No need for #Bean declaration when you have #Component/#Service/#Repository
#Configuration
public class AppConfig { //other stuff here not duplicated beans }
And your main class
package com.app;
#SpringBootApplication // contains #EnableAutoConfiguration #ComponentScan #Configuration
public class App {
#Autowired
private Service service;
public static void main(String[] args) {
final SpringApplication app = new SpringApplication(App.class);
//app.setShowBanner(false);
app.run();
}
#PostConstruct
public void foo() {
System.out.println("Instantiated service name = " + service.serviceName);
}
}

Spring MVC multiple controllers of the same class

I want to have one controller class, but 4 instances of it, each of instance will have own datasource and controller path, everything else (methods, validations rules, views names) will be the same;
So i need something like this :
class MyController{
private MyService service;
#RequestMapping("somework")
public String handleRequest(){
........
}
....................
}
Configuration class :
#Configuration
#EnableWebMvc
public class AppConfiguration {
#Controller // assuming it exists to get the
#RequestMapping('con1') // desired result
MyController controller1(){
MyController con = new MyController();
con.setService(service1Bean);
return con;
}
#Controller // assuming it exists to get the
#RequestMapping('con2') // desired result
MyController controller2(){
MyController con = new MyController();
con.setService(service2Bean);
return con;
}
...............................
}
No, you can't do this.
First, annotations are a set in stone at compile time. They are constant meta data that you cannot modify. So even though, they are accessible at run time through reflection, you cannot modify them.
Second, the #Controller annotation call only be used to annotate types. You cannot use it on a method. There is no corresponding annotation in Spring MVC that does what you want in your example. (You could always write your own.)
Finally, the Spring MVC stack registers your #Controller beans' methods as handlers mapping them to the various URL patterns you provide. If it tries to register a pattern that has already been registered, it fails because duplicate mappings are not allowed.
Consider refactoring. Create a #Controller class for each path you want but move the logic to a #Service bean which you can customize to use whatever data source you need.
You may achieve what you want by implementing an abstract superclass of
your controller, with constructor parameters for your service.
Then you should write derive your controllers from the abstract superclass,
with a constructor, where you inject your concrete service implementation:
public abstract class MyBaseController {
private MyService service;
public MyBaseController(final MyService service) {
this.service = service;
}
...
#RequestMapping("method1")
public ... method1( ... ) {
...
}
}
#Controller
#RequestMapping("con1")
public MyController1 extends MyBaseController {
#Autowired
public MyController1(#Qualifier("con1") final MyService service) {
super(service);
}
}
#Controller
#RequestMapping("con2")
public MyController2 extends MyBaseController {
#Autowired
public MyController1(#Qualifier("con2") final MyService service) {
super(service);
}
}
#Configuration
public class MyConfiguration {
#Bean(name = "con1")
public MyService serviceCon1() {
return ...;
}
#Bean(name = "con2")
public MyService serviceCon2() {
return ...;
}
}

Categories

Resources