为什么Spring的@Component注解不使用完整的类名?

huangapple go评论125阅读模式
英文:

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 @Components 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

huangapple
  • 本文由 发表于 2020年3月4日 03:17:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/60514124.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定