简体   繁体   English

自动装配在 Spring 中是如何工作的?

[英]How does autowiring work in Spring?

I'm a little confused as to how the inversion of control ( IoC ) works in Spring .我对Spring的控制反转( IoC ) 如何工作感到有些困惑。

Say I have a service class called UserServiceImpl that implements UserService interface.假设我有一个名为UserServiceImpl的服务类,它实现了UserService接口。

How would this be @Autowired ?这将如何@Autowired

And in my Controllers , how would I instantiate an instance of this service?在我的Controllers ,我将如何instantiate此服务的instance

Would I just do the following?我会做以下事情吗?

UserService userService = new UserServiceImpl();

First, and most important - all Spring beans are managed - they "live" inside a container, called "application context".首先,也是最重要的——所有 Spring bean 都被管理——它们“存在于”一个容器中,称为“应用程序上下文”。

Second, each application has an entry point to that context.其次,每个应用程序都有一个该上下文的入口点。 Web applications have a Servlet, JSF uses a el-resolver, etc. Also, there is a place where the application context is bootstrapped and all beans - autowired. Web 应用程序有一个 Servlet, JSF使用一个 el-resolver 等。此外,还有一个地方可以引导应用程序上下文和所有 bean - 自动装配。 In web applications this can be a startup listener.在 Web 应用程序中,这可以是启动侦听器。

Autowiring happens by placing an instance of one bean into the desired field in an instance of another bean.自动装配是通过将一个 bean 的实例放入另一个 bean 实例中的所需字段来实现的。 Both classes should be beans, ie they should be defined to live in the application context.这两个类都应该是 bean,即它们应该被定义为存在于应用程序上下文中。

What is "living" in the application context?什么是应用程序上下文中的“生活”? This means that the context instantiates the objects, not you.这意味着上下文实例化对象,而不是您。 Ie - you never make new UserServiceImpl() - the container finds each injection point and sets an instance there.即 - 你永远不会创建new UserServiceImpl() - 容器找到每个注入点并在那里设置一个实例。

In your controllers, you just have the following:在您的控制器中,您只有以下内容:

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

    }
}

A few notes:一些注意事项:

  • In your applicationContext.xml you should enable the <context:component-scan> so that classes are scanned for the @Controller , @Service , etc. annotations.在您的applicationContext.xml您应该启用<context:component-scan>以便为@Controller@Service等注释扫描类。
  • The entry point for a Spring-MVC application is the DispatcherServlet, but it is hidden from you, and hence the direct interaction and bootstrapping of the application context happens behind the scene. Spring-MVC 应用程序的入口点是 DispatcherServlet,但它对您隐藏,因此应用程序上下文的直接交互和引导发生在幕后。
  • UserServiceImpl should also be defined as bean - either using <bean id=".." class=".."> or using the @Service annotation. UserServiceImpl也应该定义为 bean - 使用<bean id=".." class="..">或使用@Service注释。 Since it will be the only implementor of UserService , it will be injected.由于它将是UserService的唯一实现者,因此它将被注入。
  • Apart from the @Autowired annotation, Spring can use XML-configurable autowiring.除了@Autowired注释,Spring 还可以使用 XML 可配置的自动装配。 In that case all fields that have a name or type that matches with an existing bean automatically get a bean injected.在这种情况下,所有名称或类型与现有 bean 匹配的字段都会自动注入 bean。 In fact, that was the initial idea of autowiring - to have fields injected with dependencies without any configuration.事实上,这是自动装配的最初想法——在没有任何配置的情况下注入依赖项的字段。 Other annotations like @Inject , @Resource can also be used.也可以使用其他注释,如@Inject@Resource

Depends on whether you want the annotations route or the bean XML definition route.取决于您是想要注释路由还是 bean XML 定义路由。

Say you had the beans defined in your applicationContext.xml :假设您在applicationContext.xml定义了 bean:

<beans ...>

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

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

</beans>

The autowiring happens when the application starts up.自动装配发生在应用程序启动时。 So, in fooController , which for arguments sake wants to use the UserServiceImpl class, you'd annotate it as follows:因此,在fooController ,出于参数的fooController ,它希望使用UserServiceImpl类,您可以按如下方式对其进行注释:

