How does #Required annotation work with JavaConfig? - spring

I'm pretty new to the Spring Framework and I got problems to understand the #Required annotation in combination with a Java configured application.
Here is an example.
Config-File
#Configuration
public class AppConfig {
#Bean
public Movie movieA() {
return new Movie();
}
#Bean
public MovieHolder holder() {
return new MovieHolder();
}
}
MovieHolder.java
public class MovieHolder {
private Movie movie;
public Movie getMovie() {
return movie;
}
#Required
public void setMovie(Movie movie) {
this.movie = movie;
}
}
Context initialization
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MovieHolder holder = (MovieHolder) context.getBean("holder");
System.out.println("movie: " + holder.getMovie());
As far as I understood the documentation of the #Required annotation, there should rise an exception, because movie isn't set directly or by autowiring. Instead is the output movie: null.
What am I doing wrong? Or isn't this the correct use of the #Required annotation?

Setting the required properties in the beans that you are instantiating is your own responsibility. The BeanPostProcessor that processes the bean-definitions in the classes annotated with #Configuration is called ConfigurationClassPostProcessor. The BeanPostProcessor that processes your #Required annotation defaults to RequiredAnnotationBeanPostProcessor, which is registered by default when you use context:annotation-config and context:component-scan in your configuration. If you are not using these two tags, you can even register your own RequiredAnnotationBeanPostProcessor as a bean.
Now, the default implementation of the RequiredAnnotationBeanPostProcessor has a method called boolean shouldSkip(..) that checks for a boolean attribute named SKIP_REQUIRED_CHECK_ATTRIBUTE. The value of this attribute is checked for each bean during the post-processing by the RequiredAnnotationBeanPostProcessor. If it returns false, the #Required constraint is enforced, otherwise it is not.
Now, the ConfigurationClassPostProcessor set the value of this attribute to true while creating the bean definitions from the #Configuration classes (I guess for the reason that if you are defining a bean, you should ensure that it has the required properties). Hence, the #Required is not enforced for such beans.
As an aside, You might think that where did this SKIP_REQUIRED_CHECK_ATTRIBUTE attribute come from and where is it set: it is set on the instances of BeanDefinition that are used by Spring internally for bean creation and post-processing.
If you really want to enforce the #Required constraints, you would have to override the RequiredAnnotationBeanPostProcessor, override the boolean shouldSkip(..) method and register this class instead of the default RequiredAnnotationBeanPostProcessor. And as the documentation for RequiredAnnotationBeanPostProcessor says:
A default RequiredAnnotationBeanPostProcessor will be registered by the "context:annotation-config" and "context:component-scan" XML tags. Remove or turn off the default annotation configuration there if you intend to specify a custom RequiredAnnotationBeanPostProcessor bean definition.
Another way would be to use the initMethod attribute on your #Bean annotation. Which could perform checks to see that the required properties are indeed set. However, since this is code based configuration, you could just as well call that init method yourself.
Also, in my opinion, there is not much point in going through a lot of trouble to use your own RequiredAnnotationBeanPostProcessor, as the following documentation says:
Please note that an 'init' method may still need to implemented (and may still be desirable), because all that this class does is enforce that a 'required' property has actually been configured with a value. It does not check anything else... In particular, it does not check that a configured value is not null.
So, to summarize: #Required doesn't work with #Configuration classes by default. If you need to make sure that all your properties are set, you can just as well do it yourself when you create the bean in the #Bean methods (By calling some init method that performs such validations, or just supplying the required properties yourself). And if you really need to make the #Required annotation work, you'd need to use your own implementation of the RequiredAnnotationBeanPostProcessor, register it as a bean in the spring context and give up the benefits of context:annotation-config.

