I am a complete Spring beginner and I am trying to get basic configuration with Spring Boot working. I would like to use constructor injection wherever possible. However, Spring is throwing exceptions that I do not understand. I've shortened the problematic code for easier reading:
My config YAML file (I have snake yaml on the classpath):
database:
inactive_timeout: 100
active_jdbc_connections:
# this is a list with one property in each element
- name: foo
- name: bar
- name: baz
- name: qux
The application code:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class DemoApplication {
private final DBProperties dbProperties;
DBProperties getDbProperties() {
return dbProperties;
}
public DemoApplication(DBProperties dbProperties) {
this.dbProperties = dbProperties;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
And the class that Spring is failing to wire properly:
package com.example.demo;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
#ConfigurationProperties(prefix = "database")
#Component
public class DBProperties {
private final List<ConnectionProperties> activeJdbcConnections;
private int inactiveTimeout;
// ERROR: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'int' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
public DBProperties(List<ConnectionProperties> activeJdbcConnections, int inactiveTimeout) {
this.activeJdbcConnections = activeJdbcConnections;
this.inactiveTimeout = inactiveTimeout;
}
public List<ConnectionProperties> getActiveJdbcConnections() {
return activeJdbcConnections;
}
public int getInactiveTimeout() {
return inactiveTimeout;
}
#Component
public static class ConnectionProperties {
private String name;
// ERROR: Parameter 0 of constructor in com.example.demo.DBProperties$ConnectionProperties required a bean of type 'java.lang.String' that could not be found.
public ConnectionProperties(String name){
this.name = name;
}
public String getName() {
return name;
}
}
}
There are two separate errors. First, in the DBProperties constructor Spring is not able to wire the integer inactiveTimeout, even though it has no trouble wiring the parameter activeJdbcConnections. This can be solved using the #Value parameter, but this is undesirable because #Value does not recognize the prefix specified by #ConfigurationParameters, and so the prefix must be repeated for each #Value annotation.
Second, Spring cannot wire the name parameter of ConnectionProperties. Spring is able to work with a setter if it is added, but as stated above I want to work with constructor injection. As the name of the parameter matches the property I want wired, I do not understand the problem here. As a side note, I was unable to solve this with an #Value annotation, as I do not know the syntax for specifying a property from the current list element.
How can I get Spring to properly instantiate my configuration classes using constructor injection?
There are a few issues with your approach I think. Firstly you can't define beans of type int and String (even if you did try to define them somewhere) so the name and inactiveTimeout will never be available as beans to autowire through constructor injection.
Secondly, #Components when scanned are Spring singleton beans by default which means that there is only one instance of them per Spring context. It doesn't really then make sense for ConnectionProperties to be a component if you're passing it in a list for the DBProperties constructor since there can only be one instatiated ConnectionProperties so it would be a bit of a short list!
If you want DBProperties to be a bean with some parameters provided in its constructor then you probably want to define it as a #Bean directly and call the constructor yourself. Spring can't work out what parameters you want to put in there! Something like:
#Bean
public DBProperties dbProperties() {
...
return new DBProperties(myArray, myNumber);
}
Then this DBProperties bean will be available to be Constructor injected anywhere else in your Spring boot application.
Related
I'm trying to test a class that depends on a bean to work, which I'd like to mock. This bean requires a string value, which is received from my application.yml file using #ConfigurationProperties and that is likely the problem since other beans mocked in the same test class work just fine. Running the application normally also works just fine, so the error seems to be related to the #MockBean in some way.
I have this configuration class which gets the value from the application.yml:
#Data
#ConfigurationProperties("some_api")
public class SomeApiDaoConfig {
private String url;
}
Also, the value is set in the integrationTest application.yml file:
some_api:
url: http://localhost:8082
And also this factory, which creates the bean:
#Factory
public class SomeApiDaoFactory {
#Singleton
public SomeApiDao someApiDao(SomeApiDaoConfig someApiDaoConfig) {
return new SomeApiDao(someApiDaoConfig.getUrl());
}
}
The test class is basically:
#MicronautTest(packages = {"<<path to someApiDao>>"})
public class ServiceTest {
#Inject private BlockingStub blockingStub;
#Inject private AnotherDao anotherDao;
#Inject private SomeApiDao someApiDao;
#BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
}
... (tests)
#MockBean(AnotherDao.class)
AnotherDao anotherDao() {
return mock(AnotherDao.class);
}
#MockBean(SomeApiDao.class)
SomeApiDao someApiDao() {
return mock(SomeApiDao.class);
}
When I run the tests, however, this error pops up while it tries to initialize the SomeApiDao bean:
Failed to inject value for parameter [url] of class: <path to test>.$ServiceTest$SomeApiDao3Definition$Intercepted
Message: No bean of type [java.lang.String] exists. Make sure the bean is not disabled by bean requirements (enable trace logging for 'io.micronaut.context.condition' to check) and if the bean is enabled then ensure the class is declared a bean and annotation processing is enabled (for Java and Kotlin the 'micronaut-inject-java' dependency should be configured as an annotation processor).
Path Taken: new GrpcEmbeddedServer(ApplicationContext applicationContext,ApplicationConfiguration applicationConfiguration,GrpcServerConfiguration grpcServerConfiguration,[ServerBuilder serverBuilder],ApplicationEventPublisher eventPublisher,ComputeInstanceMetadataResolver computeInstanceMetadataResolver,List metadataContributors) --> ServerBuilder.serverBuilder(GrpcServerConfiguration configuration,[List serviceList],List interceptors,List serverTransportFilters) --> new Service([SomeApiDao someApiDao]) --> new $ServiceTest$SomeApiDao3Definition$Intercepted([String url],BeanContext beanContext,Qualifier qualifier,Interceptor[] interceptors)
io.micronaut.context.exceptions.DependencyInjectionException: Failed to inject value for parameter [url] of class: <path to test>.$ServiceTest$SomeApiDao3Definition$Intercepted
#Data
#ConfigurationProperties("some_api")
public class SomeApiDaoConfig {
private String url;
}
I'll assume #Data is the Lombok annotation and thus it's creating a constructor argument for the url. Micronaut did not support injecting configuration keys into constructor arguments until 1.3 and it requires the constructor be annotated with ConfigurationInject
This question already has answers here:
Setter DI vs. Constructor DI in Spring?
(9 answers)
Closed 5 years ago.
In a Pro Spring 3 Book,
Chapter 4 - Introduction IOC and DI in Spring - Page 59, In "Setter Injection vs. Constructor Injection" section, a paragraph says
Spring included, provide a mechanism for ensuring that all dependencies are defined when
you use Setter Injection, but by using Constructor Injection, you assert the requirement for the dependency in a container-agnostic manner"
Could you explain with examples
A class that takes a required dependency as a constructor argument can only be instantiated if that argument is provided (you should have a guard clause to make sure the argument is not null) (or use a non-nullable type in Kotlin). A constructor therefore enforces the dependency requirement whether or not you're using Spring, making it container-agnostic.
If you use setter injection, the setter may or may not be called, so the instance may never be provided with its dependency. The only way to force the setter to be called is using #Required or #Autowired
, which is specific to Spring and is therefore not container-agnostic.
So to keep your code independent of Spring, use constructor arguments for injection. This applies to tests; you'll have an easier time instantiating and testing the class in a normal unit test, without needing to configure an application context or the complexity that comes along with setting up an integration test.
Update: Spring 4.3 will perform implicit injection in single-constructor scenarios, making your code more independent of Spring by potentially not requiring an #Autowired annotation at all.
(...) by using Constructor Injection, you assert the requirement for the dependency in a container-agnostic manner
This mean that you can enforce requirements for all injected fields without using any container specific solution.
Setter injection example
With setter injection special spring annotation #Required is required.
#Required
Marks a method (typically a JavaBean setter method) as being 'required': that is, the setter method must be configured to be dependency-injected with a value.
Usage
import org.springframework.beans.factory.annotation.Required;
import javax.inject.Inject;
import javax.inject.Named;
#Named
public class Foo {
private Bar bar;
#Inject
#Required
public void setBar(Bar bar) {
this.bar = bar;
}
}
Constructor injection example
All required fields are defined in constructor, pure Java solution.
Usage
import javax.inject.Inject;
import javax.inject.Named;
#Named
public class Foo {
private Bar bar;
#Inject
public Foo(Bar bar) {
this.bar = bar;
}
}
Unit testing
This is especially useful in Unit Testing. Such kind of tests should be very simple and doesn't understand annotation like #Required, they generally not need a Spring for running simple unit test. When constructor is used, setup of this class for testing is much easier, there is no need to analyze how class under test is implemented.
To make it simple, let us say that we can use constructor based dependency injection for mandatory dependencies and setter based injection for optional dependencies. It is a rule of thumb!!
Let's say for example.
If you want to instantiate a class you always do it with its constructor. So if you are using constructor based injection, the only way to instantiate the class is through that constructor. If you pass the dependency through constructor it becomes evident that it is a mandatory dependency.
On the other hand, if you have a setter method in a POJO class, you may or may not set value for your class variable using that setter method. It is completely based on your need. i.e. it is optional. So if you pass the dependency through setter method of a class it implicitly means that it is an optional dependency. Hope this is clear!!
Constructor injection is used when the class cannot function without the dependent class.
Property injection is used when the class can function without the dependent class.
As a concrete example, consider a ServiceRepository which depends on IService to do its work. Since ServiceRepository cannot function usefully without IService, it makes sense to have it injected via the constructor.
The same ServiceRepository class may use a Logger to do tracing. The ILogger can be injected via Property injection.
Other common examples of Property injection are ICache (another aspect in AOP terminology) or IBaseProperty (a property in the base class).
By using Constructor Injection, you assert the requirement for the dependency in a container-agnostic manner
We need the assurance from the IoC container that, before using any bean, the injection of necessary beans must be done.
In setter injection strategy, we trust the IoC container that it will first create the bean first but will do the injection right before using the bean using the setter methods. And the injection is done according to your configuration. If you somehow misses to specify any beans to inject in the configuration, the injection will not be done for those beans and your dependent bean will not function accordingly when it will be in use!
But in constructor injection strategy, container imposes (or must impose) to provide the dependencies properly while constructing the bean. This was addressed as " container-agnostic manner", as we are required to provide dependencies while creating the bean, thus making the visibility of dependency, independent of any IoC container.
Edit:
Q1: And how to prevent container from creating bean by constructor with null values instead of missing beans?
You have no option to really miss any <constructor-arg> (in case of Spring), because you are imposed by IoC container to provide all the constructor arguments needed to match a provided constructor for creating the bean. If you provide null in your <constructor-arg> intentionally. Then there is nothing IoC container can do or need to do with it!
This example may help:
Controller class:
#RestController
#RequestMapping("/abc/dev")
#Scope(value = WebApplicationContext.SCOPE_REQUEST)
public class MyController {
//Setter Injection
#Resource(name="configBlack")
public void setColor(Color c) {
System.out.println("Injecting setter");
this.blackColor = c;
}
public Color getColor() {
return this.blackColor;
}
public MyController() {
super();
}
Color nred;
Color nblack;
//Constructor injection
#Autowired
public MyController(#Qualifier("constBlack")Color b, #Qualifier("constRed")Color r) {
this.nred = r;
this.nblack = b;
}
private Color blackColor;
//Field injection
#Autowired
private Color black;
//Field injection
#Resource(name="configRed")
private Color red;
#RequestMapping(value = "/customers", produces = { "application/text" }, method = RequestMethod.GET)
#ResponseStatus(value = HttpStatus.CREATED)
public String createCustomer() {
System.out.println("Field injection red: " + red.getName());
System.out.println("Field injection: " + black.getName());
System.out.println("Setter injection black: " + blackColor.getName());
System.out.println("Constructor inject nred: " + nred.getName());
System.out.println("Constructor inject nblack: " + nblack.getName());
MyController mc = new MyController();
mc.setColor(new Red("No injection red"));
System.out.println("No injection : " + mc.getColor().getName());
return "Hello";
}
}
Interface Color:
public interface Color {
public String getName();
}
Class Red:
#Component
public class Red implements Color{
private String name;
#Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Red(String name) {
System.out.println("Red color: "+ name);
this.name = name;
}
public Red() {
System.out.println("Red color default constructor");
}
}
Class Black:
#Component
public class Black implements Color{
private String name;
#Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Black(String name) {
System.out.println("Black color: "+ name);
this.name = name;
}
public Black() {
System.out.println("Black color default constructor");
}
}
Config class for creating Beans:
#Configuration
public class Config {
#Bean(name = "configRed")
public Red getRedInstance() {
Red red = new Red();
red.setName("Config red");
return red;
}
#Bean(name = "configBlack")
public Black getBlackInstance() {
Black black = new Black();
black.setName("config Black");
return black;
}
#Bean(name = "constRed")
public Red getConstRedInstance() {
Red red = new Red();
red.setName("Config const red");
return red;
}
#Bean(name = "constBlack")
public Black getConstBlackInstance() {
Black black = new Black();
black.setName("config const Black");
return black;
}
}
BootApplication (main class):
#SpringBootApplication
#ComponentScan(basePackages = {"com"})
public class BootApplication {
public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
}
}
Run Application and hit URL:
GET 127.0.0.1:8080/abc/dev/customers/
Output:
Injecting setter
Field injection red: Config red
Field injection: null
Setter injection black: config Black
Constructor inject nred: Config const red
Constructor inject nblack: config const Black
Red color: No injection red
Injecting setter
No injection : No injection red
With examples? Here's a simple one:
public class TwoInjectionStyles {
private Foo foo;
// Constructor injection
public TwoInjectionStyles(Foo f) {
this.foo = f;
}
// Setting injection
public void setFoo(Foo f) { this.foo = f; }
}
Personally, I prefer constructor injection when I can.
In both cases, the bean factory instantiates the TwoInjectionStyles and Foo instances and gives the former its Foo dependency.
I am trying to write up an integration test case with Spring Boot Test.
I customize the ConversionService to know about the new java.time types:
#Configuration
public class ConversionServiceConfiguration {
#Bean
public static ConversionService conversionService() {
final FormattingConversionService reg = new DefaultFormattingConversionService();
new DateTimeFormatterRegistrar().registerFormatters(reg);
return reg;
}
}
and then later expect it to work:
#Component
class MyServiceConfig {
#Value("${max-watch-time:PT20s}")
private Duration maxWatchTime = Duration.ofSeconds(20);
}
When running under the normal SpringApplication.run this seems to work fine. However, in my test case:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT, classes= {
MyServiceMain.class,
AttachClientRule.class
})
public class MyTest {
#Inject
#Rule
public AttachClientRule client;
#Test(expected=IllegalArgumentException.class)
public void testBad() throws Exception {
client.doSomethingIllegal();
}
}
it blows up:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'AttachClientRule': Unsatisfied dependency expressed through constructor parameter 0:
Error creating bean with name 'MyServiceConfig': Unsatisfied dependency expressed through field 'maxWatchTime': Failed to convert value of type [java.lang.String] to required type [java.time.Duration];
nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [java.time.Duration]: no matching editors or conversion strategy found;
Peering deep into the guts of the TypeConverterDelegate that does the actual conversion, it seems to capture the ConversionService used from a field on the DefaultListableBeanFactory. Setting a watchpoint on where that field is set, I find the AbstractApplicationContext.refresh() method:
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh(); // <--- MyServiceConfig initialized here
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // <--- DefaultListableBeanFactory.conversionService set here!!!
// Last step: publish corresponding event.
finishRefresh();
So the #Value injection is happening before the ConversionService is applied to the BeanFactory. No bueno!
I've found what seems to be a workaround:
#Configuration
public class ConversionServiceConfiguration implements BeanFactoryPostProcessor {
#Bean
public static ConversionService conversionService() {
final FormattingConversionService reg = new DefaultFormattingConversionService();
new DateTimeFormatterRegistrar().registerFormatters(reg);
return reg;
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.setConversionService(conversionService());
}
}
This forces the initialization to happen earlier on, but doesn't feel like the right solution (at least it's not documented as such).
Where have I gone wrong?
Spring 4.3.0, Spring Boot 1.4.0M3
EDIT
And now I've discovered another way for it to fail! Without making the same configuration class implement EnvironmentAware:
#Override
public void setEnvironment(Environment environment) {
((AbstractEnvironment) environment).setConversionService(conversionService());
}
I find that the PropertySourcesPropertyResolver uses the wrong (default) ConversionService. This is driving me mad!
Caused by: java.lang.IllegalArgumentException: Cannot convert value [PT15s] from source type [String] to target type [Duration]
at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:94)
at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:65)
at org.springframework.core.env.AbstractPropertyResolver.getProperty(AbstractPropertyResolver.java:143)
at org.springframework.core.env.AbstractEnvironment.getProperty(AbstractEnvironment.java:546)
at com.mycorp.DoSomething.go(DoSomething.java:103)
The Spring Boot developers have confirmed that this is poorly documented and does not work as specified: https://github.com/spring-projects/spring-boot/issues/6222
Try to remove static keyword from conversionService bean definition.
The following example shows explicit wiring of dependencies using spring java config that results in a different bean being wired in while using and interface for a spring configuration class.
This seems like it shouldn't occur or at least give the normal warning that there are two beans as candidates for autowiring and it doesn't know which to select.
Any thoughts on this issue? My guess is there is no real name spacing between configuration classes as is implied by the syntax "this.iConfig.a()" Could this be considered a bug (if only for not warning about the 2 candidate beans)?
public class Main
{
public static void main( final String[] args )
{
final ApplicationContext context = new AnnotationConfigApplicationContext( IConfigImpl.class, ServiceConfig.class );
final Test test = context.getBean( Test.class );
System.out.println( test );
}
}
public class Test
{
private final String string;
public Test( final String param )
{
this.string = param;
}
public String toString()
{
return this.string;
}
}
#Configuration
public interface IConfig
{
#Bean
public String a();
}
#Configuration
public class IConfigImpl implements IConfig
{
#Bean
public String a()
{
return "GOOD String";
}
}
#Configuration
public class ServiceConfig
{
#Autowired
IConfig iConfig;
#Bean
Test test()
{
return new Test( this.iConfig.a() );
}
#Bean
String a()
{
return "BAD String";
}
}
In this case, I would expect to have "GOOD String" to be always be wired in the Test object, but flipping the order of IConfigImpl.class, ServiceConfig.class in the context loader changes which string is loaded.
Tested with Spring 4.0.7
EDIT: Further testing shows this has nothing to to with inherented configs. Same thing results if you drop the IConfig interface.
I believe this was a behavior of Spring for years.
If you redefine a bean, the one that is being loaded as last wins.
Another question would be how to control the order of bean loading when java configs are used. Check out this article http://www.java-allandsundry.com/2013/04/spring-beans-with-same-name-and.html which shows you how to do the ordering by using #Import of the other Spring java config.
The solution is actually simple - if you need to override a previously
defined bean(without say the flexibility of autowiring with a
different bean name), either use the XML bean configuration for both
the bean being overridden and the overriding bean or use the
#Configuration. XML bean configuration is the first example in this
entry, the one with #Configuration would be something like this:
#Configuration
public class Context1JavaConfig {
#Bean
public MemberService memberService() {
return new MemberSvcImpl1();
}
}
#Configuration
#Import(Context1JavaConfig.class)
public class Context2JavaConfig {
#Bean
public MemberService memberService() {
return new MemberSvcImpl2();
}
}
Stepan has mentioned the issue of order. The following is about your comment on their answer
Overriding beans of the same name makes sense, but in this case, I'm
specifically referencing the bean as specified in the iConfig
configuration. I would expect to get the one specified there.
In order to implement #Configuration and the caching of beans so that calls like
#Configuration
class Example {
#Bean
public UncaughtExceptionHandler uncaughtExceptionHandler() {
return (thread, throwable) -> System.out.println(thread + " => " + throwable.getMessage());
}
#Bean
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Thread newThread() {
Thread thread = new Thread();
thread.setUncaughtExceptionHandler(uncaughtExceptionHandler()); // <<<<<< allowing this
return thread;
}
}
Spring actually uses CGLIB to create a proxy subtype of the #Configuration annotated class. This proxy maintains a reference to the backing ApplicationContext and uses that to resolve a bean.
So the call in your example
return new Test(this.iConfig.a());
isn't really invoking IConfigImpl#a(). It invokes this code (as of 4.2) from the proxy interceptor. The code uses the corresponding Method to determine the target bean name and uses the ApplicationContext's BeanFactory to resolve the bean. Since the bean definition for a bean named a has already been overriden, that new bean definition gets used. That bean definition is using the ServiceConfig#a() method as its factory method.
This is described in the documentation, here
All #Configuration classes are subclassed at startup-time with CGLIB.
In the subclass, the child method checks the container first for any
cached (scoped) beans before it calls the parent method and creates a
new instance.
Could this be considered a bug [...]?
I don't believe so. The behavior is documented.
This question already has answers here:
Setter DI vs. Constructor DI in Spring?
(9 answers)
Closed 5 years ago.
In a Pro Spring 3 Book,
Chapter 4 - Introduction IOC and DI in Spring - Page 59, In "Setter Injection vs. Constructor Injection" section, a paragraph says
Spring included, provide a mechanism for ensuring that all dependencies are defined when
you use Setter Injection, but by using Constructor Injection, you assert the requirement for the dependency in a container-agnostic manner"
Could you explain with examples
A class that takes a required dependency as a constructor argument can only be instantiated if that argument is provided (you should have a guard clause to make sure the argument is not null) (or use a non-nullable type in Kotlin). A constructor therefore enforces the dependency requirement whether or not you're using Spring, making it container-agnostic.
If you use setter injection, the setter may or may not be called, so the instance may never be provided with its dependency. The only way to force the setter to be called is using #Required or #Autowired
, which is specific to Spring and is therefore not container-agnostic.
So to keep your code independent of Spring, use constructor arguments for injection. This applies to tests; you'll have an easier time instantiating and testing the class in a normal unit test, without needing to configure an application context or the complexity that comes along with setting up an integration test.
Update: Spring 4.3 will perform implicit injection in single-constructor scenarios, making your code more independent of Spring by potentially not requiring an #Autowired annotation at all.
(...) by using Constructor Injection, you assert the requirement for the dependency in a container-agnostic manner
This mean that you can enforce requirements for all injected fields without using any container specific solution.
Setter injection example
With setter injection special spring annotation #Required is required.
#Required
Marks a method (typically a JavaBean setter method) as being 'required': that is, the setter method must be configured to be dependency-injected with a value.
Usage
import org.springframework.beans.factory.annotation.Required;
import javax.inject.Inject;
import javax.inject.Named;
#Named
public class Foo {
private Bar bar;
#Inject
#Required
public void setBar(Bar bar) {
this.bar = bar;
}
}
Constructor injection example
All required fields are defined in constructor, pure Java solution.
Usage
import javax.inject.Inject;
import javax.inject.Named;
#Named
public class Foo {
private Bar bar;
#Inject
public Foo(Bar bar) {
this.bar = bar;
}
}
Unit testing
This is especially useful in Unit Testing. Such kind of tests should be very simple and doesn't understand annotation like #Required, they generally not need a Spring for running simple unit test. When constructor is used, setup of this class for testing is much easier, there is no need to analyze how class under test is implemented.
To make it simple, let us say that we can use constructor based dependency injection for mandatory dependencies and setter based injection for optional dependencies. It is a rule of thumb!!
Let's say for example.
If you want to instantiate a class you always do it with its constructor. So if you are using constructor based injection, the only way to instantiate the class is through that constructor. If you pass the dependency through constructor it becomes evident that it is a mandatory dependency.
On the other hand, if you have a setter method in a POJO class, you may or may not set value for your class variable using that setter method. It is completely based on your need. i.e. it is optional. So if you pass the dependency through setter method of a class it implicitly means that it is an optional dependency. Hope this is clear!!
Constructor injection is used when the class cannot function without the dependent class.
Property injection is used when the class can function without the dependent class.
As a concrete example, consider a ServiceRepository which depends on IService to do its work. Since ServiceRepository cannot function usefully without IService, it makes sense to have it injected via the constructor.
The same ServiceRepository class may use a Logger to do tracing. The ILogger can be injected via Property injection.
Other common examples of Property injection are ICache (another aspect in AOP terminology) or IBaseProperty (a property in the base class).
By using Constructor Injection, you assert the requirement for the dependency in a container-agnostic manner
We need the assurance from the IoC container that, before using any bean, the injection of necessary beans must be done.
In setter injection strategy, we trust the IoC container that it will first create the bean first but will do the injection right before using the bean using the setter methods. And the injection is done according to your configuration. If you somehow misses to specify any beans to inject in the configuration, the injection will not be done for those beans and your dependent bean will not function accordingly when it will be in use!
But in constructor injection strategy, container imposes (or must impose) to provide the dependencies properly while constructing the bean. This was addressed as " container-agnostic manner", as we are required to provide dependencies while creating the bean, thus making the visibility of dependency, independent of any IoC container.
Edit:
Q1: And how to prevent container from creating bean by constructor with null values instead of missing beans?
You have no option to really miss any <constructor-arg> (in case of Spring), because you are imposed by IoC container to provide all the constructor arguments needed to match a provided constructor for creating the bean. If you provide null in your <constructor-arg> intentionally. Then there is nothing IoC container can do or need to do with it!
This example may help:
Controller class:
#RestController
#RequestMapping("/abc/dev")
#Scope(value = WebApplicationContext.SCOPE_REQUEST)
public class MyController {
//Setter Injection
#Resource(name="configBlack")
public void setColor(Color c) {
System.out.println("Injecting setter");
this.blackColor = c;
}
public Color getColor() {
return this.blackColor;
}
public MyController() {
super();
}
Color nred;
Color nblack;
//Constructor injection
#Autowired
public MyController(#Qualifier("constBlack")Color b, #Qualifier("constRed")Color r) {
this.nred = r;
this.nblack = b;
}
private Color blackColor;
//Field injection
#Autowired
private Color black;
//Field injection
#Resource(name="configRed")
private Color red;
#RequestMapping(value = "/customers", produces = { "application/text" }, method = RequestMethod.GET)
#ResponseStatus(value = HttpStatus.CREATED)
public String createCustomer() {
System.out.println("Field injection red: " + red.getName());
System.out.println("Field injection: " + black.getName());
System.out.println("Setter injection black: " + blackColor.getName());
System.out.println("Constructor inject nred: " + nred.getName());
System.out.println("Constructor inject nblack: " + nblack.getName());
MyController mc = new MyController();
mc.setColor(new Red("No injection red"));
System.out.println("No injection : " + mc.getColor().getName());
return "Hello";
}
}
Interface Color:
public interface Color {
public String getName();
}
Class Red:
#Component
public class Red implements Color{
private String name;
#Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Red(String name) {
System.out.println("Red color: "+ name);
this.name = name;
}
public Red() {
System.out.println("Red color default constructor");
}
}
Class Black:
#Component
public class Black implements Color{
private String name;
#Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Black(String name) {
System.out.println("Black color: "+ name);
this.name = name;
}
public Black() {
System.out.println("Black color default constructor");
}
}
Config class for creating Beans:
#Configuration
public class Config {
#Bean(name = "configRed")
public Red getRedInstance() {
Red red = new Red();
red.setName("Config red");
return red;
}
#Bean(name = "configBlack")
public Black getBlackInstance() {
Black black = new Black();
black.setName("config Black");
return black;
}
#Bean(name = "constRed")
public Red getConstRedInstance() {
Red red = new Red();
red.setName("Config const red");
return red;
}
#Bean(name = "constBlack")
public Black getConstBlackInstance() {
Black black = new Black();
black.setName("config const Black");
return black;
}
}
BootApplication (main class):
#SpringBootApplication
#ComponentScan(basePackages = {"com"})
public class BootApplication {
public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
}
}
Run Application and hit URL:
GET 127.0.0.1:8080/abc/dev/customers/
Output:
Injecting setter
Field injection red: Config red
Field injection: null
Setter injection black: config Black
Constructor inject nred: Config const red
Constructor inject nblack: config const Black
Red color: No injection red
Injecting setter
No injection : No injection red
With examples? Here's a simple one:
public class TwoInjectionStyles {
private Foo foo;
// Constructor injection
public TwoInjectionStyles(Foo f) {
this.foo = f;
}
// Setting injection
public void setFoo(Foo f) { this.foo = f; }
}
Personally, I prefer constructor injection when I can.
In both cases, the bean factory instantiates the TwoInjectionStyles and Foo instances and gives the former its Foo dependency.