public class FooController {

    // You could also annotate the setUserService method instead of this
    @Autowired
    private UserService userService;

    // rest of class goes here
}

When it sees @Autowired , Spring will look for a class that matches the property in the applicationContext , and inject it automatically.当它看到@Autowired ,Spring 将在applicationContext查找与该属性匹配的类,并自动注入它。 If you have more than one UserService bean, then you'll have to qualify which one it should use.如果您有多个UserService bean,那么您必须确定它应该使用哪一个。

If you do the following:如果您执行以下操作:

UserService service = new UserServiceImpl();

It will not pick up the @Autowired unless you set it yourself.除非您自己设置,否则它不会接收@Autowired

@Autowired is an annotation introduced in Spring 2.5, and it's used only for injection. @Autowired是 Spring 2.5 引入的注解,仅用于注入。

For example:例如:

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

How does @Autowired work internally? @Autowired在内部如何工作?

Example:例子:

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

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

.xml file it will look alike if not using @Autowired : .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>

If you are using @Autowired then:如果您使用@Autowired则:

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

.xml file it will look alike if not using @Autowired : .xml 文件,如果不使用@Autowired它看起来很像:

<bean id="englishGreeting" class="com.bean.EnglishGreeting"></bean>

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

If still have some doubt then go through below live demo如果仍有疑问,请通过下面的现场演示

How does @Autowired work internally ? @Autowired 如何在内部工作?

You just need to annotate your service class UserServiceImpl with annotation:您只需要使用注释对您的服务类UserServiceImpl进行注释:

@Service("userService")

Spring container will take care of the life cycle of this class as it register as service. Spring 容器将在该类注册为服务时负责该类的生命周期。

Then in your controller you can auto wire (instantiate) it and use its functionality:然后在您的控制器中,您可以自动连接(实例化)它并使用其功能:

@Autowired
UserService userService;

Spring dependency inject help you to remove coupling from your classes. Spring 依赖注入可帮助您从类中移除耦合。 Instead of creating object like this:而不是像这样创建对象:

UserService userService = new UserServiceImpl();

You will be using this after introducing DI:介绍 DI 后,您将使用它:

@Autowired
private UserService userService;

For achieving this you need to create a bean of your service in your ServiceConfiguration file.为此,您需要在ServiceConfiguration文件中创建服务的 bean。 After that you need to import that ServiceConfiguration class to your WebApplicationConfiguration class so that you can autowire that bean into your Controller like this:之后,您需要将该ServiceConfiguration类导入到您的WebApplicationConfiguration类中,以便您可以将该 bean 自动装配到您的控制器中,如下所示:

public class AccController {

    @Autowired
    private UserService userService;
} 

You can find a java configuration based POC here example .您可以在 示例中找到基于 Java 配置的 POC。

There are 3 ways you can create an instance using @Autowired .有 3 种方法可以使用@Autowired创建实例。

1. @Autowired on Properties 1.@ @Autowired属性

