对于Spring中的控制反转(IoC)是如何工作的,我有点困惑。

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

@Autowired会怎么做?

在我的控制器中,我如何实例化这个服务的实例?

我能做下面的事情吗?

UserService userService = new UserServiceImpl();

当前回答

你只需要用annotation注释你的服务类UserServiceImpl:

@Service("userService")

Spring容器将在该类注册为服务时负责其生命周期。

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

@Autowired
UserService userService;

其他回答

@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内部是如何工作的?

标准方法:

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

输出:示例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";
    }
}

输出:示例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。

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

UserService userService = new UserServiceImpl();

你将在引入DI之后使用它:

@Autowired
private UserService userService;

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

public class AccController {

    @Autowired
    private UserService userService;
} 

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

有3种方法可以使用@Autowired创建实例。

1. @Autowired on Properties

注释可以直接用于属性,因此不需要getter和setter:

    @Component("userService")
    public class UserService {

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

    @Component
    public class UserController {

        @Autowired
        UserService userService

    }

在上面的例子中,当创建UserController时,Spring会查找并注入userService。

2. @自动连接设置

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

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

首先,也是最重要的一点——所有Spring bean都是受管理的——它们“生活”在一个容器中,称为“应用程序上下文”。

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

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

在应用程序上下文中,什么是“生活”?这意味着实例化对象的是上下文,而不是您。也就是说——你永远不会创建新的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);

    }
}

注意事项:

In your applicationContext.xml you should enable the <context:component-scan> so that classes are scanned for the @Controller, @Service, etc. annotations. 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. UserServiceImpl should also be defined as bean - either using <bean id=".." class=".."> or using the @Service annotation. Since it will be the only implementor of UserService, it will be injected. Apart from the @Autowired annotation, Spring can use XML-configurable autowiring. In that case all fields that have a name or type that matches with an existing bean automatically get a bean injected. 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.