Hey-men-whatsup Hey-men-whatsup - 5 months ago 19
Java Question

Can't autowire Service even after dependency is being managed by Spring container

I've been modifying my class to avoid

new
operator on
StudentService
( let Spring's container manages it) there in my controller. I need
Student student;
field there gets injected with
StudentService
but rather than getting the
student
injected it gets injected with an exception saying :
No qualifying bean of type [com.Student] is defined: expected single matching bean but found 2: studentService,ss


I want this program to print


NAME : bravo


Student :

package com;
public interface Student {
public String getN();
}


StudentService :

package com;
import org.springframework.stereotype.Service;
@Service
public class StudentService implements Student{
String name;
@Override
public String getN() {
return this.name;
}

public void setName(String name) {
this.name = name;
}

}


Controller

package com;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class TheController {

@Autowired
private ApplicationContext appContext;

@Autowired
Student student;

@RequestMapping("/")
public String aMethod() {

StudentService ss = (StudentService) appContext.getBean("ss");
ss.setName("bravo");

System.out.println("NAME : " + student.getN());
// while "NAME : " + ss.getN() will work, of course
return "";
}
}


dispatcher-servlet

...
<context:component-scan base-package="com" />

...
<bean id="ss" class="com.StudentService" />

...


EDIT : If I do this :

@Service ("st")

public class StudentService implements Student{


and this (in controller)

@Autowired
@Qualifier("st")
Student student; // tipenya interface


no exceptions, but it produces


NAME :null


If I get rid of the
@Service
and load
StudentService
as
<bean class="com.StudentService".. >
and load it manually through application context then it will work.

I mean how is that
@Service
can't be autowired

Answer

Spring knows more than 2 ways to declare "beans" (objects it can use to inject). One is xml:

<bean id="ss" class="com.StudentService" />

This will cause spring to create a bean named "ss" by doing new com.StudentService(...). It should even try to figure out constructor arguments by finding other beans that match. The resulting object is stored in your application context for future use. E.g. when some place requests to get a bean of type StudentService or with one named "ss".

@Service

As well as @Component and some others will cause spring to treat the class this is attached to as bean. The name will be derived from the class name: it's simply lower-cased. It will also new an instance of that type and take care for all dependencies. Both XML and @-annotation configured beans will be available in the application context.

In your case, you tell spring in two different ways to do the same: that you want to have com.StudentService available as bean, once named explicitly "ss" via xml, once implicitly named "studentService" in java.

Now when you request in your controller a bean that matches the type Student like so

@Autowired
Student student; 

it has 2 equally good candidates and can't decide which one to inject. This is your current exception.

To solve the problem, you can either declare only 1 bean (preferably) or you qualify which one you mean by providing the name. For example

@Autowired
@Qualifier("ss")
Student student;

That's almost exactly what you do later with

StudentService ss = (StudentService) appContext.getBean("ss");

When you combine both like

@Autowired
@Qualifier("st")
Student student;

...

StudentService ss = (StudentService) appContext.getBean("ss");
ss.setName("bravo");

System.out.println("NAME : " + student.getN());

What happens is that you now have 2 independent beans / instances of StudentService, which are both "singletons" (wrt their bean-name). You set the name of one, and read the name of the other. The expected result is that the name of the "st" bean is still null.

Comments