After study and using spring boot, I understand the usage and the logic behind the spring boot ConditionalOnClass, my questions are:
why "Since this annotation is parsed by loading class bytecode, it is safe to specify classes here that may ultimately not be on the classpath".
Where is the JVM spec related this?
https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnClass.java#L39
/**
* The classes that must be present. Since this annotation is parsed by loading class
* bytecode, it is safe to specify classes here that may ultimately not be on the
* classpath, only if this annotation is directly on the affected component and
* <b>not</b> if this annotation is used as a composed, meta-annotation. In order to
* use this annotation as a meta-annotation, only use the {#link #name} attribute.
* #return the classes that must be present
*/
Class<?>[] value() default {};
Because Spring catches ClassNotFoundException.
Check source code - https://github.com/spring-projects/spring-boot/blob/d3c34ee3d1bfd3db4a98678c524e145ef9bca51c/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnClassCondition.java#L218
Related
When writing web application with Spring Boot you can declare your beans to be part of the session- or request scope.
Is it possible to create a scope for injection of data with messages received by a #RabbitListener?
When receiving a message with a the RabbitListener, I would like to initialize some metadata which I would like to have available for injection in code called during message processing. (e.g. receiving queue for logging, or a pre-configured factory where the configuration depends on parameters known at receiving time of the message).
Passing this data through all called methods feels just ugly.
Ways I could think of, but don't know how to do it:
AOP
Custom scopes
Help is greatly appreciated!
Probably the simplest approach would be to use a MessagePostProcessor (add to the container/container factory via the afterReceivePostProcessors property).
/**
* Set {#link MessagePostProcessor}s that will be applied after message reception, before
* invoking the {#link MessageListener}. Often used to decompress data. Processors are invoked in order,
* depending on {#code PriorityOrder}, {#code Order} and finally unordered.
* #param afterReceivePostProcessors the post processor.
* #since 1.4.2
*/
public void setAfterReceivePostProcessors(MessagePostProcessor... afterReceivePostProcessors) {
Assert.notNull(afterReceivePostProcessors, "'afterReceivePostProcessors' cannot be null");
Assert.noNullElements(afterReceivePostProcessors, "'afterReceivePostProcessors' cannot have null elements");
this.afterReceivePostProcessors = MessagePostProcessorUtils.sort(Arrays.asList(afterReceivePostProcessors));
}
Use a ThreadLocal to store the metadata; you can then access the metadata in the listener.
I'm using Spring Integration in my Spring Boot project.
My spring boot application gives error like this:
Caused by: java.lang.ClassCastException: class java.lang.String cannot be cast to class java.util.List (java.lang.String and java.util.List are in module java.base of loader 'bootstrap')
at org.springframework.integration.support.MessageBuilder.getSequenceDetails(MessageBuilder.java:206)
at org.springframework.integration.support.AbstractIntegrationMessageBuilder.popSequenceDetails(AbstractIntegrationMessageBuilder.java:87)
at org.springframework.integration.support.MessageBuilder.popSequenceDetails(MessageBuilder.java:238)
at org.springframework.integration.support.MessageBuilder.popSequenceDetails(MessageBuilder.java:49)
at org.springframework.integration.aggregator.AbstractCorrelatingMessageHandler.completeGroup(AbstractCorrelatingMessageHandler.java:843)
at org.springframework.integration.aggregator.AbstractCorrelatingMessageHandler.processMessageForGroup(AbstractCorrelatingMessageHandler.java:498)
at org.springframework.integration.aggregator.AbstractCorrelatingMessageHandler.handleMessageInternal(AbstractCorrelatingMessageHandler.java:471)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:170)
... 129 more
I'm not using sequenceDetails header anywhere and It looks like Spring Integrations puts this itself and now gives exception.
I debugged and saw that the sequenceDetails header like this:
sequenceDetails [[67eea8d9-3295-4ea1-b85e-9ef970298986, 0, 0]]
Why do I face this error? Please tell me what you want to see from my project to solve this problem.
It looks like you do some serialization before that aggregator so List<List<Object>> value for IntegrationMessageHeaderAccessor.SEQUENCE_DETAILS header is converted to string somehow. Probably just via simple toString().
First of all you may not populate such a headers information via applySequence=false on the splitter upstream.
Secondly you may disable popping sequence details on the aggregator level.
See its respective property:
/**
* Perform a
* {#link org.springframework.integration.support.MessageBuilder#popSequenceDetails()}
* for output message or not. Default to true. This option removes the sequence
* information added by the nearest upstream component with {#code applySequence=true}
* (for example splitter).
* #param popSequence the boolean flag to use.
* #since 5.1
*/
public void setPopSequence(boolean popSequence) {
Or you can convert that string back to List<List<Object>> from string via some header enricher in between.
It is really unusual to see such a header be converted to string. I'm really curious how that happened in your use-case. Perhaps we may some automation on the framework level if we would know more about your scenario.
Before starting to read, that's a list of the links I've tried to read to solve this issue:
Spring : Create beans at runtime
How to add bean instance at runtime in spring WebApplicationContext?
https://www.logicbig.com/tutorials/spring-framework/spring-core/bean-definition.html
Spring - Programmatically generate a set of beans
How do I create beans programmatically in Spring Boot?
I'm using SpringBoot and RabbitMQ for my services and lately, I've read this article: https://programmerfriend.com/rabbit-mq-retry/
I would like to generalize this idea and create a SpringBoot library that can get in the names of the queues that need to be created via the application.properties file and do everything behind the scenes, so the creation of the different queues and the binding between them will happen "automagically" whenever I'll integrate the library into a service.
The problem which I'm facing is basically to have the same behavior #Bean gives me. I need to repeat this behavior multiple N times (as many as the number of names specified in my application.properties).
From what I could see online, there's a possibility to create beans when the app is loaded, but the only way to do it is by telling the context what type of class you want to generate and let Spring handle it itself. as the class I want to generate does not contain a default constructor (and does not controlled by me), I wondered if there's a way to create an object and add this specific instance to the application context.
but the only way to do it is by telling the context what type of class you want to generate and let Spring handle it itself
No; since 5.0 you can now provide a Supplier for the bean.
/**
* Register a bean from the given bean class, using the given supplier for
* obtaining a new instance (typically declared as a lambda expression or
* method reference), optionally customizing its bean definition metadata
* (again typically declared as a lambda expression).
* <p>This method can be overridden to adapt the registration mechanism for
* all {#code registerBean} methods (since they all delegate to this one).
* #param beanName the name of the bean (may be {#code null})
* #param beanClass the class of the bean
* #param supplier a callback for creating an instance of the bean (in case
* of {#code null}, resolving a public constructor to be autowired instead)
* #param customizers one or more callbacks for customizing the factory's
* {#link BeanDefinition}, e.g. setting a lazy-init or primary flag
* #since 5.0
*/
public <T> void registerBean(#Nullable String beanName, Class<T> beanClass,
#Nullable Supplier<T> supplier, BeanDefinitionCustomizer... customizers) {
So
context.registerBean("fooBean", Foo.class, () -> someInstance);
This is the code that I have:
#Component
#Configuration
#PropertySource("application.properties")
public class Program {
#Value("${app.title}")
private String appTitle;
public Program() {
System.out.println(appTitle);
}
}
The application.properties has
app.title=The Program
The output is null insteaf of The Program.
So, what am I missing? I have tried several examples; none worked.
Since appTitle is an autowired field, it is not set until after the object is initially constructed. This is why the value is still null in your example. The bean construction process in this scenario is as follows:
The Program constructor is called, creating a new Program instance
The appTitle field is set on the newly constructed bean to ${app.title}
The ideal fix for this depends on your goals. If you truly need the value within the constructor, you can pass it in as an autowired constructor parameter. The value will then be available within the constructor:
#Component
#Configuration
#PropertySource("application.properties")
public class Program {
public Program(#Value("${app.title}") appTitle) {
System.out.println(appTitle);
}
}
If you don't need it in the constructor itself, but need it for the proper initialization of the bean, you could alternatively use the #javax.annotation.PostConstruct annotation to make use of it after the object's construction but before it is made available for use elsewhere:
#Component
#Configuration
#PropertySource("application.properties")
public class Program {
#Value("${app.title}")
private String appTitle;
#PostConstruct
public void printAppTitle() {
System.out.println(appTitle);
}
}
Finally, if you don't need the value at construction time, but need it during the life of the bean, what you have will work; it just won't be available within the body of the constructor itself:
#Component
#Configuration
#PropertySource("application.properties")
public class Program {
#Value("${app.title}")
private String appTitle;
}
Nothing wrong, just don't do it in a constructor...
Other answers on this question are written assuming the goal is creating a Spring-managed bean that uses the given property in its creation. However, based on your comments in another answer, it looks like the question you want answered is how to access an externalized property (one provided by #Value) within a no-argument constructor. This is based on your expectation that a Java inversion of control (IoC) container such as Spring should allow accessing externalized properties (and presumably other dependencies) within a no-argument constructor. That being the case, this answer will address the specific question of accessing the property within a no-argument constructor.
While there are certainly ways this goal could be achieved, none of them would be idiomatic usage of the Spring framework. As you discovered, autowired fields (i.e. fields initialized using setter injection) cannot be accessed within the constructor.
There are two parts to explaining why this is. First, why does it work the way it does, programmatically? Second, why was it designed the way it was?
The setter-based dependency injection section of the Spring docs addresses the first question:
Setter-based DI is accomplished by the container calling setter methods on your beans after invoking a no-argument constructor or a no-argument static factory method to instantiate your bean.
In this case, it means that first the object is created using the no-argument constructor. Second, once the object is constructed, the appTitle is initialized on the constructed bean. Since the field isn't initialized until after the object is constructed, it will have its default value of null within the constructor.
The second question is why Spring is designed this way, rather than somehow having access to the property within the constructor. The constructor-based or setter-based DI? sidebar within the Spring documentation makes it clear that constructor arguments are in fact the idiomatic approach when dealing with mandatory dependencies in general.
Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies. [...]
The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null. Furthermore, constructor-injected components are always returned to the client (calling) code in a fully initialized state. [...]
Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. [...]
A property needed to construct the object certainly would be categorized as a mandatory dependency. Therefore, idiomatic Spring usage would be to pass in this required value in the constructor.
So in summary, trying to access an application property within a no-argument constructor is not supported by the Spring framework, and in fact runs contrary to the recommended use of the framework.
I have code like the following:
#Scheduled(cron = "${cron.foo.bar}")
#ConditionalOnProperty(name="cron.foo.bar.enabled", relaxedNames = false)
public void parseFooBar() {
... blah blah blah ...
}
In my properties file, I have:
cron.foo.bar=1 * * * * ?
cron.foo.bar.enabled=false
This does not work, and parseFooBar gets executed every minute on the 1st second.
However, if I add the field:
#Value("${cron.foo.bar.enabled}")
private String enabledProp;
so that I can do a log and see what it is, parseFooBar does NOT get executed. Removing the injected String once again sees parseFooBar execute. What am I doing wrong?
Edit: This is using Spring 4.1.5, Spring Boot 1.2.1, and JDK 8
Edit 2: moving the annotation to the type also works. (without having to force the #Value). But the annotation is both a Method and a Type annotation? It gives me a little more flexibility to do it on the method...
A condition in Spring Framework is used to control whether or not a component is registered in the application context. From the javadoc of #Conditional:
The #Conditional annotation may be used in any of the following ways:
as a type-level annotation on any class directly or indirectly annotated with #Component, including #Configuration classes
as a meta-annotation, for the purpose of composing custom stereotype annotations
as a method-level annotation on any #Bean method
When the condition is declared on parseFooBar it has no effect as it's not a #Bean method. It works as you expect when you declare it on the type as it then makes the component conditional such that it's not registered in the application context when the property doesn't match.