Reputation: 1072
I've recently came across a behavior in Spring framework which left me very puzzled: if two classes have the same simple name (but are from different packages), and both are marked with the @Component
annoation, then any attempt to mark a variable of the type of one of them with @Autowired
will result in an exception, since there are 2 different Beans declared with the desired name. Simply put - the @Component
annotation uses the Class' simple name instead of its full name (this is also mentioned in this question).
Is there a reason why Spring works this way? From what I understood, the whole point of dependency injection is that you can receive an object of the appropriate type without knowing how to create it, and so forcing the receiver of the dependency to know the source of the dependency through annotations such as @Qualifier
even though there is only 1 truly relevant option really confuses me.
Upvotes: 1
Views: 3689
Reputation: 1769
Why doesn't Spring's @Component annotation use the full class name? Is there a reason why Spring works this way?
Spring-Beans can be defined in different ways: With the @Component
annotation like you did and for example with the @Bean
annotation in a "config" class.
In such a "config" class you can have several methods which are annotated with @Bean
and each of the beans has the methodname as simple name. The full qualified name of the class would not be possible in such a case.
See the example how you can create Spring Beans in your application:
/**
* Beans which are created from this class use by default the name of the method
* because it's the name which makes most sense for these Beans
*/
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
@Bean
public ClientService clientService() {
return new ClientServiceImpl();
}
}
/**
* The created bean uses the simple type name as bean name.
*/
@Component
public class CustomService {
....
}
But you can also override this behaviour with a custom implementation of BeanNamingStrategy
.
For more details about naming beans and overriding the naming strategy you can read: https://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch02s02.html
Upvotes: 0
Reputation: 106430
Not knowing how a bean is created is not the same as knowing where to find the bean.
As you've observed, if you have two @Component
s which have the same simple name, and you attempt to @Autowire
them in, Spring won't know which one to bring in. This is because the names generated by Spring are non-qualified; the default generator will only ever use the simple name of the class.
While there's a nifty @ComponentScan
-oriented fix to this, I prefer that there be no ambiguity when I'm looking at components or beans, so I would espouse explicitly naming your beans so that there can be no ambiguity going forward.
// Assuming these are in different packages
@Component("foo")
public class MyComponent {}
@Component("bar")
public class MyComponent {}
Upvotes: 2
Reputation: 1733
It works fine. First component:
package com.example.demo.component1;
import org.springframework.stereotype.Component;
@Component
public class SimpleComponent {
public String action() {
return "imSimpleComponent";
}
}
Second component:
package com.example.demo.component2;
import org.springframework.stereotype.Component;
@Component("SimpleComponent2")
public class SimpleComponent {
public String action() {
return "imSimpleComponent2";
}
}
Controller:
package com.example.demo.controller;
import com.example.demo.component1.SimpleComponent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ResourceController {
@Autowired
private SimpleComponent simpleComponent;
@Autowired
private com.example.demo.component2.SimpleComponent simpleComponent2;
@RequestMapping("/home")
public String hello() {
return simpleComponent.action() + "_" + simpleComponent2.action();
}
}
http://localhost:8080/home return:
imSimpleComponent_imSimpleComponent2
Upvotes: 2