Just tried to declare a #Bean RequiredAnnotationBeanPostProcessor with overridden shouldSkip() method.
Yes, it checks my beans, but it fails even if I set all the required properties, i.e. it always fails.
I think Spring has a real problem with supporting #Required annotation for Java Config, since Spring has no way to tell whether or not you have set the property when you do it directly in Java code. (It can't inspect for 'null' fields later, since this would mean changing the semantics of the #Required annotation which should allow explicitly set null values).
When you use an XML config, Spring creates a wrapper object to set the properties, so it can track all the configured 'setXxx()' operations.
Conclusion: there is no reasonable way to enable #Required annotation for beans created in Java #Configuration classes.
(Very unfortunate feature, in my opinion, since the bean class writer and the class user might be different persons).

Related

Spring Boot application.properties appear unregistered when accessed from constructor

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.

Java Configuration vs Component Scan Annotations

Java configuration allows us to manage bean creation within a configuration file. Annotated #Component, #Service classes used with component scanning does the same. However, I'm concerned about using these two mechanisms at the same time.
Should Java configuration and annotated component scans be avoided in the same project? I ask because the result is unclear in the following scenario:
#Configuration
public class MyConfig {
#Bean
public Foo foo() {
return new Foo(500);
}
}
...
#Component
public class Foo {
private int value;
public Foo() {
}
public Foo(int value) {
this.value = value;
}
}
...
public class Consumer {
#Autowired
Foo foo;
...
}
So, in the above situation, will the Consumer get a Foo instance with a 500 value or 0 value? I've tested locally and it appears that the Java configured Foo (with value 500) is created consistently. However, I'm concerned that my testing isn't thorough enough to be conclusive.
What is the real answer? Using both Java config and component scanning on #Component beans of the same type seems like a bad thing.
I think your concern is more like raised by the following use case:
You have a custom spring-starter-library that have its own #Configuration classes and #Bean definitions, BUT if you have #Component/#Service in this library, you will need to explicitly #ComponentScan these packages from your service, since the default #ComponentScan (see #SpringBootApplication) will perform component scanning from the main class, to all sub-packages of your app, BUT not the packages inside the external library. For that purpose, you only need to have #Bean definitions in your external library, and to inject these external configurations via #EnableSomething annotation used on your app's main class (using #Import(YourConfigurationAnnotatedClass.class) OR via using spring.factories in case you always need the external configuration to be used/injected.
Of course, you CAN have #Components in this library, but the explicit usage of #ComponentScan annotation may lead to unintended behaviour in some cases, so I would recommend to avoid that.
So, to answer your question -> You can have both approaches of defining beans, only if they're inside your app, but bean definitions outside your app (e.g. library) should be explicitly defined with #Bean inside a #Configuration class.
It is perfectly valid to have Java configuration and annotated component scans in the same project because they server different purposes.
#Component (#Service,#Repository etc) are used to auto-detect and auto-configure beans.
#Bean annotation is used to explicitly declare a single bean, instead of letting Spring do it automatically.
You can do the following with #Bean. But, this is not possible with #Component
#Bean
public MyService myService(boolean someCondition) {
if(someCondition) {
return new MyServiceImpl1();
}else{
return new MyServiceImpl2();
}
}
Haven't really faced a situation where both Java config and component scanning on the bean of the same type were required.
As per the spring documentation,
To declare a bean, simply annotate a method with the #Bean annotation.
When JavaConfig encounters such a method, it will execute that method
and register the return value as a bean within a BeanFactory. By
default, the bean name will be the same as the method name
So, As per this, it is returning the correct Foo (with value 500).
In general, there is nothing wrong with component scanning and explicit bean definitions in the same application context. I tend to use component scanning where possible, and create the few beans that need more setup with #Bean methods.
There is no upside to include classes in the component scan when you create beans of their type explicitly. Component scanning can easily be targeted at certain classes and packages. If you design your packages accordingly, you can component scan only the packages without "special" bean classes (or else use more advanced filters on scanning).
In a quick look I didn't find any clear information about bean definition precedence in such a case. Typically there is a deterministic and fairly stable order in which these are processed, but if it is not documented it maybe could change in some future Spring version.

What is the difference between "constructor based injection" and " autowire by constructor mode" in Spring

