【问题标题】:How does autowiring work in Spring?自动装配在 Spring 中是如何工作的?
【发布时间】:2011-03-10 08:58:12
【问题描述】:

我对@9​​87654321@ (IoC) 在Spring 中的工作方式有点困惑。

假设我有一个名为 UserServiceImpl 的服务类,它实现了 UserService 接口。

@Autowired 会怎样?

在我的Controllers 中,我将如何instantiateinstance 提供这项服务?

我会做以下事情吗?

UserService userService = new UserServiceImpl();

【问题讨论】:

    标签: java spring spring-mvc ioc-container autowired


    【解决方案1】:

    首先,也是最重要的 - 所有 Spring bean 都是托管的 - 它们“存在”在一个容器中,称为“应用程序上下文”。

    其次,每个应用程序都有一个指向该上下文的入口点。 Web 应用程序有一个 Servlet,JSF 使用一个 el-resolver,等等。此外,还有一个地方可以引导应用程序上下文并且所有 bean - 自动装配。在 Web 应用程序中,这可以是启动侦听器。

    自动装配是通过将一个 bean 的实例放入另一个 bean 实例的所需字段中来实现的。这两个类都应该是 bean,即它们应该被定义为存在于应用程序上下文中。

    什么是应用程序上下文中的“生活”?这意味着 context 实例化了对象,而不是你。 IE。 - 你永远不会创建new UserServiceImpl() - 容器会找到每个注入点并在那里设置一个实例。

    在您的控制器中,您只有以下内容:

    @Controller // Defines that this class is a spring bean
    @RequestMapping("/users")
    public class SomeController {
    
        // Tells the application context to inject an instance of UserService here
        @Autowired
        private UserService userService;
    
        @RequestMapping("/login")
        public void login(@RequestParam("username") String username,
               @RequestParam("password") String password) {
    
            // The UserServiceImpl is already injected and you can use it
            userService.login(username, password);
    
        }
    }
    

    几点说明:

    • 在您的applicationContext.xml 中,您应该启用<context:component-scan>,以便扫描类以查找@Controller@Service 等注释。
    • Spring-MVC 应用程序的入口点是 DispatcherServlet,但它对您隐藏,因此应用程序上下文的直接交互和引导发生在幕后。
    • UserServiceImpl 也应该定义为 bean - 使用 <bean id=".." class=".."> 或使用 @Service 注释。由于它将是UserService 的唯一实现者,因此将被注入。
    • 除了@Autowired 注解,Spring 可以使用 XML 可配置的自动装配。在这种情况下,所有名称或类型与现有 bean 匹配的字段都会自动注入 bean。事实上,这就是自动装配的最初想法——让字段注入依赖项而无需任何配置。也可以使用@Inject@Resource 等其他注解。

    【讨论】:

    • 是的,UserServiceImpl注解了Service,UserService就是接口
    • 默认作用域是单例的,所以你将只有一个 bean 的实例,它被注入到多个地方。如果您将范围明确定义为“原型”,则将存在多个实例,可能是惰性的(取决于配置)
    • 非常感谢您的帖子,它真的为我清理了一切。关于“由于它将是唯一的实现者或用户服务,它将被注入。” - 如果有多个实现用户服务的类怎么办? Spring 如何知道它应该使用哪个实现?
    • 如果有一个被指定为“主要”,它会使用它。否则抛出异常
    • 不,userService 只创建一次,它在单例范围内
    【解决方案2】:

    取决于你是想要注解路由还是 bean XML 定义路由。

    假设您在 applicationContext.xml 中定义了 bean:

    <beans ...>
    
        <bean id="userService" class="com.foo.UserServiceImpl"/>
    
        <bean id="fooController" class="com.foo.FooController"/>
    
    </beans>
    

    自动装配发生在应用程序启动时。因此,在 fooController 中,为了争论,它想要使用 UserServiceImpl 类,您可以将其注释如下:

    public class FooController {
    
        // You could also annotate the setUserService method instead of this
        @Autowired
        private UserService userService;
    
        // rest of class goes here
    }
    

    当它看到@Autowired 时,Spring 会在applicationContext 中寻找与该属性匹配的类,并自动注入它。如果你有多个UserService bean,那么你必须限定它应该使用哪一个。

    如果您执行以下操作:

    UserService service = new UserServiceImpl();
    

    除非您自己设置,否则它不会接收@Autowired

    【讨论】:

    • 那么在applicationContext.xml 中定义bean id 有什么用。我们必须用UserService 类型定义userService 变量。那么为什么要在xml 文件中输入。
    • @viper 他说的是接口我相信
    【解决方案3】:

    @Autowired是Spring 2.5引入的注解,仅用于注入。

    例如:

    class A {
    
        private int id;
    
        // With setter and getter method
    }
    
    class B {
    
        private String name;
    
        @Autowired // Here we are injecting instance of Class A into class B so that you can use 'a' for accessing A's instance variables and methods.
        A a;
    
        // With setter and getter method
    
        public void showDetail() {
            System.out.println("Value of id form A class" + a.getId(););
        }
    }
    

    【讨论】:

    • 这不会编译并且通常是不正确的。 @Autowired 并不意味着“您可以使用类AB 类中的所有函数(方法)和变量”。它所做的是将A 的实例带入B 的实例中,因此您可以从B 执行a.getId()
    • @dimadima 因此,如果他执行 System.out.println("Value of id form A class" + a.getId());,而不是像他实际所做的那样,它会更正确。请回复,因为这对我来说很直观,并且根据我目前的理解水平正在解释自动装配。
    • 在 spring 2.5 中引入了 autowired 注解docs.spring.io/spring-framework/docs/2.5.x/api/org/…
    • 为了更好地理解我是新手,@autowired 会使用默认构造函数实例化 A 类吗?如果不是,如果我们使用自动装配,如何在 bean 或服务中实例化值。我猜如果它调用默认构造函数,为什么首先使用自动装配,只需执行 A a = new A()。请澄清一下?
    • @Sameer 通过自动装配依赖项,您可以在单元测试以及控制器、服务和 Dao 类中保存大量样板代码,因为字段的实例化会自动附带。无需调用构造函数。
    【解决方案4】:

    @Autowired 在内部是如何工作的?

    例子:

    class EnglishGreeting {
       private Greeting greeting;
       //setter and getter
    }
    
    class Greeting {
       private String message;
       //setter and getter
    }
    

    .xml 文件,如果不使用@Autowired,它将看起来很像:

    <bean id="englishGreeting" class="com.bean.EnglishGreeting">
       <property name="greeting" ref="greeting"/>
    </bean>
    
    <bean id="greeting" class="com.bean.Greeting">
       <property name="message" value="Hello World"/>
    </bean>
    

    如果您使用的是@Autowired,那么:

    class EnglishGreeting {
       @Autowired //so automatically based on the name it will identify the bean and inject.
       private Greeting greeting;
       //setter and getter
    }
    

    .xml 文件,如果不使用@Autowired,它将看起来很像:

    <bean id="englishGreeting" class="com.bean.EnglishGreeting"></bean>
    
    <bean id="greeting" class="com.bean.Greeting">
       <property name="message" value="Hello World"/>
    </bean>
    

    如果还有疑问,请看下面的现场演示

    How does @Autowired work internally ?

    【讨论】:

      【解决方案5】:

      你只需要用注解来注解你的服务类UserServiceImpl

      @Service("userService")
      

      Spring 容器在注册为服务时会处理此类的生命周期。

      然后在您的控制器中,您可以自动连接(实例化)它并使用它的功能:

      @Autowired
      UserService userService;
      

      【讨论】:

        【解决方案6】:

        Spring 依赖注入可帮助您从类中移除耦合。 而不是像这样创建对象:

        UserService userService = new UserServiceImpl();
        

        你将在介绍 DI 后使用它:

        @Autowired
        private UserService userService;
        

        为了实现这一点,您需要在 ServiceConfiguration 文件中创建服务的 bean。之后,您需要将该 ServiceConfiguration 类导入您的 WebApplicationConfiguration 类,以便您可以像这样将该 bean 自动装配到您的控制器中:

        public class AccController {
        
            @Autowired
            private UserService userService;
        } 
        

        您可以在此处找到基于 Java 配置的 POC example.

        【讨论】:

          【解决方案7】:

          您可以通过 3 种方式使用 @Autowired 创建实例。

          1. @Autowired 属性

          注解可以直接用在属性上,因此不需要getter和setter:

              @Component("userService")
              public class UserService {
          
                  public String getName() {
                      return "service name";
                  }
              }
          
              @Component
              public class UserController {
          
                  @Autowired
                  UserService userService
          
              }
          

          在上面的示例中,Spring 在创建 UserController 时查找并注入 userService

          2。 @Autowired 在二传手上

          @Autowired 注释可用于 setter 方法。在下面的示例中,当在 setter 方法上使用注解时,会在创建 UserController 时使用 userService 的实例调用 setter 方法:

          public class UserController {
          
              private UserService userService;
          
              @Autowired
              public void setUserService(UserService userService) {
                      this.userService = userService;
              }
          }
          

          3. @Autowired 关于构造函数

          @Autowired 注解也可用于构造函数。在下面的示例中,当在构造函数上使用注解时,userService 的实例会在创建 UserController 时作为参数注入构造函数:

          public class UserController {
          
              private UserService userService;
          
              @Autowired
              public UserController(UserService userService) {
                  this.userService= userService;
              }
          }
          

          【讨论】:

            【解决方案8】:

            简而言之,自动布线,自动布线链接,现在出现的问题是由谁执行此操作以及哪种布线。 答案是:容器会这样做,并且支持次要类型的布线,原语需要手动完成。

            问题:容器如何知道什么类型的接线?

            答案:我们定义为byType,byName,constructor。

            问题:有没有我们不定义自动装配类型的方法?

            回答:是的,通过一个注释@Autowired 就可以做到这一点。

            问题:但是系统怎么知道,我需要选择这种辅助数据?

            回答:您将在 spring.xml 文件中提供该数据,或者通过对您的类使用 sterotype 注释来提供该数据,以便容器自己可以为您创建对象。

            【讨论】:

              【解决方案9】:

              标准方式:

              @RestController
              public class Main {
                  UserService userService;
              
                  public Main(){
                      userService = new UserServiceImpl();
                  }
              
                  @GetMapping("/")
                  public String index(){
                      return userService.print("Example test");
                  }
              }
              

              用户服务接口:

              public interface UserService {
                  String print(String text);
              }
              

              UserServiceImpl 类:

              public class UserServiceImpl implements UserService {
                  @Override
                  public String print(String text) {
                      return text + " UserServiceImpl";
                  }
              }
              

              输出:Example test UserServiceImpl

              这是紧耦合类的一个很好的例子,糟糕的设计例子和测试会有问题(PowerMockito 也很糟糕)。

              现在让我们看一下 SpringBoot 依赖注入,松耦合的好例子:

              界面保持不变,

              主类:

              @RestController
              public class Main {
                  UserService userService;
              
                  @Autowired
                  public Main(UserService userService){
                      this.userService = userService;
                  }
              
                  @GetMapping("/")
                  public String index(){
                      return userService.print("Example test");
                  }
              }
              

              ServiceUserImpl 类:

              @Component
              public class UserServiceImpl implements UserService {
                  @Override
                  public String print(String text) {
                      return text + " UserServiceImpl";
                  }
              }
              

              输出:Example test UserServiceImpl

              现在编写测试很容易:

              @RunWith(MockitoJUnitRunner.class)
              public class MainTest {
                  @Mock
                  UserService userService;
              
                  @Test
                  public void indexTest() {
                      when(userService.print("Example test")).thenReturn("Example test UserServiceImpl");
              
                      String result = new Main(userService).index();
              
                      assertEquals(result, "Example test UserServiceImpl");
                  }
              }
              

              我在构造函数上显示了@Autowired 注释,但它也可以用于 setter 或字段。

              【讨论】:

              • 为什么要为主类创建构造函数?而不是自动装配声明?
              【解决方案10】:

              控制反转的整个概念意味着您无需手动实例化对象并提供所有必要的依赖项。 当您使用适当的注释(例如@Service)注释类时,Spring 将自动为您实例化对象。如果您不熟悉注解,您也可以使用 XML 文件。但是,当您不想加载整个 spring 上下文时,在单元测试中手动实例化类(使用 new 关键字)并不是一个坏主意。

              【讨论】:

                【解决方案11】:

                请记住,您必须通过将元素 &lt;context:annotation-config/&gt; 添加到 spring 配置文件中来启用 @Autowired 注释。这将注册AutowiredAnnotationBeanPostProcessor,它负责处理注释。

                然后您可以使用字段注入方法自动装配您的服务。

                public class YourController{
                
                 @Autowired
                 private UserService userService; 
                
                }
                

                我是从Spring @autowired annotation 帖子中找到的

                【讨论】:

                  猜你喜欢
                  • 2020-11-19
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多