I have a spring controller that uses annotations. I gave this controller a constructor that takes two arguments. I want both ways of initializing the controller: constructor injection and setter injection.
#Controller("viewQuestionController")
#RequestMapping("/public/viewQuestions")
public class ViewQuestionController
{
#Resource(name="questionService")
private QuestionService questionService;
/*public ViewQuestionController()
{
int i=0;
i++;
}
*/
public ViewQuestionController(#Qualifier("questionService") QuestionService questionService)
{
this.questionService = questionService;
}
#Resource(name="questionService")
public void setQuestionService(QuestionService questionService)
{
this.questionService = questionService;
}
}
When I uncomment the default constructor, the controller is initiated correctly. However, if I don't, I get a BeanInstantiationException, No default constructor found; nested exception is java.lang.NoSuchMethodException.
So, is my configuration for the annotated constructor wrong or does a completely annotated controller in spring always need a default constructor?
If you want to configure constructor injection via annotations, you need to put the corresponding annotation on the constructor. I'm not sure how it can be done with #Resource, but #Autowired and #Inject support it:
#Autowired
public ViewQuestionController(#Qualifier("questionService") QuestionService questionService)
or
#Inject
public ViewQuestionController(#Named("questionService") QuestionService questionService)
I think Controller beans need a default constructor as they are initialized by the framework but there is no way to tell the framework hot to provide the dependency.
On second thought why not you autowire your question service and Spring will take care of it.
The following code should be good
#Controller("viewQuestionController")
#RequestMapping("/public/viewQuestions")
public class ViewQuestionController
{
#Autowired
private QuestionService questionService;
//Not providing any constructor would also be fine
public ViewQuestionController(){}
questionService will be initialized properly by Spring
Related
what is the purpose of #Autowired annotation on a constructor? What is the difference between non-annotated and annotated constructor? Thank you.
Autowiring feature enables you to inject the object dependency implicitly.
Without autowiring you have to initiate the object like:
public class SomeOperation() {
private CarService carService;
public SomeOperation() {
carService = new CarServiceImpl();
}
}
But if you annotate with #Autowired you don't have to initiate the object. The framework will bring the class which implements the carService and initiate your object with it.
public class SomeOperation() {
private CarService carService;
#Autowired
public SomeOperation(CarService carService) {
this.carService = carService;
}
}
What is the difference between non-annotated and annotated
constructor?
In Spring 3 or below, the annotation on the constructor is mandatory to make Spring consider the constructor as the way to instantiate the bean and inject dependencies provided in parameters.
Spring 4 and above versions don't require the annotation to do that.
You just need to declare the constructor with any parameter to achieve that.
So in recent Spring versions, don't clutter the code with the annotation :
public Foo(Bar bar){
this.bar = bar;
}
In an Spring mockmvc test I want to replace a bean by a mock implementation which is configured using Mockito.when() definitions. The definitions are indeed respected at the time the mock is configured, as well as at the time the mock is injected into a depending bean (a controller advice in my case) during application context startup. However, when the mock is used during a certain test, all when definitions are gone.
Why?
Some remarks:
The mock is completely new code, so it is impossible that I am not aware of any call to Mockito.reset().
the mock at the time of usage is the same as at the time of creation.
a bypassing solution to the problem is to configure the mock in a #BeforeEach method in AbstractTest. However, I want to understand why it does not work without.
Here a simplified and anonymized example
#Component
public class MyBean {
private String property;
...
public String getProperty() {
return property;
}
}
#ControllerAdvice
public class MyControllerAdvice() {
private MyBean myBean;
#Autowired
public MyControllerAdvice(MyBean myBean) {
this.myBean = myBean;
System.out.println(this.myBean.getProperty()); // --> outputs "FOOBAR"
}
#ModelAttribute
public String getMyBeanProperty() {
return myBean.getProperty(); // --> returns null
}
}
public class AbstractTest {
#Configuration
static class Config {
#Bean
public MyBean () {
MyBean myBean = Mockito.mock(MyBean.class, "I am a mock of MyBean");
when(myBean.getProperty()).thenReturn("FOOBAR");
}
}
}
That's not a problem of Mockito. I think you simplified the example a lot and we don't see the full picture, but I can say that main cause - 2 different beans MyBean: one is initialized with Spring's #Component, second is in configuration class with #Bean.
Why do you use #Component for POJO/DO?
#Bean in the configuration class is being initialized lazy so better way to use #PostConstruct
If you want to leave both beans mark MyBean in the configuration class as #Primary
While looking at an existing Spring application, I stumbled upon a class with field injection, which we all know isn't recommended for various reasons. I have then decided to refactor it to make use of a more appropriate approach: constructor based DI.
Before refactoring
#Component
public class MaintenanceModeInterceptor implements HandlerInterceptor {
private static final String MAINTENANCE_MODE_VIEW = "common/maintenanceMode";
#Autowired
private ApplicationObject applicationObject;
public MaintenanceModeInterceptor() {
// Required by Spring
}
...
}
After refactoring
#Component
public class MaintenanceModeInterceptor implements HandlerInterceptor {
private static final String MAINTENANCE_MODE_VIEW = "common/maintenanceMode";
private ApplicationObject applicationObject;
public MaintenanceModeInterceptor() {
// Required by Spring
}
#Autowired
public MaintenanceModeInterceptor(ApplicationObject applicationObject) {
this.applicationObject = applicationObject;
}
...
}
Maybe it is related to the fact that a default constructor is present. However, if I remove it, I end up having this exception:
Caused by: java.lang.NoSuchMethodError: my.application.web.interceptor.MaintenanceModeInterceptor: method <init>()V not found
So my understanding is that Spring requires a default constructor for interceptors.
Is there any way to achieve construtor based DI in this scenario?
Thank you.
I think you should remove the non #Autowired constructor and do perform a clean build on your project.
I have the following components, in two different files:
#Component
public class Chauffeur {
Car car;
public Chauffeur(Car car){
this.car = car;
}
public void go(){
System.out.println("Chauffeur");
car.drive();
}
}
#Component
public class Car{
public void drive() {
System.out.println("Drive car");
}
}
the following configuration file:
#Configuration
#ComponentScan
public class DriveableConfiguration {
}
and the following test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=DriveableConfiguration.class)
public class DriveableTest {
#Autowired
Chauffeur chauffeur;
#Test
public void chauffeurTest(){
chauffeur.go();
}
}
All the classes above are in the same package and the test is passing.
In the test I annotated chauffer with #Autowired, which should mean that the Spring container looks after the creation of the instance of Chauffeur without the developer needing to explicitly instantiate it.
Now, the constructor for Chauffer needs an instance of Car, so there is no default constructor for that class. Nonetheless the container creates it, injecting the required instance in the constructor.
Is the #Autowired saying to the container to instantiate the element with whatever (Components, Beans) it can provide, included parameters in the constructor? If so, in what case is it needed to use #Autowired to annotate a constructor?
Only if you use Spring 4.3+. In such a case #Autowired on constructor is optional if you have one non default constructor.
You can check the example here.
So as of 4.3, you no longer need to specify an explicit injection annotation in such a single-constructor scenario. This is particularly elegant for classes which otherwise do not carry any container annotations at all, for example when programmatically registered
For versions lower than 4.3 you will an exception will be thrown:
the container will throw an exception looking for a default
constructor, unless you explicitly indicate autowire mode
‘constructor’ in your bean definition setup (e.g. in an XML )
#Component
public class MyClass {
public MyClass() {
SomeInterface something;
// Spring magic that i don't know
something.toString();
}
}
What Spring magic do I need to use to inject a bean into "something"?
I also wouldn't mind if it was a field. It just has to be usable from within the constructor!
The basic rules also apply to Spring:
to construct an object, Spring needs to invoke the constructor
Spring can't call a method of an object or set one of its fields if it isn't constructed yet
so if you want to access a field set by Spring , you can't do that from the constructor, unless the value is passed as argument to the constructor.
This thus leaves two choices:
constructor injection:
#Autowired
public MyClass(SomeInterface something) {
// use something
}
post-construct method, called after all the injections have been done, whatever the way:
#Autowired
private SomeInterface something;
#PostConstruct
private void initialize() {
// use something
}