#Required attribute checked for one bean but not another - java

I have a bean Config that has a required setter. However, when I don't set the setting in the bean config file I don't get an exception regarding the missing attribute.
public class Config extends MatchSet{
...
#Required
public void setSections(List<Section> section){...}
}
public class OtherClass{
...
#Required
public void setMatchSets(List<MatchSet> sets){...}
}
public interface MatchSet{...}
If I don't include the matchSets field of OtherClass I get a BeanInitializationException. But if I don't set the sections field of Config I don't get the exception. I am passing the instance of Config as one of the elements of the MatchSet list passed to OtherClass.
I have tried this using unit tests (SpringJUnit4ClassRunner) and using my main (ClassPathXmlApplicationContext) and the behavior is the same in either case.
Why is the #Required attribute not being checked for Config? Is it because it is being passed as a MatchSet?
Thanks

Turns out this was caused by a different issue. I did not have the getter/setter set up correctly. Primarily, this was because I had tried to set up method chaining and so was not returning null from the setter. Also, I was accepting a List in the setting but returning an ImmutableList from the getter. The combination was causing Spring to not recognize the setter and therefore not check

Related

Spring boot autowiring: strange case of property value being populated

I've been blown away by something and my sleep is nowhere after finding out that my code is working from last 2 years when clearly it shouldn't!
So it's a Spring boot application. The application has some controllers. One of the classes I'm using extends WebServiceGatewaySupport, so requires that I #Autowire this. The class name is AClient. Now, this class needs 4 properties to work. Three of them are coming from properties file, e.g.
#Value("${a.userName}")String userName;
So three properties are obviously picked from application.properties file. Now the fourth property had me blown away. It is NOT being fetched as rest three, but is being copied from constructor parameter. So we have only one constructor in the class:
public AClient(String d){
this.d=d;
}
Now this property is being used in one of the methods this class has
public String getSomeData(){
// This method gets Data based on property d
}
And interestingly and surprisingly, this property's value is present everytime this bean is accessed! The bean is #Autowired at one place only, inside the Controller class. I haven't marked the property value to be fetched from application.properties file. I haven't provided this class any clue where to get the value of d from. I haven't provided any public methods for other classes to set this value. Yet the code is working and I can even place a debug pointer in method getSomeData() and see that the value of d is present!
Now I understand I might be missing something obvious, but what? Is there a way I can get into Spring container when #Autowired objects are being instantiated and debug from that point to see where this value is coming from? I've checked the code multiple times. Run hundreds of query on Google to find something like Spring boot does some magic stuff to map the missing String properties. But the variable name in properties file is different from what it is in AClient class. So how can it even map? This is truly killing me now!
Addition:
Less relevant, but the code is being accessed in a standard way:
#Autowired
AClient aClient;
public someOtherMethod(){
aClient.getSomeData();
}
So when I place a debugger on first line of someOtherMethod() and hover over aClient, it shows variable d value populated, same as in application.properties file!
Edit:Here's what I missed:
#Configuration
public class someConfig{
#Bean
public AClient aClient(){
// Someone else fetched property from application.properties file, created an object of AClient class using argument constructor and returned that object here. So now Spring is using #Autowire reference for this object I guess
}
}
So basically, your #Configuration class looks similar to this?
#Configuration
public class SomeConfig {
#Value("${a-client.important-value-d}")
private String importantValueDFromApplicationProperties;
#Bean
public AClient aClient() {
return new AClient(importantValueDFromApplicationProperties);
}
}
If yes, then Spring will the aClient for every #Autowired requesting it. Therefore the value of importantValueDFromApplicationProperties will be present in this certain instance.
Another note:
I would recommend using Spring Boot's #ConfigurationProperties instead of #Value. Take a look.

JavaBeanBooleanPropertyBuilder for "Beans" without setters

I found this post for connecting a Java Bean as property binding with an existing JavaFX property. The binding should target a boolean property:
class MyClass {
private boolean loaded;
public boolean isLoaded() {
return loaded;
}
// Value changed internally
}
For real beans, meaning beans with setters the following works like a charm. But I've the problem that there's no setter for the loaded property, just because it's set internally and shouldn't be modifyable for external classes.
BooleanProperty loadedProeprty = new JavaBeanBooleanPropertyBuilder()
.bean(bean)
.name("loaded")
.getter("isLoaded")
.build();
Is there any way to create still a property for such "beans" without a setter? For now I just get a NoSuchMethodException for the expected setter MyClass.setLoaded(boolean).
Use ReadOnlyJavaBeanBooleanPropertyBuilder instead.
Normal properties in JavaFX are always read/write and thus require a setter. The read only variant creates a read only property and thus does not require a setter.

Spring #Autowired and #Value on property not working

