Is it possible to #Lazy init a Spring #Value?
e.g.
#Lazy
#Value("${someConfig}")
private String someConfig;
The scenario I'm specifically referring to, is a variable which is set via JNDI, and an embedded Tomcat container, which has some of it's JNDI variables initialised during Spring Boot loading... other scenarios I could think of where you'd want JIT variable population: It's "expensive" to retrieve a variable and you don't want to impact startup time, the variable isn't available at application startup, etc.
The above code gives the following error:
java.lang.IllegalArgumentException: Cannot subclass final class
java.lang.String
I imagine you could possibly achieve lazy-loaded variables by using a #ConfigurationProperties bean?
A follow up question: Can/Would a #Value-initialised variable be reinitialised (without an app restart), if the underlying variable source is changed (e.g. JNDI on the server)? i.e. re-retrieved
(I'm in the process of trying these last two scenarios)
You can give a try to such a setup. Downside is that it requires beans using this variable to be also declared as #Lazy.
#Bean(name = "myVar")
#Lazy
String foo(#Value("${someConfig}") String someConfig) {
return someConfig;
}
#Component
#Lazy
class SomeComponent {
#Autowired
#Qualifier("myVar")
String myVar;
}
Related
I need to assure data migration using mongock.
The #ChangeUnit class holds the logic for migration. It has a field annotated with #Value which is always null, even though I properly initialized in application.properties:
mongock.migration-scan-package=my.package
login-secret=test
Then the MigrationConfiguration looks as follows:
#ChangeUnit(id = "test", order = "001", author = "test")
#RequiredArgsConstructor
#Configuration
public class InitUsersChangeLog {
private final MyService service;
private final MongoTemplate template;
#Value("${login-secret}")
private String LOGIN;
#Execution
public void initUser() {
service.create(User.builder().login(LOGIN).build());
}
}
Main class:
#EnableMongock
#SpringBootApplication
public class MailServiceApplication {...}
My assumption is that this value is not injected properly into the MongockConfiguration bean. I tried to configure the bean manually (without using mongock.migration-scan-package=my.package) in the properties, but with no success.
As Mongock currently doesn't support #Value annotation you can try to use getProperty method from Environment bean. Environment bean can be injected same as other beans using constructor or Lombok annotations.
You want to change this:
#Value("your.key.property")
to that:
private final Environment env;
public void method(){
env.getProperty("your.key.property")
}
Mongock currently no supports #value injection via field o method parameter. We will provide that in a future minor release within version 5, but we can't give you dates, yet.
Extending MichalJ's answer, which is absolutely valid. I would like to add that the changeUnits are not retrieved by Mongock via Springboot, they are processed by Mongock independently. So the annotation #Configuration, #Component, etc. won't be taken into account and they could even be damaging.
Related to that, this code won't work, at least not in a near future:
#Value("${login-secret}")
private String LOGIN;
First, as said, Mongock doesn't support value currently, but the first approach will require the constructor parameter to have that #Value("${login-secret}"), not at the field level.
I have a test use case in which based on a boolean property we decide which one of 2 Spring Beans to create upon application startup.
For simplicity, let's call them ProfileServiceA and ProfileServiceB.
When the boolean property is set to false (the default) it creates ProfileServiceA and when it's true it creates ProfileServiceB.
This is done inside of a configuration class annotated with #Configuration.
My test is pretty simple, I want to verify which one of the Beans is loaded in the Spring Context when our application is starting based on that property.
For each of those Beans, when we create them, the constructor gets multiple objects in order to be instantiated.
Most of the objects I have annotated with the #MockBean because they are only being pointed to in the constructor like we usually do with Dependency Injection and that's not a problem for me.
One of those properties is not only pointed at in the constructor but is actually being used to build one of the fields.
in the constructor of ProfileService we do something like this:
this.url = securityProperties.getProfile().getProtocol() + "://" + securityProperties.getProfile().getHost() + ":" + securityProperties.getProfile().getPort() + "/" + securityProperties.getProfile().getEndpoint();
The SecurityProperties class has the inner static class Profile, So when the application context starts to load and this Bean is being instantiated in the constructor (even before the method annotated with #Before) I fail on NullPointerException because the object Profile is still null (The SecurityProperties is not because it's annotated with #MockBean).
The SecurityProperties and its inner static class Profile are not defined as Beans but still, I tried to set the Profile object inside of the test class to be also #MockBean or #Mock but this didn't solve the issue.
I figured that I probably need to inject "real" Objects into the context but I couldn't find a wiki/docs on how to inject "real" objects WITH Mocked ones to an Autowired Spring Bean.
The SecurityProperties is not a Spring Bean but a regular data object (POJO).
You can implement the BeanPostProcessor interface in your test code. In the implementation of postProcessBeforeInitialization, if the current bean name is securityProperties (or whatever name your SecurityProperties bean has) then mock the behavior of SecurityProperties::getProfile() to return an actual Profile instance to avoid the NPE.
This works because ProfileService depends on SecurityProperties so the latter will be initiated first and right after that your postProcessBeforeInitialization will be called, right before the instantiation of ProfileService
I am writing a spring application which is interactive and basically handles lots of commands like create, list, update, delete various types of resources.
For now, just assume a single run of the application handles only a single command and the program exits.
For all the classes to validate command, execute the command, required factory classes for each resource there is a separate class and each class is annotated with #Component annotation for spring to manage all the components.
There are also some of the manually defined beans by #Bean method.
Now that my application first identifies what kind of command is executed (create, delete, list, update, etc), I want the only beans of that command to be created and Autowired wherever required (after taking command from user) and I want to avoid the creation of dozens of beans related to other commands.
On searching, I came to know about Lazy instantiation of Beans that spring provides.
However, I am not sure if it is the weapon I am searching for.
What I tried
Very first I found #Lazy annotation, but since I want to lazily load all the Beans, I don't want to write #Lazy everywhere in each class.
Then I found setting below property in application.yml does the work.
spring:
main:
lazy-initialization: true
I tried that but still, it is not lazily creating the beans.
My application.yml files looks like this
spring:
main:
lazy-initialization: true
My main SpringBootApplication file looks like this:
#Slf4j
#SpringBootApplication
public class SpringBootApplication {
public static void main(String[] args) {
System.out.println("Loading Application...");
ApplicationContext context = SpringApplication.run(SpringBootApplication.class, args);
final AtomicInteger counter = new AtomicInteger(0);
log.info("**************** START: Total Bean Objects: {} ******************", context.getBeanDefinitionCount());
Arrays.asList(context.getBeanDefinitionNames())
.forEach(beanName -> {
log.info("{}) Bean Name: {} ", counter.incrementAndGet(), beanName);
});
log.info("**************** END: Total Bean: {} ******************", context.getBeanDefinitionCount());
}
}
My other classes looks like this:
#Component
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class MyClass1 implements ResourceCreator<MyClass2, MyClass3> {
private final RequestValidatorImpl requestValidator;
private final ResourceCreator resourceCreator;
#Override
public MyClass2 toImplementFunction(MyClass3 myclass3) {
//logic
}
On running the application, It prints all the classes where I annotated #Component as well as beans created by #Bean method.
I have also tried using below in Application.properties but still no use.
spring.main.lazy-initialization=true
Also, if you wish, please comment on whether I should use #Component for each Class like I am using or not and what is better practice instead.
I think you misunderstood the meaning of the lazy flag you are passing,
It means that the object will be created only when it is invoked but it does not say that it will not scan that package. Spring will scan all packages and store bean definition names but it will create the objects only when it is invoked, if you have passed the lazy flag to it.
You can verify this behaviour by checking the number of beans created when you pass the lazy flag as true and false.
you can check it as given below
ApplicationContext context = SpringApplication.run(SpringBootApplication.class, args);
System.out.println("count:"+context.getBeanDefinitionCount());
Edit on Apr/7th 2020 start
Another way to do that is create a constructor and use that to inject the autowired properties and print out a log when they enter the constructor.
I did the same in a sample project and below is he result, first one is for eager initialization and next one for lazy.
spring.main.lazy-initialization=false
Application logs
Inside Constructor
calling bean
inside bean method
spring.main.lazy-initialization=true
Application logs
calling bean
Inside Constructor
inside bean method
Edit on Apr/7th 2020 end
Please mark this as answered if I answered your question.
Thank you
Long story short:
For anyone wanting to lazily initialize their whole Spring Boot context, setting this property to true is the way to go:
spring.main.lazy-initialization=true
Pro tip:
It can be used in combination with the #Lazy annotation, set to false; so all the defined beans will use lazy initialization, except for those that we explicitly configure with #Lazy(false).
In such a way, the lazy initialization becomes opt-out instead of the default opt-in.
What I'm trying to achieve is have a #Resource with a dynamic name parameter. Specifically, I want to inject a DataSource object using #Resource(name = "{JNDI_NAME_PARAM}") because we can have many datasources configured in an application server, and the datasource used by the application is defined in an .xml or .config file. Since I do not know the name of the datasource during compile time I need to be able to get it at runtime. Right now I'm injecting a custom #ApplicationScoped bean which creates a datasource in its #PostConstruct method using InitialContext().lookup(). However I'm curious (mostly because it is more elegant) as to how I could achieve injection using the #Resource annotation.
I COULD create a custom default JNDI name in the app server and change the datasource it points to when needed but this can't work with more than one deployment and many times we have the application deployed twice, once in a test database and once in a production database so having the JNDI point at two different datasources at the same time.
You can use the Method based injection.
It requires the setter method (setMyDB).
public class Test {
public javax.sql.DataSource myDB;
#Resource(name="student")
private void setMyDB(javax.sql.DataSource ds) {
myDB = ds;
}
}
If the names are known, we can have multiple resources under
#Resources({
#Resource(your type)
#Resource(your type)
})
I have a circular reference in one of my projects at work using spring, which I am unable to fix, and fails with the following error at startup:
'org.springframework.security.authenticationManager': Requested bean is currently in creation: Is there an unresolvable circular reference?
I tried to recreate the same problem at a smaller level in a sample project (without all the details of my work project). I have however been unable to come up with a plausible scenario where spring fails with an error.
Here's what I have:
public class ClassA {
#Autowired
ClassB classB;
}
public class ClassB {
#Autowired
ClassC classC;
}
#Component
public class ClassC {
#Autowired
ClassA classA;
}
#Configuration
public class Config {
#Bean
public ClassA classA() {
return new ClassA();
}
#Bean
public ClassB classB() {
return new ClassB();
}
}
I have a similar scenario in my project, which fails, and I was expecting spring to complain in my sample project as well. But it works fine! Can someone give me a simple example of how to break spring with the circular reference error?
Edit: I fixed the issue using javax.inject.Provider. The only other difference in the 2 projects was the annotations used were javax.inject.Inject and javax.annotation.ManagedBean in place of #Autowired and #Component.
You could use #Lazy to indicate that the bean is lazily created, breaking the eager cycle of autowiring.
The idea is that some bean on the cycle could be instantiated as a proxy, and just at the moment it is really needed it will be initialized. This means, all beans are initialized except the one that is a proxy. Using it for the first time will trigger the configuration and as the other beans are already configured it will not be a problem.
From one Issue in Spring-Jira:
#Lazy annotation that can be used in conjunction with #Configuration
to indicate that all beans within that configuration class should be
lazily initialized. Of course, #Lazy may also be used in conjunction
with individual #Bean methods to indicate lazy initialization on a
one-by-one basis.
https://jira.springsource.org/browse/SJC-263
Meaning that annotating your bean as #Lazy would be enough. Or if you prefer just annotate the configuration class as #Lazy as follows:
#Configuration
#Lazy
public class Config {
#Bean
public ClassA classA() {
return new ClassA();
}
#Bean
public ClassB classB() {
return new ClassB();
}
}
If you implement an interface of your beans this will work quite well.
This is an old thread, so I guess you almost forgot about the issue, but I want to let you know about the mystery. I encountered the same problem, and mine didn't go away magically, so I had to resolve the problem. I'll solve your questions step by step.
1. Why you couldn't reproduce the circular reference exception?
Because Spring takes care of it. It creates beans and injects them as required.
2. Then why does your project produce the exception?
As #sperumal said, Spring may produce circular exception if you use constructor injection
According to the log, you use Spring Security in your project
In the Spring Security config, they do use constructor injection
Your beans which injects the authenticationManager had the circular reference
3. Then why has the exception gone away mystically?
The exception may or may not occur depends on the creation order of beans. I guess you made several *context.xml files or so, and load them with config something like below in web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:*-context.xml</param-value>
</context-param>
The xml files will be loaded by XmlWebApplicationContext class and the loading order of files are not guaranteed. It just loads files from the file system. The problem is here. There's no problem if the class loads the application context file first, because your beans are already created when they are used for the construction injection of Spring Security. But, if it loads the Spring Security context file first, the circular reference problem occurs, because Spring tries to use your beans in the constructor injection before they had been created.
4. How to solve the problem?
Force the loading order of the xml files. In my case, I loaded the security context xml file at the end of the application context file by using <import resource="">. The loading order can be changed depends on environments even with the same code, so I recommend setting the order to remove potential problems.
According to Spring documentation, it is possible to get Circular dependency issue or BeanCurrentlyInCreationException by using constructor injection.
The solution to fix the issue is to use setters instead of Constructor injection.
Reference http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/beans.html.