I know and understand constructor based injection. But, the autowiring modes confuse me.
1) I have read that default mode for autowiring is 'no autowiring' i.e. We have to manually set the properties in xml file. But, isn't xml based configuration a type of autowiring? How can it be considered 'No autowiring'?
2) Other modes for autowiring are i) byName ii) byType iii)constructor iv) auto-detect. Am i correct to assume the following:
a) When using xml configuration based autowiring, the default mode is 'byName'(i.e. I have to keep the name of property reference the same as the name of the bean which is being used as a property.)
b) When using Annotations, default mode is 'byType'( Regardless of the place the #Autowired keyword is placed i.e on the setter, on the constructor or on the property, it will search the type of the property being autowried)
3) What is the difference between constructor based injection and 'constructor' mode of autowiring?(I have read that constructor mode means it applies byType mode on all the constructor arguments, but how is it different from placing #Autowired keyword on the constructor)
4) I know that to enable autowired mode byName in annotations, in the bean definition in the xml file, I have to use " autowire = 'byName' ". But, suppose I am using Annotations only config( using #Component, and no bean definitions in the xml ), and I want to use byName autowire mode, then what is the way of doing that?
I think you are a bit confused. First, you need to understand dependency injection (see here). There is ton of info about DI but in short, it means some third party (e.g spring IOC) passes the dependencies to the objects rather than the objects to create/obtain the references themselves. This could happen either through constructor or setter. For instance, consider constructor DI
class B{
}
class A{
private B b;
public A(B b){
this.b = b;
}
}
Some third party will inject an instance of class B into A rather class A create a reference to B itself. Very often you would use an interface so class A wouldn't even know what object will be injected into it.
Now in Spring there are different ways to configure these associations between the objects (the example above). You can either use XML, Java Config or Autowiring. They are independent but do the same stuff.
In both XML and JAVA config you need to configure the dependencies explicitly - either in xml file or having a #Configuration class for the JAVA Config and annotating the beans with #Bean. The autowiring is different. There you create simple POJOs which you annotate with #Component, #Controller, #Service or #Repository. They will be automatically registered as beans via component scanning. With autowiring you don't need to explicitly configure the dependencies in XML file or JAVA Config class. You can do it in code directly. For instance, if we have to compare java config vs autowiring using the previous example
Java Config (explicit config in a config class)
#Bean
public A getA(){
return new A(new B());
}
Autowiring (implicit - done in code)
#Component
class B{
}
#Component
class A{
private B b;
#Autowired
public A(B b){
this.b = b;
}
}
In the latter we autowire class B into class A (they both will be registered as beans due to #Component annotation) without having explicitly defined this association in an xml file or java config class. I hope it makes sense.
If you have to specify the bean names in the xml, its not happening automatically, hence its not autowiring.
With autowiring spring will figure out what bean to inject even though it may not be explicitly written.
When using xml configuration based autowiring, the default mode is 'byName'
When using Annotations, the ordering that happens depends on the annotation used as there are a few that can be used. #Autowire #Resource #Inject.
When using #Component, the default wiring is of type. The method below will resolve on any autowiring needs for a Service object.
#Bean
public Service getMyService(){
return new Service();
}
If there are multiple #Bean methods that return a Service you will get an error.
If you wanted to do wire via name while using #Component you would add the #Qualifier("nameToUse") annotation to the variable. It would find an #Bean annotated method called getNameToUse().
#Autowired
#Qualifier("nameToUse")
private Service myService;

What does the #Value annotation in method do?

I see one method annotated with #Value("${some.property}")
as in
#Value("${some.property}")
public void setSomething(String param) {
... do something with param
}
What is that annotation doing there?
Basically it tells Spring's AutowiredAnnotationBeanPostProcessor to call the setSomething method with the resolved value of some.property as the argument... but only if you have a PropertySourcesPlaceholderConfigurer in your bean definitions; if you haven't configured one the post processor will only inject the string "${some.property}"(without quotes) to your method.
An IllegalArgumentException will be thrown if the value could not be resolved unless you have used a default e.g. "${some.property:default}".
Spring resolves these values using the current Environment and its PropertySources e.g. JVM system properties, a Java properties file, etc.
Also you may use Spring Expression Language (SpEL) to resolve things like #{someBean.someMethod} or #{systemProperties[user.region]}
Sidenote: As the documentation states
Fields are injected right after construction of a bean, before any
config methods are invoked. [...] Bean property setter methods [as in this case] are effectively just a special case of such a general config method.
A common mistake is to try to execute some logic in your constructor using the value injected but at this moment the value has not be resolved nor injected because the constructor must finish in order to inject the value in the config method. In these cases you have to use the #Value or #Autowired annotations in your constructor arguments.
You may also use #PostConstruct or the XML init-method attribute pointing to a method that will be executed after the bean properties have been set. Alternatively you can implement the InitializingBean interface.

How to override a Spring #Autowire annotation and set a field to null?

I am a Spring neophyte who is working on a large Spring-based project that has extensive coupling between Spring beans. I am trying to write some integration tests that exercise subsets of the total application functionality. To do so, I'd like to override some of the autowiring.
For example, suppose I have a class
public class MyDataServiceImpl implements MyDataService {
#Qualifier("notNeededForMyDataServiceTest")
#Autowired
private NotNeededForMyDataServiceTest notNeededForMyDataServiceTest;
//...
}
and a context file with:
<bean id="myDataService"
class="MyDataServiceImpl">
</bean>
In my test, I have no need to use the notNeededForMyDataServiceTest field. Is there some way I can override the #Autowired annotation and set notNeededForMyDataServiceTest to null, perhaps in the XML file? I don't want to modify any of the Java classes, but I do want to avoid the (problematic) configuration of notNeededForMyDataServiceTest.
I tried doing:
<bean id="myDataService"
class="MyDataServiceImpl">
<property name="notNeededForMyDataServiceTest"><null/></property>
</bean>
That doesn't work. IntelliJ informs me "Cannot resolve property 'notNeededForMyDataServiceTest'", apparently because there are no getters and setters for that field.
I'm using Spring Framework 3.1.3.
The following configuration should work, I took the liberty of mixing in Java configuration
#Configuration
//This will load your beans from whichever xml file you are using
#ImportResource("classpath:/path/beans.xml")
public class TestConfigLoader{
// This will declare the unused bean and inject MyDataServiceImpl with null.
public #Bean(name="notNeededForMyDataServiceTest") NotNeededForMyDataServiceTest getNotNeededForMyDataServiceTest(){
return null;
}
... any other configuration beans if required.
}
And annotate your test class like so:
// In your test class applicationContext will be loaded from TestConfigLoader
#ContextConfiguration(classes = {TestConfigLoader.class})
public class MyTest {
// class body...
}
These could help:
Context configuration with annotated classes
Testing with #Configuration Classes and Profiles
Spring TestContext Framework
and profiles:
beans profile="..."
Introducing #Profile
You could create different beans definition in the XML configuration and then activate them using the -Dspring.profiles.active="profile1,profile2" env.
You're using the #Autowired mechanism wrong. The qualifier is not a property that you need to set. That's actually the name of a bean, so that the container will be able to choose one particular instance in case multiple beans of the same type are defined in the same context.
So the container will look for a bean of type NotNeededForMyDataServiceTest and the name (which would actually be the bean id in XML): notNeededForMyDataServiceTest.
What I think you want is to instruct the container to not inject anything in that field if no bean of type NotNeededForMyDataServiceTest is defined in the application context. That could be achieved simply by setting the required attribute of the annotation to false:
#Autowired(required = false)
NotNeededForMyDataServiceTest someOptionalDependency;
The only drawback of this approach would be that the container will never complain at runtime if there's nothing to inject in that field (and perhaps you would want this sanity check when your code runs in production).
If you don't want to make that dependency optional (or you can't edit that code for some reason), you'll need to provide a mock / null value for that field by setting that explicitly in your context. One option to do that would be to use Java configuration instead of XML (like in #Abe's answer) and another approach would be to make use of a factory bean which returns null (like in this question).

Resources