Spring property injection and property initialization order - java

class A {
private String field1="123";
private String field2="prefix"+field1;
getter&setter;
}
filed1 is injected by one value("abc") defined in property file.
In some cases, value of field2 is always "prefix123" rather than "prefixabc".
Does property injection and initialization have order?

It could be safer to initialize field two inside a postconstruct method:
#PostConstruct
void initFieldTwo(){
field2="prefix"+field1;
}
That way you know it will happen after field 1 has been initialized.

Related

Autowiring both the constructor and the field

Is it wrong to autowire both the field and the constructor when doing the automatic configuration of the spring container. For example:
#Component
public class Test1 {
#Autowired
private Test2 B;
#Autowired
Test(Test2 C) {
this.B=C;
}
}
And could you explain what happened exactly ?
It's wrong. You may have two Test2 beans, one named "B" and one named "C" (names should be lowercase BTW). That constructor will be called first, setting the field to C; after that the field will be injected, overwriting the field with B. If there is only one Test2 bean then it will work, but keep in mind that the point of constructor injection is to avoid field injection and enable use of final fields instead.

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 are the #Value at the constructor really useful?

I have this doubt....
I was trying to have a default value for a field in my object, default value I wanted to bring it from a properties.
So, I thought about use it this way
#Value("${app.message.text:default}")
final String text;
final Object object
MyClass(Object object){
this.object = object;
}
//getters for both fields
Well, text is always null....
Then I tried this way
final String text;
final Object object
MyClass(Object object, #Value("${app.message.text:default}") final String text){
this.object = object;
this.text = text
}
It does work, but then at the constructors I have to specify the value of text.... then I will never use the default from the property... Did I misunderstand this? is there anyway to archive what I am trying using this #Value?
Thanks.
The reason why it won't work at property level:
When you annotate a class member, Spring uses reflection to set the value (after object is created). Now, in this case, the field is marked as final so it has to be set while creating an object (and not after the object is created) so the only way to #Value final fields would be to use constructor injection.
The reason why it doesn't work in constructor:
Spring only sets the value using #Value if the bean is created via Spring (i.e. by #Component, #Configuration or #Bean annotation so make sure your class is annotated with either of these.

guice injection in static variable

I Have doubt about guice injection.
Is it possible to inject a #named variable value to a static variable?
I have tried
#Provides
#Named("emp.id")
public Integer getEmpId() {
return 2;
}
and tried to inject this value to static variable such as
#Inject
#Named("emp.id")
private static Integer id;
But the id return value null, When I removed static modifier the id gave value 1.
What is really happening here?
Guice does not inject static fields by design. You can request static injection but this should be done only as a crutch:
This API is not recommended for general use because it suffers many of the same problems as static factories: it's clumsy to test, it makes dependencies opaque, and it relies on global state.
In your case you could add this to your configure method to have your static field injected by Guice:
requestStaticInjection(Foo.class);
If you don't add this the Integer will be initialized to null (by default).
I have no idea why id was set to 1 after you removed the static modifier, however. Seems that it should have been set to 2 if your Guice module was setup correctly.

#Required attribute checked for one bean but not another

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

Categories

Resources