#StepScope does not work with #ConstructorBinding in SpringBoot - java

I tried to make a #ConfigurationProperties bean without setters so I used #ConstructorBinding. Let's call this class PropertiesFromYml.java.
This works fine, bean is created perfectly by the properties from the yml file.
The configuration class has the necessary setup:
#Configuration
#ConfigurationPropertiesScan("com.my.package") // this is where PropertiesFromYml.java is stored
public class MyConfig
I wanted to add the #StepScope to that PropertiesFromYml.java because my SpringBoot application will run scheduled batch jobs, so I don't want to initiate this class, but only when the job needs it.
BUT: Unfortunately, the bean is always created during startup. I guess this is because of the #ConfigurationPropertiesScan annotation on MyConfig.java class. But how can I tell to create the bean only when necessary, like with #StepScope?

Configuration will be created at the start of your application. Instead what you may want to do is split your class into 2. The actual configuration part and the schedule part: SchedularConfiguration, SchedularComponent. And then inject the configuration into the component.
#StepScope
#Component
class SchedularComponent {
#Autowired
SchedularConfiguration config;
}
That will allow you to have the scope you want. Usually you wouldn't have a whole other class for configuration of a component (but your requirements may vary) and use directly the #Value annotation to select what application property you want: #Value("${schedular.cron}").

Related

How to access a property in .properties file during or before the main() method in Spring?

We have converted an existing web app over to Spring Boot.
All I want to to do is be able to use #Profile("production"). We store that in myProps.properties in the resources folder.
I can access the .properties file all over the app just fine using #Value, but it seems in order to get the #Profile to work, you have to set the active profiles before the ServletContext is set/final which seems to happen during the main() method.
I've tried dozens of things and failed. I've found you can't set a static field with #Value in the main application class. I've tried making a class that implements WebApplicationInitializer like https://www.baeldung.com/spring-profiles but my onStartup() method that I override doesn't ever get run.. I put in a break point and it never gets hit. The only way I can get a break point to hit is if I #Autowire the servletContext on that method, but then the context is already set and cannot be altered.
I've tried AnnotationConfigWebApplicationContext, I've tried ServletContainerInitializer, I've tried ConfigurableEnvironment, etc. I feel like I'm going in circles.
I feel like there is a big piece I'm missing here in order to do things the "Spring way". Can anyone offer Java annotation-configured way for me to get a property and set the active profiles for using later in the product? Thanks.
Why do you need it before main? Why don't you just create multiple properties files and define the profile in the top-level property file?
(parent) application.properties
spring.profiles.active = development-application.properties
(profile) development-application.properties
my.value = foo
Based on your comments, I suggest something like the following as a solution:
Define your tasks as beans with #Components and annotate them with #Profile to divide them up by the environment you want them to be used in (I prefer setting profiles as environment variables).
#Component
#Profile("production")
public class TaskA implements Task {
public void doWork(){}
}
#Component
#Profile("staging")
public class TaskB implements Task {
public void doWork(){}
}
Marking these classes with #Component means Spring will manage them and they can be injected, perhaps into a #Component that executes the tasks:
#Component
public class TaskDoer {
private List<Task> tasksToDo;
#Inject
public TaskDoer(List<Task> tasksToDo) {
this.tasksToDo = tasksToDo;
}
}
When deploying your app, set the profile to use in the environment variables in whatever way is appropriate for your setup:
SPRING_PROFILES_ACTIVE = production, someOtherProfile
Now, when the application starts, Spring will see that the active profiles are "production" and "someOtherProfile" (multiple profiles can be set) and will not load any beans with an #Profile that aren't for that profile.

How does spring recognize the param inside a constructor

I was trying to configure promethues in my code and I just had to create a bean like below. I was wondering how spring recognised CollectorRegistry. How did spring instantiate all the necessary variable for CollectorRegistry and setup all the necessary cofnfiguration?
#Component
public class TestProm{
public TestProm(CollectorRegistry registry){
// Some initialization code here
}
}
However, when I tried to define it in another way by defining a #Bean in my #Configuration class, it didn't seem to work properly as my own constructor for CollectorRegistry didn't have all the necessary properties.
#Configuration
public class PromConfiguration{
#Bean
public TestProm getTestProm() {
return new TestProm(new CollectorRegistry());
}
}
public class TestProm{
public TestProm(CollectorRegistry registry){
//Some code here
}
}
How do I recognise/replicate the initialization of CollectorRegistry done by spring when I do my custome implementation.
How did spring instantiate all the necessary variable for CollectorRegistry and setup all the necessary cofnfiguration?
In the first example you require a Bean of type CollectorRegistry and Spring will actually create such bean for you if you have spring-boot-starter-actuator and Prometheus dependencies on your classpath and you have autoconfiguration enabled :
if you use #EnableAutoConfiguration
if you are using Spring Boot and #SpringBootApplication annotation (#EnableAutoConfiguration is part of this annotation underneath) :
...
#EnableAutoConfiguration
#ConfigurationPropertiesScan
...
public #interface SpringBootApplication
In this case Spring will scan the classpath and load all configurations. The bean that you are interested in is part of PrometheusMetricsExportAutoConfiguration :
#Bean
#ConditionalOnMissingBean
public CollectorRegistry collectorRegistry() {
return new CollectorRegistry(true);
}
In the second example the instance of CollectorRegistry is not managed by Spring because you create it through new keyword. However it should work as CollectorRegistry has default constructor which initializes autoDescribe field to false. And the default Bean of this class which is created by Spring (in a way described above) has this field set to true. So the value of this field is the source of your differences.
Also if this instance is not managed by Spring - it prevents it to be injected into other components which require it. As the scope of CollectorRegistry is Singleton (shown above) other beans might require to share the instance to work properly (for example some beans might want to register/deregister collectors) but if you create CollectorRegistry like that with new keyword - you will not get the singleton instance but a new instance which cannot be shared across other beans.
How do I recognise/replicate the initialization of CollectorRegistry done by spring when I do my custom implementation?
If you want to use the default CollectorRegistry (assuming you want to use the bean that is created by default in the way described above) just inject bean of this type to your beans and it should be enough.

