I'm developing a Java Spring application. I have some fields in my application which are configured using a .yml config file. I would like to import those values using an #Value annotation on the fields in question. I would also like to use the best-practice of constructor injection rather than field injection, but I would like to write my constructor using Lombok rather than manually. Is there any way to do all these things at once? As an example, this doesn't work but is similar to what I want to do:
#AllArgsConstructor
public class my service {
#Value("${my.config.value}")
private String myField;
private Object myDependency;
...
}
In this case, what I want is Lombok to generate a constructor which sets only myDependency, and for myField to be read from my config file.
Thanks!
You need #RequiredArgsConstructor and mark myDependency as final. In this case, Lombok will generate a constructor based on 'required' final filed as argument, for example:
#RequiredArgsConstructor
#Service
public class MyService {
#Value("${my.config.value}")
private String myField;
private final MyComponent myComponent;
//...
}
That is equal the following:
#Service
public class MyService {
#Value("${my.config.value}")
private String myField;
private final MyComponent myComponent;
public MyService(MyComponent myComponent) { // <= implicit injection
this.myComponent = myComponent;
}
//...
}
Since here is only one constructor, Spring inject MyComponent without the explicit use of the #Autowired annotation.
Male sure you are using at least version 1.18.4 of Lombok. And that you have your desired annotation added to the lombok.config file.
lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Value
Here is your class:
#AllArgsConstructor(onConstructor = #__(#Autowired))
public class MyService{
#Value("${my.config.value}")
private String myField;
private Object myDependency;
}
And here is the lombok generated class:
public class MyService {
#Value("${my.config.value}")
private String myField;
private Object myDependency;
#Autowired
#Generated
public MyService(#Value("${my.config.value}") final String myField, final Object myDependency) {
this.myField = myField;
this.myDependency = myDependency;
}
PS: Make sure you have the lombok.config file under /src/main/java folder. I tried adding it to /src/main/resources and it did not work.
Response taken from here.
Related
I wonder whether it's possible to replace all the Autowired fields with final ones and #RequiredArgsConstructor below the class declaration?
For instance, replace the following code
public class Controller {
#Autowired
private Reposiroty repository;
#Autowired
private Service service;
...
}
with something like that:
#RequiredArgsConstructor
public class Controller {
private final Reposiroty repository;
private final Service service;
...
}
Thanks in advance!
I am trying to load the api key value from the application.properties file and below are the class files. I am unable to start the application as it is not able to find the unique bean. Not sure what i am missing. Can someone please help.
This is our AppProperties.java
#Component
#PropertySource("classpath:application.properties")
#ConfigurationProperties(prefix = AppProperties.APP_PROPERTIES_PREFIX)
public class AppProperties {
public static final String APP_PROPERTIES_PREFIX = "bi";
private String accessTokenUri;
private String clientId;
private String clientSecret;
private String basicAuth;
private String apiKey;
//getters and setters
}
This is our DiagnosticProperties.java
#Component
#PropertySource("classpath:application.properties")
#ConfigurationProperties(prefix = "bi")
public class DiagnosticProperties extends AppProperties {
private String diagnosisUrl;
//getters and setters
}
This is our ObservationProperties.java
#Component
#PropertySource("classpath:application.properties")
#ConfigurationProperties(prefix = "bi")
public class ObservationProperties extends AppProperties {
private String observationUrl;
//getters and setters
}
This is our DiagnosticServiceImpl.java
#Service
public class DiagnosticServiceImpl implements DiagnosticService {
private static final Logger LOGGER =
LoggerFactory.getLogger(ObservationServiceImpl.class);
private final WebClient webClient;
private final DiagnosticProperties diagnosticProperties;
public DiagnosticServiceImpl(final WebClient webClient,final DiagnosticProperties
diagnosticProperties) {
this.webClient = webClient;
this.diagnosticProperties = diagnosticProperties;
}
#Override
public Mono<DiagnosticResponse> getPatientDiagnosticDetails(final String uri) {
return diagnosticDetails(uri);
}
You should not put any annotations on the AppProperties (that could have been an abstract class). The classes that inherit from it only need #ConfigurationProperties(prefix = "..") and #Component or they could be also enabled with #EnableConfigurationProperties from another configuration class.
When you inject - be specific about which configuration properties you want to inject - either by specifying a type - like you did in your example, or by adding #Qualifier("bean-name") to the parameter on the injection point.
Spring Boot out-of-the-box configures application.properties property source.
I'm trying to mock a private field in my class which is being initialized by OSGI container in which my app is running. I'm putting a sample code for reference, any clue on this please:
import org.apache.felix.scr.annotations.*
#Component (name = "MyServiceImpl ", ds = true, immediate = true)
#Service
public class MyServiceImpl extends MyBasee implements MyService {
#Reference (name = "MyOtherService", bind = "bind", unbind = "unbind", policy = ReferencePolicy.STATIC)
private MyOtherService myServiceRegistryConsumer;
}
Here I'm trying to mock private field MyOtherService myServiceRegistryConsumer
With Mockito you can mock and inject fields using the #InjectMocksannotation.
#RunWith(MockitoJUnitRunner.class)
public class AppTest {
#Mock
private MyOtherService myServiceRegistryConsumer;
#InjectMocks
private MyServiceImpl myServiceImpl;
#Test
public void testSomething() {
// e.g. define behavior for the injected field
when(myServiceRegistryConsumer.methodA()).thenReturn(/* mocked return value */);
}
}
I have created a repository but when I call my repository it gives a NullPointerException everytime. Can someone help me figure out why?
My repository
#Repository
public interface WorkflowObjectRepository extends CrudRepository<WorkflowObject, String> {
#Override
WorkflowObject findOne(String id);
#Override
void delete(WorkflowObject workflowObject);
#Override
void delete(String id);
#Override
WorkflowObject save(WorkflowObject workflowObject);
}
My Object
#Data
#Entity
#Table(name = "workflowobject")
public class WorkflowObject {
#GeneratedValue
#Column(name="id")
private String id;
#Column(name = "state_name")
private String stateName;
}
My test
public class Test {
#Autowired
static WorkflowObjectRepository subject;
public static void main(String[] args) {
final WorkflowObject obj = new WorkflowObject();
obj.setId("maha");
obj.setStateName("test");
subject.findOne("maha");
}
}
application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/vtr?
autoReconnect=true
spring.datasource.username=vtr
spring.datasource.password=vtr
The problem is you are trying to autowire a static data member
#Autowired
static WorkflowObjectRepository subject;
What happens in your case is static is getting initialized before the bean so you are autowiring on null, just remove the static and deal with it as instance variable.
repositories are singletones so no point of making them static
In order to work properly with #Autowired you need to keep in mind that spring scans annotations to allow automatically classes load.
If you need to #Autowired some class, the class Test needs to have some annotation, that tells to Spring Boot to find it.
Your Test class need to have #Component, #Service, #Controller, #Repository, etc. Otherwise #Autowired will not work because Spring Boot not know your class and will not process it.
You can find some explanation here
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
Is it auto detected with #Autowired?
Is it dependency injection by name when #Qualifier is used?
How can we do setter and constructor injection using these annotations?
You can use #Qualifier along with #Autowired. In fact spring will ask you explicitly select the bean if ambiguous bean type are found, in which case you should provide the qualifier
For Example in following case it is necessary provide a qualifier
#Component
#Qualifier("staff")
public Staff implements Person {}
#Component
#Qualifier("employee")
public Manager implements Person {}
#Component
public Payroll {
private Person person;
#Autowired
public Payroll(#Qualifier("employee") Person person){
this.person = person;
}
}
EDIT:
In Lombok 1.18.4 it is finally possible to avoid the boilerplate on constructor injection when you have #Qualifier, so now it is possible to do the following:
#Component
#Qualifier("staff")
public Staff implements Person {}
#Component
#Qualifier("employee")
public Manager implements Person {}
#Component
#RequiredArgsConstructor
public Payroll {
#Qualifier("employee") private final Person person;
}
provided you are using the new lombok.config rule copyableAnnotations (by placing the following in lombok.config in the root of your project):
# Copy the Qualifier annotation from the instance variables to the constructor
# see https://github.com/rzwitserloot/lombok/issues/745
lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier
This was recently introduced in latest lombok 1.18.4.
The blog post where the issue is discussed in detail
The original issue on github
And a small github project to see it in action
NOTE
If you are using field or setter injection then you have to place the #Autowired and #Qualifier on top of the field or setter function like below(any one of them will work)
public Payroll {
#Autowired #Qualifier("employee") private final Person person;
}
or
public Payroll {
private final Person person;
#Autowired
#Qualifier("employee")
public void setPerson(Person person) {
this.person = person;
}
}
If you are using constructor injection then the annotations should be placed on constructor, else the code would not work. Use it like below -
public Payroll {
private Person person;
#Autowired
public Payroll(#Qualifier("employee") Person person){
this.person = person;
}
}
The #Qualifier annotation is used to resolve the autowiring conflict, when there are multiple beans of same type.
The #Qualifier annotation can be used on any class annotated with #Component or on methods annotated with #Bean. This annotation can also be applied on constructor arguments or method parameters.
Ex:-
public interface Vehicle {
public void start();
public void stop();
}
There are two beans, Car and Bike implements Vehicle interface
#Component(value="car")
public class Car implements Vehicle {
#Override
public void start() {
System.out.println("Car started");
}
#Override
public void stop() {
System.out.println("Car stopped");
}
}
#Component(value="bike")
public class Bike implements Vehicle {
#Override
public void start() {
System.out.println("Bike started");
}
#Override
public void stop() {
System.out.println("Bike stopped");
}
}
Injecting Bike bean in VehicleService using #Autowired with #Qualifier annotation. If you didn't use #Qualifier, it will throw NoUniqueBeanDefinitionException.
#Component
public class VehicleService {
#Autowired
#Qualifier("bike")
private Vehicle vehicle;
public void service() {
vehicle.start();
vehicle.stop();
}
}
Reference:- #Qualifier annotation example
#Autowired to autowire(or search) by-type
#Qualifier to autowire(or search) by-name
Other alternate option for #Qualifier is #Primary
#Component
#Qualifier("beanname")
public class A{}
public class B{
//Constructor
#Autowired
public B(#Qualifier("beanname")A a){...} // you need to add #autowire also
//property
#Autowired
#Qualifier("beanname")
private A a;
}
//If you don't want to add the two annotations, we can use #Resource
public class B{
//property
#Resource(name="beanname")
private A a;
//Importing properties is very similar
#Value("${property.name}") //#Value know how to interpret ${}
private String name;
}
more about #value