Referring to #Autowired component in another component's constructor [duplicate] - java

Shown below is a snippet of code where I try and reference my ApplicationProperties bean. When I reference it from the constructor it is null, but when referenced from another method it is fine. Up until now I have not had no problem using this autowired bean in other classes. But this is the first time I have tried to use it in the constructor of another class.
In the code snippet below applicationProperties is null when called from the constructor but when referenced in the convert method it is not. What am I missing
#Component
public class DocumentManager implements IDocumentManager {
private Log logger = LogFactory.getLog(this.getClass());
private OfficeManager officeManager = null;
private ConverterService converterService = null;
#Autowired
private IApplicationProperties applicationProperties;
// If I try and use the Autowired applicationProperties bean in the constructor
// it is null ?
public DocumentManager() {
startOOServer();
}
private void startOOServer() {
if (applicationProperties != null) {
if (applicationProperties.getStartOOServer()) {
try {
if (this.officeManager == null) {
this.officeManager = new DefaultOfficeManagerConfiguration()
.buildOfficeManager();
this.officeManager.start();
this.converterService = new ConverterService(this.officeManager);
}
} catch (Throwable e){
logger.error(e);
}
}
}
}
public byte[] convert(byte[] inputData, String sourceExtension, String targetExtension) {
byte[] result = null;
startOOServer();
...
Below is s snippet from ApplicationProperties ...
#Component
public class ApplicationProperties implements IApplicationProperties {
/* Use the appProperties bean defined in WEB-INF/applicationContext.xml
* which in turn uses resources/server.properties
*/
#Resource(name="appProperties")
private Properties appProperties;
public Boolean getStartOOServer() {
String val = appProperties.getProperty("startOOServer", "false");
if( val == null ) return false;
val = val.trim();
return val.equalsIgnoreCase("true") || val.equalsIgnoreCase("on") || val.equalsIgnoreCase("yes");
}

Autowiring (link from Dunes comment) happens after the construction of an object. Therefore they will not be set until after the constructor has completed.
If you need to run some initialization code, you should be able to pull the code in the constructor into a method, and annotate that method with #PostConstruct.

To have dependencies injected at construction time you need to have your constructor marked with the #Autowired annoation like so.
#Autowired
public DocumentManager(IApplicationProperties applicationProperties) {
this.applicationProperties = applicationProperties;
startOOServer();
}

Yes, both answers are correct.
To be honest, this problem is actually similar to the post Why is my Spring #Autowired field null? .
The root cause of the error can be explained in the Spring reference doc (Autowired) , as follow:
Autowired Fields
Fields are injected right after construction of a bean, before any
config methods are invoked.
But the real reason behind this statement in Spring doc is the Lifecycle of Bean in Spring. This is part of Spring's design philosophy.
This is Spring Bean Lifecycle Overview:
Bean needs to be initialized first before it can be injected with properties such as field. This is how beans are designed, so this is the real reason.
I hope this answer is helpful to you!

Related

How to get boolean value from application.yml in SpringBoot

I have application.yml that looks like this:
feature:
toggles:
checksLoginAndRegistration: true
I am trying to get it in my class with #Value annotation, but it's not working.
public class UMLUserRepository implements UserRepository {
#Value("${feature.toggles.checksLoginAndRegistration}")
private boolean checksLoginAndRegistration;
private void validateLoginNow(LoginInfo info, User user) {
checkKnownBlock(info, user.username);
if(checksLoginAndRegistration){
try {
service.validateLogin(user.username);
} catch (ValidationException alidationException) {
throw new Exception(user.username);
}
}
}
When I debug the code my checksLoginAndRegistration variable is set to false.
According to the comments you have used #Value annotation within a simple POJO. Not inside a Spring Bean like #Component, #Service or #Configuration.
You cannot inject a value to a POJO class using #Value.
This annotation can be used for injecting values into fields in Spring-managed beans, and it can be applied at the field or constructor/method parameter level.
But still you get value false for checksLoginAndRegistration parameter because it is an primitive type which has a default value false. If you chaged it to boxed type Boolean you can see the value of checksLoginAndRegistration is null
Update
#ConfigurationProperties(prefix = "feature.toggles")
public class AppConfig {
private Boolean checksLoginAndRegistration;
}
Then update your UMLUserRepository class, (We make checksLoginAndRegistration is a dependency to UMLUserRepository class)
public class UMLUserRepository implements UserRepository {
private final Boolean checksLoginAndRegistration;
public UMLUserRepository(Boolean checksLoginAndRegistration) {
this.checksLoginAndRegistration = checksLoginAndRegistration;
}
}
This is the class where you crate instance of UMLUserRepository class. An it should be a Spring Bean.
#Component (or #Service)
public class ClassYouInitatingUMLUserRepository {
#Autowire
private AppConfig appConfig;
public void yourMethod() {
UMLUserRepository repo = new UMLUserRepository(appConfig.getChecksLoginAndRegistration());
}
I would encourage you to check the possibility to convert UMLUserRepository class to a Spring bean. Then this won't be needed.
Hmm, it seems like you do everything correctly. I can suggest what can go wrong
Is it all what file contains?
If not, check is there only one feature key or not. If there's another one, remove it.
Have you added #Configuration annotation to your configuration class?
If not, add it.

Overiding a Autowired object on class construction

I have a class that uses an autowired properties object. These properties have some configuration required in order for my communications to work properly. In my unit tests I wrote a scenario in which communication should fail, by overriding the properties object in the class constructor as follows:
public class TokenRetriever{
#Autowired
private TokenRepository repository;
#Autowired
private Properties properties;
//custom constructor for me to override the properties
public TokenRetriever(Properties properties){
this.properties = properties;
}
private Token retrieveToken() {
Token token = null;
try {
//communication to an endpoint using properties
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return token;
}
public Token getAccessToken() throws NullAccessToken {
Token token;
token = repository.findTop1ByExpiresAtGreaterThanOrderByExpiresAtDesc(LocalDateTime.now());
if (token == null) token = this.retrieveToken();
if (token == null) throw new NullAccessToken("Could not retrieve any tokens");
return token;
}
}
And this is my unit test:
#Test
void ShouldNotRetrieveAToken() {
//this is the property i'm changing in order to force a failure
properties.setClientId("dummy");
tokenRetriever = new TokenRetriever(properties);
Exception exception = assertThrows(NullAccessToken.class,
() ->
tokenRetriever.getAccessToken()
);
String expectedMessage = "Could not retrieve any tokens";
String actualMessage = exception.getMessage();
assertTrue(actualMessage.contains(expectedMessage));
}
Which works just fine when I run the unit test. However, when I build the project this fails because the error is not thrown. I assume this is because the overriding is not working. I'm new to spring boot and junits, so this probably has to do with spring lifecycles. How can I accomplish the properties overide in order for my junit to pass?
Constructor injection Does injection only when the object create.
if you want create another object with different property object you must use Setter-based dependency injection.
there is setter-based injection documentation https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-setter-injection
You are mixing constructor and field injection.
It's recommended to use constructor injection where possible. You also will not need an annotation.
private final TokenRepository repository;
private final Properties properties;
public TokenRetriever(TokenRepository repository, Properties properties){
this.repository = repository;
this.properties = properties;
}

My annotation #Value return null even it being used and called into component annotated classes

I'm using Spring and need some help:
I want to set one API key using application.properties instead of hardcoding it, but it always returns null. IntelliJ evaluates it correctly to the value I've set in the file.
I've already read other questions here and almost all solutions are saying that Spring can only "inject" those value anotations in managed classes, like Components, Beans, etc. That's what (think) I did and still got null!
Everything else is working as I intended. Any direction is appreciated!
My application.properties
api.someapiservice.key=08e...f
Class that uses the properties value:
#Component
public class ApiClient implements ApiClientInterface {
#Value("${api.someapiservice.key}")
private String API_KEY;
public ApiClient () {
System.out.println(API_KEY); //Returns null after spring log info: Initialized JPA EntityManagerFactory for persistence unit 'default'
...
}
Class that uses ApiClient:
#Component
public class SomeService {
private final SomeRepository someRepository;
private final ApiClient apiClient;
public PlaylistService(SomeRepository someRepository , ApiClient apiClient ) {
this.SomeRepository = SomeRepository;
this.apiClient = ApiClient;
}
Field injection can't possibly happen until after the instance is already constructed, so your #Value (or #Autowired) fields will always be null in the constructor. Move the #Value to a constructor parameter instead.
If you want to know what is the value of your #Value field on start up. You can use #PostConstruct annotation, or you can move #Value annotation on your class constructor.
private String API_KEY;
public ApiClient(#Value("${api.test.value}") String key) {
this.API_KEY= key;
System.out.println(this.API_KEY);
}
or using #PostConstruct Annotation
#Value("${api.someapiservice.key}")
private String API_KEY;
#PostConstruct
public void init() {
System.out.println(this.API_KEY);
}

How to create instance by interface using reflection?

I'm trying coding Spring's DI , just a simple example. There is a controller, and this #AutoWired is a Empty Annotation defined by me.
public class UserController {
#AutoWired
private UserServise userServise;// a empty interface
}
This is the code that implement Annotation injection:
UserController userController = new UserController();
Class<? extends UserController> clazz = userController.getClass();
Stream.of(clazz.getDeclaredFields()).forEach(field -> {
AutoWired annotation = field.getAnnotation(AutoWired.class);
if (annotation != null) {
field.setAccessible(true);
Class<?> type = field.getType();
try {
Object o = type.getDeclaredConstructor().newInstance();
field.set(userController, o);
} catch (Exception e) {
e.printStackTrace();
}
}
});
When the program runs into
Object o = type.getDeclaredConstructor().newInstance();
throws
java.lang.NoSuchMethodException: com.learning.servise.UserServise.<init>()
I guess program cannot find a constructor for a interface, So how can I create this instance for the injection?
I am not completely sure what you are trying to achieve. I'm assuming that UserService is an interface?
If so it cannot be instantiated. You must either a class which implements the interface.
So either write a class (can also be anonymous or lambda) or use a proxy:
Object instance = Proxy.newProxyInstance(type.getClassLoader(),
new Class<?>[]{type}, new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//implement your methods here
//determine which method you're in by checking the method arg
}
});
Don't know if this is what you're after, but it is my best guess.
But maybe you're going at this wrong. When you're trying to replicate Spring, it is important that you have a component or bean you can autowire. So you should probably focus on your #Bean annotation (or similar) first. You'd want some sort of registry which picks up annotated beans and then injects them into your Autowired fields. It seems you have this back-to-front.
You should first focus on registering beans to your framework and only when you have achieved that you should try to inject them.

Autowire property in #Component from PropertySourcesPlaceholderConfigurer [duplicate]

Shown below is a snippet of code where I try and reference my ApplicationProperties bean. When I reference it from the constructor it is null, but when referenced from another method it is fine. Up until now I have not had no problem using this autowired bean in other classes. But this is the first time I have tried to use it in the constructor of another class.
In the code snippet below applicationProperties is null when called from the constructor but when referenced in the convert method it is not. What am I missing
#Component
public class DocumentManager implements IDocumentManager {
private Log logger = LogFactory.getLog(this.getClass());
private OfficeManager officeManager = null;
private ConverterService converterService = null;
#Autowired
private IApplicationProperties applicationProperties;
// If I try and use the Autowired applicationProperties bean in the constructor
// it is null ?
public DocumentManager() {
startOOServer();
}
private void startOOServer() {
if (applicationProperties != null) {
if (applicationProperties.getStartOOServer()) {
try {
if (this.officeManager == null) {
this.officeManager = new DefaultOfficeManagerConfiguration()
.buildOfficeManager();
this.officeManager.start();
this.converterService = new ConverterService(this.officeManager);
}
} catch (Throwable e){
logger.error(e);
}
}
}
}
public byte[] convert(byte[] inputData, String sourceExtension, String targetExtension) {
byte[] result = null;
startOOServer();
...
Below is s snippet from ApplicationProperties ...
#Component
public class ApplicationProperties implements IApplicationProperties {
/* Use the appProperties bean defined in WEB-INF/applicationContext.xml
* which in turn uses resources/server.properties
*/
#Resource(name="appProperties")
private Properties appProperties;
public Boolean getStartOOServer() {
String val = appProperties.getProperty("startOOServer", "false");
if( val == null ) return false;
val = val.trim();
return val.equalsIgnoreCase("true") || val.equalsIgnoreCase("on") || val.equalsIgnoreCase("yes");
}
Autowiring (link from Dunes comment) happens after the construction of an object. Therefore they will not be set until after the constructor has completed.
If you need to run some initialization code, you should be able to pull the code in the constructor into a method, and annotate that method with #PostConstruct.
To have dependencies injected at construction time you need to have your constructor marked with the #Autowired annoation like so.
#Autowired
public DocumentManager(IApplicationProperties applicationProperties) {
this.applicationProperties = applicationProperties;
startOOServer();
}
Yes, both answers are correct.
To be honest, this problem is actually similar to the post Why is my Spring #Autowired field null? .
The root cause of the error can be explained in the Spring reference doc (Autowired) , as follow:
Autowired Fields
Fields are injected right after construction of a bean, before any
config methods are invoked.
But the real reason behind this statement in Spring doc is the Lifecycle of Bean in Spring. This is part of Spring's design philosophy.
This is Spring Bean Lifecycle Overview:
Bean needs to be initialized first before it can be injected with properties such as field. This is how beans are designed, so this is the real reason.
I hope this answer is helpful to you!

Categories

Resources