How to call Spring Framework repositories methods

I know that there are questions similar to this one, but none of them have helped me. I'm following along this tutorial, and the part I can't wrap my mind around is:
#SpringBootApplication
public class Application {
private static final Logger log =
LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
#Bean
public CommandLineRunner demo(CustomerRepository repository) {
return (args) -> {
// save a couple of customers
...
// more lines, etc...
What I don't understand is where the repository passed into demo comes from. I know that the Autowired annotation can do something like that, but it isn't used at all here.
The more specific reason I ask is because I'm trying to adapt what they do here to an application I'm working on. I have a class, separate from all of the persistence/repository stuff, and I want to call repository methods like save and findAll. The issue is that the repository is an interface, so I can't instantiate an object of it to call the methods. So do I have to make a new class that implements the interface and create an object of that? Or is there an easier way using annotations?
When creating a #Bean, adding the repository in the parameters of the bean is enough to wire the repos in your bean. This works pretty much like adding #Autowired annotation inside a class that is annotated as #Component or something similar.
Spring works mostly with interface, since that is simplier to wire vs wiring concrete classes.
Can you try #Repository before the declaration of class? Worked for me in a Spring MVC structure.
#Repository
public class EntityDAOImpl implements EntityDAO{
...
}
The thing to wrap your head around is a Spring Boot application at startup time aims to resolve its dependancy tree. This means discovering and instantiating Beans that the application defines, and those are classes annotated with #Service, #Repository, etc.
This means the default constructor (or the one marked with #Autowire) of all beans is invoked, and after all beans have been constructed the application starts to run.
Where the #Bean annotation comes into play is if you have a bean which does not know the values of it's constructor parameters at compile time (e.g. if you want to wire in a "started at" timestamp): then you would define a class with an #Configuration annotation on it, and expose an #Bean method in it, which would return your bean and have parameters that are the beans dependencies. In it you would invoke the beans constructor and return the bean.
Now, if you want a certain method of some class to be invoked after the application is resolved, you can implement the CommandLineRunner interface, or you can annotate a method with #PostConstruct.
Some useful links / references:
https://docs.spring.io/spring-javaconfig/docs/1.0.0.m3/reference/html/creating-bean-definitions.html
https://www.baeldung.com/spring-inject-prototype-bean-into-singleton
Running code after Spring Boot starts
Execute method on startup in Spring

How to tell if a class is Spring loaded in java?

I have been given a task to assign a property from .properties file to a non Spring bean class using #Value annotation. To do this, I created a method on a #Component annotated class and set the property into it, then called that method from the non Spring bean class. I thought this would work, however, still showing as null.
I was told this is because the #Component annotated class I used is not spring loaded. Question, how can I tell if a class is Spring loaded bean? I have been searching on google but can't find anything helpful aside from examples with #Component or #Configuration annotations. Thanks.
Spring Container is responsible for creating or managing beans. It all satisfy the dependencies by injecting them either through constructor or setter method. But in your case you want the #Value injection in your non spring bean which is really not possible as per my understanding. Because here the spring does not creating the object then how it satisfy the dependencies of it.
You have two options for this situation.
Either annotate class using #Component
Either read property file using Properties
https://www.mkyong.com/java/java-properties-file-examples/

Spring's #Value not working correctly when in CamelConfiguration subclass

I noticed a strange behaviour when using #Value with CamelConfiguration
Having an example properties file:
test.list=foo,bar,baz
and having a PropertySourcesPlaceholderConfigurer, a ConversionService and when referencing the property in some regular Spring configuration:
#Configuration
#PropertySource(value = "file:example.properties")
public class RegularConfig {
#Value("${test.list}")
List<String> testList;
}
Everything works as intended (testList contains three values: foo, bar, and baz), but when the configuration class extends org.apache.camel.spring.javaconfig.CamelConfiguration:
#Configuration
#PropertySource(value = "file:example.properties")
public class RegularConfig extends CamelConfiguration {
#Value("${test.list}")
List<String> testList;
}
(see minimal running example for both cases at https://github.com/michalmela/stackoverflow-questions/tree/master/35719697)
the testList contains one, joined value: foo,bar,baz.
Is this a misconfiguration on my side? Or some kind of bug (or feature)?
(I know the obvious workaround is to split the values manually, which is what I already went with, but I'd just like to understand what is going on here)
CamelConfiguration declare a BeanPostProcessor (camelBeanPostProcessor). BeanPostProcessor-s are instantiated by spring at first (because they have to see all others beans instantiation).
When Spring instantiate this camelBeanPostProcessor, it creates an instance of your class extending CamelConfiguration, inject the properties, and invoke camelBeanPostProcessor().
So, properties injected in this instance are injected at the beginning of the Spring ApplicationContext initialization. At this time, your ConversionService is not yet registered: The default converter is used, instead of the StringToCollectionConverter.
As a workaround, you can register explicitly a ConversionService before refreshing the applicationContext :
AnnotationConfigApplicationContext ctxt = new AnnotationConfigApplicationContext();
ctxt.getBeanFactory().setConversionService(new DefaultConversionService());
ctxt.register(...);
I know it may sound stupid but are you sure the configuration worked correctly before extending CamelConfiguration class? It doesn't look to me that #Value without using SpEL will split the list. I would use this configuration
#Value(value = "#{'${test.list}'.split(',')}")
Which version of Spring are you using?. Thanks

Categories

Resources