The annotation can be used directly on properties, therefore eliminating the need for getters and setters:注释可以直接用于属性,因此无需使用 getter 和 setter:

    @Component("userService")
    public class UserService {

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

    @Component
    public class UserController {

        @Autowired
        UserService userService

    }

In the above example, Spring looks for and injects userService when UserController is created.在上面的例子中,Spring 在创建UserController时查找并注入userService

2. @Autowired on Setters 2. @Autowired在 Setter 上

The @Autowired annotation can be used on setter methods. @Autowired注释可用于 setter 方法。 In the below example, when the annotation is used on the setter method, the setter method is called with the instance of userService when UserController is created:在下面的示例中,当在 setter 方法上使用注解时,setter 方法会在创建UserController时使用userService的实例调用:

public class UserController {

    private UserService userService;

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

3. @Autowired on Constructors 3. @Autowired在构造函数上

The @Autowired annotation can also be used on constructors. @Autowired注解也可以用在构造函数上。 In the below example, when the annotation is used on a constructor, an instance of userService is injected as an argument to the constructor when UserController is created:在下面的示例中,当在构造函数上使用注解时,会在创建UserController时将userService的实例作为参数注入构造函数:

public class UserController {

    private UserService userService;

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

Standard way:标准方式:

@RestController
public class Main {
    UserService userService;

    public Main(){
        userService = new UserServiceImpl();
    }

    @GetMapping("/")
    public String index(){
        return userService.print("Example test");
    }
}

User service interface:用户服务界面:

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

UserServiceImpl class: UserServiceImpl 类:

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

Output: Example test UserServiceImpl输出: Example test UserServiceImpl

That is a great example of tight coupled classes, bad design example and there will be problem with testing (PowerMockito is also bad).这是紧耦合类的一个很好的例子,糟糕的设计例子,测试会有问题(PowerMockito 也很糟糕)。

Now let's take a look at SpringBoot dependency injection, nice example of loose coupling:现在让我们来看看 SpringBoot 依赖注入,松耦合的好例子:

Interface remains the same,界面保持不变,

Main class:主要类:

@RestController
public class Main {
    UserService userService;

    @Autowired
    public Main(UserService userService){
        this.userService = userService;
    }

    @GetMapping("/")
    public String index(){
        return userService.print("Example test");
    }
}

ServiceUserImpl class: ServiceUserImpl 类:

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

Output: Example test UserServiceImpl输出: Example test UserServiceImpl

and now it's easy to write test:现在很容易编写测试:

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

I showed @Autowired annotation on constructor but it can also be used on setter or field.我在构造函数上显示了@Autowired注释,但它也可以用于 setter 或字段。

The whole concept of inversion of control means you are free from a chore to instantiate objects manually and provide all necessary dependencies.控制反转的整个概念意味着您无需手动实例化对象并提供所有必要的依赖项。 When you annotate class with appropriate annotation (eg @Service ) Spring will automatically instantiate object for you.当您使用适当的注释(例如@Service )注释类时,Spring 将自动为您实例化对象。 If you are not familiar with annotations you can also use XML file instead.如果您不熟悉注释,也可以使用 XML 文件代替。 However, it's not a bad idea to instantiate classes manually (with the new keyword) in unit tests when you don't want to load the whole spring context.但是,当您不想加载整个 spring 上下文时,在单元测试中手动(使用new关键字)实例化类并不是一个坏主意。

Keep in mind that you must enable the @Autowired annotation by adding element <context:annotation-config/> into the spring configuration file.请记住,您必须通过在 spring 配置文件中添加元素<context:annotation-config/>来启用@Autowired注解。 This will register the AutowiredAnnotationBeanPostProcessor which takes care the processing of annotation.这将注册AutowiredAnnotationBeanPostProcessor ,它负责注解的处理。

And then you can autowire your service by using the field injection method.然后您可以使用字段注入方法自动装配您的服务。

public class YourController{

 @Autowired
 private UserService userService; 

}

I found this from the post Spring @autowired annotation我从Spring @autowired 注释中找到了这个

In simple words Autowiring, wiring links automatically, now comes the question who does this and which kind of wiring.简单地说,自动布线,自动布线,现在问题是谁做的,哪种布线。 Answer is: Container does this and Secondary type of wiring is supported, primitives need to be done manually.答案是:Container 执行此操作,并且支持辅助类型的接线,需要手动完成原语。

Question: How container know what type of wiring ?问题:容器如何知道什么类型的接线?

Answer: We define it as byType,byName,constructor.答:我们将其定义为 byType,byName,constructor。

Question: Is there are way we do not define type of autowiring ?问题:有没有办法不定义自动装配的类型?

Answer: Yes, it's there by doing one annotation, @Autowired.回答:是的,它是通过做一个注释来实现的,@Autowired。

Question: But how system know, I need to pick this type of secondary data ?问题:但是系统怎么知道,我需要选择这种类型的辅助数据?

Answer: You will provide that data in you spring.xml file or by using sterotype annotations to your class so that container can themselves create the objects for you.答:您将在 spring.xml 文件中或通过对类使用构造型注释来提供该数据,以便容器可以自己为您创建对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM