英文:
Why doesn't Spring's @Component annotation use the full class name?
问题
我最近在Spring框架中遇到了一个让我非常困惑的行为:如果两个类具有相同的简单名称(但来自不同的包),并且这两个类都标记有@Component
注解,那么如果尝试使用@Autowired
将一个类型为其中一个类的变量标记为自动装配,将会导致异常,因为有两个不同的Bean声明了相同的名称。简单地说 - @Component
注解使用类的简单名称而不是完整名称(这也在这个问题中提到)。
Spring为什么会以这种方式工作?从我理解的角度来看,依赖注入的整个目的是,您可以接收适当类型的对象,而不需要知道如何创建它,因此通过诸如@Qualifier
之类的注解来强制依赖的接收者知道依赖的来源,即使只有一个真正相关的选项,这确实让我感到困惑。
英文:
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.
答案1
得分: 2
以下是翻译好的代码部分:
第一个组件:
package com.example.demo.component1;
import org.springframework.stereotype.Component;
@Component
public class SimpleComponent {
public String action() {
return "imSimpleComponent";
}
}
第二个组件:
package com.example.demo.component2;
import org.springframework.stereotype.Component;
@Component("SimpleComponent2")
public class SimpleComponent {
public String action() {
return "imSimpleComponent2";
}
}
控制器:
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 返回:
imSimpleComponent_imSimpleComponent2
英文:
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
答案2
得分: 2
不知道一个 bean 是如何创建的,并不等同于知道在哪里找到这个 bean。
正如您所观察到的,如果您有两个具有相同简单名称的 @Component
,并且您尝试在其中进行 @Autowire
,Spring 将不知道要引入哪一个。这是因为 Spring 生成的名称是非限定的;默认的生成器只会使用类的简单名称。
虽然有一种聪明的 @ComponentScan
相关方法来解决这个问题(参见 这里),但我更喜欢在查看组件或者 bean 时没有歧义,所以我赞成显式地为您的 bean 命名,以便今后不会产生歧义。
// 假设这些在不同的包中
@Component("foo")
public class MyComponent {}
@Component("bar")
public class MyComponent {}
英文:
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 {}
答案3
得分: 0
> 为什么Spring的@Component注解不使用完整的类名?
> Spring为什么采取这种方式运作呢?
Spring-Beans可以以不同的方式进行定义:例如,使用你所使用的@Component
注解,或者在“config”类中使用@Bean
注解。
在这样一个“config”类中,您可以有多个被@Bean
注解标记的方法,每个Bean的方法名都作为简单名称。在这种情况下,不可能使用完全限定的类名。
看下面的示例,了解如何在应用程序中创建Spring Beans:
/**
* 从这个类创建的Bean默认使用方法的名称,因为对于这些Bean来说,名称是最有意义的
*/
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
@Bean
public ClientService clientService() {
return new ClientServiceImpl();
}
}
/**
* 创建的Bean使用简单类型名称作为Bean名称。
*/
@Component
public class CustomService {
....
}
但您还可以通过自定义的BeanNamingStrategy
实现覆盖此行为。
有关命名Bean和覆盖命名策略的更多详细信息,您可以阅读:https://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch02s02.html
英文:
> 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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论