I would like to use #Value on a property but I always get 0(on int).
But on a constructor parameter it works.
Example:
#Component
public class FtpServer {
#Value("${ftp.port}")
private int port;
public FtpServer(#Value("${ftp.port}") int port) {
System.out.println(port); // 21, loaded from the application.properties.
System.out.println(this.port); // 0???
}
}
The object is spring managed, else the constructor parameter wouldn't work.
Does anyone know what causes this weird behaviour?
Field injection is done after objects are constructed since obviously the container cannot set a property of something which doesn't exist. The field will be always unset in the constructor.
If you want to print the injected value (or do some real initialization :)), you can use a method annotated with #PostConstruct, which will be executed after the injection process.
#Component
public class FtpServer {
#Value("${ftp.port}")
private int port;
#PostConstruct
public void init() {
System.out.println(this.port);
}
}
I think the problem is caused because Spring's order of execution:
Firstly, Spring calls the constructor to create an instance, something like:
FtpServer ftpServer=new FtpServer(<value>);
after that, by reflection, the attribute is filled:
code equivalent to ftpServer.setPort(<value>)
So during the constructor execution the attribute is still 0 because that's the default value of an int.
This is a member injection:
#Value("${ftp.port}")
private int port;
Which spring does after instantiating the bean from its constructor. So at the time spring is instantiating the bean from the class, spring has not injected the value, thats why you are getting the default int value 0.
Make sure to call the variable after the constructor have been called by spring, in case you want to stick with member injection.

How does dependency injection instantiate a class without constructor?

I followed the following example of dependency injection: http://www.tutorialspoint.com/spring/spring_autowired_annotation.htm
For example the TextEditor class (from the above link):
public class TextEditor {
private SpellChecker spellChecker;
#Autowired
public void setSpellChecker( SpellChecker spellChecker ){
this.spellChecker = spellChecker;
}
public SpellChecker getSpellChecker( ) {
return spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
How can these dependencies/classes be instantiated, while they don't have any constructor?
Is Java simply making an object of that type, that is empty? Like an empty parameter constructor without any code?
Thanks for making this more clear!
Unless specified otherwise, every Java class has the default constructor. So here, you have a default public TextEditor() constructor, even though you haven't coded for it. (You could code it if you needed to change its visibility from public, declare a thrown exception, etc.)
So yes, Spring calls this default constructor - then calls the setSpellChecker method (as annotated, and through reflection) to populate it.
If no constructor is defined, a class can be instantiated via the no-argument default constructor.
So, the framework calls that constructor (supposedly using reflection) and then uses the set method to set the one field of the freshly created class.
The example above is using Spring annotations and Spring context file and those are the main and most important parts of the project, considering the DI.
So in the context file you have following line:
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
this defines a class with reference spellChecker that mapps to a class com.tutorialspoint.SpellChecker and once the compiler find such property in a method that is marked as #Autowired on instantiation of the object it injects/sets the relevant version of the required dependency.
In cases where a property doesn't match a reference tag in the applicationContext.xml file Spring is trying to map the type e.g. property with name mySpecialSpellChecker which has type of com.tutorialspoint.SpellChecker still will be mapped to bean with id="spellChecker" if there are more than one of same type Spring won't instantiate your object and you might get compile time error as Spring can't know which version of the two (or more) is the correct one so this requires developer input.
This is the order of execution:
instantiate textEditor, this has default constructor that is not visible in the code public TextEditor ()
the new instance is set in a pool of available objects with reference textEditor
instantiate spellChecker and add to the pool of available object with relevant reference/label
all #Autowired properties/methods are set/called with relevant objects in this case Spring calls: setSpellChecker(spellChecker)

How to use Custom type for #PathParam?

I want to use non spring bean class object as parameter for jersey web service class method. But it is giving missing dependency error at build time.
My code is:
#Component
#Path("/abcd")
public class ActorServiceEndpoint {
#POST
#Path("/test/{nonspringBean}")
#Produces(MediaType.APPLICATION_XML)
public void addActor(#PathParam("nonspringBean") MyNonSpringBeanClass nonspringBean){
}
}
The thing is path parameters come in String form. As per the specification, if we want the have a custom type be injected as a #PathParam, the custom class, should have one of three things:
A public static valueOf(String param) that returns the type
A public static fromString(String param) that returns the type
Or a public constructor that accepts a String
Another option implement a ParamConverter. You can see an example here.
If you don't own the class (it's a third-party class that you can't change) then your only option is to use the ParamConverter/ParamConverterProvider pair.
In either of these cases you'll want to construct the instance accordingly by parsing the String either in the constructor or in one of the above mentioned methods. After doing this, the custom type can be made a method parameter with the annotation.
The same holds true for other params, such as #FormParam, #HeaderParam, #QueryParam, etc.
It would help if you gave a bit more details of the error you're getting, but I see two problems with your code snippet:
The correct Spring annotation is #PathVariable, #PathParam is probably from another package. This doesn't apply as I guess you're using JAX-RS, not Spring annotations.
I'm not sure what converters are applied to path variables, but in any case it would need to have one for MyNonSpringBeanClass. I would take a String parameter and then instantiate MyNonSpringBeanClass myself in the function body.

Categories

Resources