对于Spring中的控制反转(IoC)是如何工作的,我有点困惑。
假设我有一个名为UserServiceImpl的服务类,它实现了UserService接口。
@Autowired会怎么做?
在我的控制器中,我如何实例化这个服务的实例?
我能做下面的事情吗?
UserService userService = new UserServiceImpl();
对于Spring中的控制反转(IoC)是如何工作的,我有点困惑。
假设我有一个名为UserServiceImpl的服务类,它实现了UserService接口。
@Autowired会怎么做?
在我的控制器中,我如何实例化这个服务的实例?
我能做下面的事情吗?
UserService userService = new UserServiceImpl();
当前回答
首先,也是最重要的一点——所有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.
其他回答
控制反转的整个概念意味着您无需手动实例化对象并提供所有必要的依赖项。 当你用适当的注释(例如@Service)注释类时,Spring会自动为你实例化对象。如果您不熟悉注释,也可以使用XML文件代替。然而,当您不想加载整个spring上下文时,在单元测试中手动实例化类(使用new关键字)并不是一个坏主意。
标准方法:
@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配置 的例子。
你只需要用annotation注释你的服务类UserServiceImpl:
@Service("userService")
Spring容器将在该类注册为服务时负责其生命周期。
然后在你的控制器中,你可以自动连接(实例化)它并使用它的功能:
@Autowired
UserService userService;
这取决于您是想要注释路由还是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。