Spring的自动布线如何工作?


510

对controlIoC)的反转如何在Spring

说我有一个称为服务的类UserServiceImpl,它实现了UserService接口。

怎么会这样@Autowired

而在我Controllers,我怎么会instantiateinstance这个服务的?

我会做以下事情吗?

UserService userService = new UserServiceImpl();

Answers:


703

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

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

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

应用程序上下文中的“生存”是什么?这意味着上下文会实例化对象,而不是您。即-您永远不会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也可以使用。

7
是的,UserServiceImpl带有Service批注,而UserService是接口
Bozho 2010年

16
默认范围是单例,因此您将只有一个bean实例,该实例被注入多个位置。如果您将范围明确定义为“原型”,则将存在多个实例,可能很懒(取决于配置)
Bozho 2013年

2
非常感谢您的帖子,它确实为我清除了一切。关于“由于它将是唯一的实现者或UserService,它将被注入。” -如果有多个实现Userservice的类怎么办?Spring如何知道应该使用哪个实现?
Shishigami 2013年

7
如果有一个被指定为“主要”,则使用它。否则会引发异常
Bozho

3
不,userService仅创建一次,处于单例范围内
Bozho

64

取决于您是要注解路由还是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然后自动将其注入。如果您有一个以上的UserServicebean,则必须确定应使用哪个bean。

如果您执行以下操作:

UserService service = new UserServiceImpl();

@Autowired除非您自行设置,否则它将不会拾取。


2
那么,什么是利用界定bean idapplicationContext.xml。我们将不得不userServiceUserService类型定义变量。那么为什么要在xml文件中输入。
毒蛇

20

@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(););
    }
}

10
这不会编译,通常是不正确的。@Autowired并不意味着“你可以使用所有的功能(方法)和变量B类从类A”。它所做的是将的实例A引入的实例B,因此您可以a.getId()从中进行B
德米特里·明科夫斯基2015年

@dimadima因此,如果他执行System.out.println(“ id形式A类的值” + a.getId());,而不是像他实际所做的那样,它将更加正确。请回覆,因为我很容易理解这一点,并且按照我目前的理解水平,正在解释自动装配。
约翰·多伊

自动装配Autowired注解春季推出2.5 docs.spring.io/spring-framework/docs/2.5.x/api/org/...
SpringLearner

1
为了更好地了解我的新知识,@ autowired会使用默认构造函数实例化A类吗?如果不是,如果使用自动装配,如何在bean或服务中实例化值。我猜如果它调用默认构造函数,为什么要首先使用自动装配,只需执行A a = new A()。请说清楚?
Sameer

@Sameer通过自动装配依赖项,您可以在单元测试以及Controller,Service和Dao类中保存大量样板代码,因为字段的实例化是自动附带的。无需调用构造函数。
kiltek

9

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

如果仍有疑问,请通过下面的现场演示进行

@Autowired在内部如何工作?


6

您只需要使用注释来注释您的服务类UserServiceImpl

@Service("userService")

Spring容器在注册为服务时将照顾此类的生命周期。

然后,您可以在控制器中自动对其进行连线(实例化)并使用其功能:

@Autowired
UserService userService;

3

Spring依赖注入有助于您消除类的耦合。而不是像这样创建对象:

UserService userService = new UserServiceImpl();

介绍DI后,您将使用此功能:

@Autowired
private UserService userService;

为此,您需要在ServiceConfiguration文件中创建服务的bean 。之后,您需要将该ServiceConfiguration类导入到您的WebApplicationConfiguration类中,以便您可以像下面这样将bean自动装配到Controller中:

public class AccController {

    @Autowired
    private UserService userService;
} 

您可以在这里找到一个Java配置基于POC 例子


1

标准方式:

@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或field上使用它。


0

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


0

请记住,必须@Autowired通过将元素添加<context:annotation-config/>到spring配置文件中来启用注释。这将注册AutowiredAnnotationBeanPostProcessor,并注意注释的处理。

然后,您可以使用现场注入方法自动为服务布线。

public class YourController{

 @Autowired
 private UserService userService; 

}

我从Spring @autowired注解中找到了这个


0

您可以使用3种方法来创建实例@Autowired

1. @Autowired关于属性

注释可以直接在属性上使用,因此无需使用getter和setter:

    @Component("userService")
    public class UserService {

        public String getName() {
            return "service name";
        }
    }

    @Component
    public class UserController {

        @Autowired
        UserService userService

    }

在上面的例子中,春季查找并注入userServiceUserController创建。

2. @Autowired在二传手

@Autowired批注可在setter方法中使用。在下面的示例中,在setter方法上使用注释时,将使用userServicewhen UserController创建实例调用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;
    }
}

0

用简单的话说自动装配,即自动连接链接,这是一个问题,由谁进行接线以及哪种接线。答案是:容器可以做到这一点,并且支持辅助类型的接线,基元需要手动完成。

问题:集装箱如何知道哪种类型的接线?

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

问题:有什么方法可以不定义自动布线的类型?

答:是的,只需做一个注释即可,@ Autowired。

问题:但是系统如何知道,我需要选择此类辅助数据?

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.