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
Related
I am learning Spring framework and using this reference,
I have a UkranianSongs class
import javax.annotation.Nonnull;
public class UkrainianSongs implements CompactDisk{
#Override
#Nonnull
public String getTitle(){
return "Ukranian Songs";
}
#Override
#Nonnull
public String getArtist(){
return "Skriabin";
}
}
I am creating a bean in CDPlayerConfig class
#Configuration
#ComponentScan
public class CDPlayerConfig {
#Bean
public CompactDisk anUkranianDisk(){
return new UkrainianSongs();
}
}
And I am autowiring compact disk class in another class
#Component
public class CompactDiskBox {
#Autowired
public CompactDisk anUkrainianDisk;
}
I wrote a JUnit test where I am trying to assert anUkranianDisk bean was initialized correctly and is not null
#RunWith(JUnit4.class)
#ContextConfiguration(classes = CDPlayerConfig.class)
public class CompactDiskBoxTest {
#Autowired(required = true)
public CompactDiskBox compactDiskBox;
#Test
public void testUkranianDisk(){
assertNotNull(compactDiskBox.anUkrainianDisk);
}
}
This throws a NullPointerException as compactDiskBox is null which means it couldn't initialize the bean right. Am I missing something here?
In order to be able to use dependency injection in your unit/integration tests, you have to set up the Spring TestContext Framework. In order to do this you should run your tests with SpringJUnit4ClassRunner.class or SpringRunner.class. SpringRunner.class is just an alias for SpringJUnit4ClassRunner.class.
I have that class as you can see below:
#Configuration
#PropertySource("classpath:sample.properties")
public class SampleConfig {
#Value("${attr1.prop}")
private String attr1;
#Value("${attr2.prop}")
private String attr2;
#Bean
public SampleService sampleService() {
return new SampleService(attr1);
}
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
I created SampleService bean with parameter attr1. Is possible get access to that properties which I load with #PropertySource later? For example after #Autowired.
Here is code of of using that bean:
#Service
public class SuperHotServiceImpl {
#Autowired
SampleService sammpleService;
public void fooFunc() {
// here I need some magic to get value of attr2.prop
sammpleService.setAttr(attr2);
}
}
Can you tell if it is possible an how? Thanks
If I understand properly your question, then this should help:
#Service
public class SuperHotServiceImpl {
#Autowired
private SampleConfig sammpleConfig;
public void fooFunc() {
System.out.println(sampleConfig.getAttr2());
}
}
Supposed that SampleConfig is annotated as #Component and that you provide the getter.
I have a Spring-Boot-Application as a multimodule-Project in maven. The structure is as follows:
Parent-Project
|--MainApplication
|--Module1
|--ModuleN
In the MainApplication project there is the main() method class annotated with #SpringBootApplication and so on. This project has, as always, an application.properties file which is loaded automatically. So I can access the values with the #Value annotation
#Value("${myapp.api-key}")
private String apiKey;
Within my Module1 I want to use a properties file as well (called module1.properties), where the modules configuration is stored. This File will only be accessed and used in the module. But I cannot get it loaded. I tried it with #Configuration and #PropertySource but no luck.
#Configuration
#PropertySource(value = "classpath:module1.properties")
public class ConfigClass {
How can I load a properties file with Spring-Boot and access the values easily? Could not find a valid solution.
My Configuration
#Configuration
#PropertySource(value = "classpath:tmdb.properties")
public class TMDbConfig {
#Value("${moviedb.tmdb.api-key}")
private String apiKey;
public String getApiKey() {
return apiKey;
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Calling the Config
#Component
public class TMDbWarper {
#Autowired
private TMDbConfig tmdbConfig;
private TmdbApi tmdbApi;
public TMDbWarper(){
tmdbApi = new TmdbApi(tmdbConfig.getApiKey());
}
I'm getting an NullPointerException in the constructor when I autowire the warper.
For field injection:
Fields are injected right after construction of a bean, before any config methods are invoked. Such a config field does not have to be public. Refer Autowired annotation for complete usage. Use constructor injection in this case like below:
#Component
public class TMDbWarper {
private TMDbConfig tmdbConfig;
private TmdbApi tmdbApi;
#Autowired
public TMDbWarper(final TMDbConfig tmdbConfig){
this.tmdbConfig = tmdbConfig;
tmdbApi = new TmdbApi(tmdbConfig.getApiKey());
}
(or)
Use #PostConstruct to initialise like below:
#Component
public class TMDbWarper {
#Autowired
private TMDbConfig tmdbConfig;
private TmdbApi tmdbApi;
#PostConstruct
public void init() {
// any initialisation method
tmdbConfig.getConfig();
}
Autowiring is performed just after the creation of the object(after calling the constructor via reflection). So NullPointerException is expected in your constructor as tmdbConfig field would be null during invocation of constructor
You may fix this by using the #PostConstruct callback method as shown below:
#Component
public class TMDbWarper {
#Autowired
private TMDbConfig tmdbConfig;
private TmdbApi tmdbApi;
public TMDbWarper() {
}
#PostConstruct
public void init() {
tmdbApi = new TmdbApi(tmdbConfig.getApiKey());
}
public TmdbApi getTmdbApi() {
return this.tmdbApi;
}
}
Rest of your configuration seems correct to me.
Hope this helps.
Here is a Spring Boot multi-module example where you can get properties in different module.
Let's say I have main application module, dataparse-module, datasave-module.
StartApp.java in application module:
#SpringBootApplication
public class StartApp {
public static void main(String[] args) {
SpringApplication.run(StartApp.class, args);
}
}
Configuration in dataparse-module. ParseConfig.java:
#Configuration
public class ParseConfig {
#Bean
public XmlParseService xmlParseService() {
return new XmlParseService();
}
}
XmlParseService.java:
#Service
public class XmlParseService {...}
Configuration in datasave-module. SaveConfig.java:
#Configuration
#EnableConfigurationProperties(ServiceProperties.class)
#Import(ParseConfig.class)//get beans from dataparse-module - in this case XmlParseService
public class SaveConfig {
#Bean
public SaveXmlService saveXmlService() {
return new SaveXmlService();
}
}
ServiceProperties.java:
#ConfigurationProperties("datasave")
public class ServiceProperties {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
application.properties in datasave-module in resource/config folder:
datasave.message=Multi-module Maven project!
threads.xml.number=5
file.location.on.disk=D:\temp\registry
Then in datasave-module you can use all your properties either through #Value.
SaveXmlService.java:
#Service
public class SaveXmlService {
#Autowired
XmlParseService xmlParseService;
#Value("${file.location.on.disk: none}")
private String fileLocation;
#Value("${threads.xml.number: 3}")
private int numberOfXmlThreads;
...
}
Or through ServiceProperties:
Service.java:
#Component
public class Service {
#Autowired
ServiceProperties serviceProperties;
public String message() {
return serviceProperties.getMessage();
}
}
I had this situation before, I noticed that the properties file was not copied to the jar.
I made the following to get it working:
In the resources folder, I have created a unique package, then stored my application.properties file inside it. e.g: com/company/project
In the configuration file e.g: TMDBConfig.java I have referenced the full path of my .properties file:
#Configuration
#PropertySource("classpath:/com/company/project/application.properties")
public class AwsConfig
Build and run, it will work like magic.
You could autowire and use the Enviornment bean to read the property
#Configuration
#PropertySource(value = "classpath:tmdb.properties")
public class TMDbConfig {
#Autowired
private Environment env;
public String getApiKey() {
return env.getRequiredProperty("moviedb.tmdb.api-key");
}
}
This should guarantee that property is read from the context when you invoke the getApiKey() method regardless of when the #Value expression is resolved by PropertySourcesPlaceholderConfigurer.
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);
}
}
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;
}