对于Spring中的控制反转(IoC)是如何工作的,我有点困惑。
假设我有一个名为UserServiceImpl的服务类,它实现了UserService接口。
@Autowired会怎么做?
在我的控制器中,我如何实例化这个服务的实例?
我能做下面的事情吗?
UserService userService = new UserServiceImpl();
对于Spring中的控制反转(IoC)是如何工作的,我有点困惑。
假设我有一个名为UserServiceImpl的服务类,它实现了UserService接口。
@Autowired会怎么做?
在我的控制器中,我如何实例化这个服务的实例?
我能做下面的事情吗?
UserService userService = new UserServiceImpl();
当前回答
简单地说,自动连接链接,现在的问题是谁做这个,哪种连接。 答案是:容器是这样做的,次要类型的连接是支持的,原语需要手动完成。
问:容器如何知道哪种类型的布线?
答:我们将其定义为byType,byName,构造函数。
问:有没有办法我们不定义类型的自动装配?
答:是的,它通过一个注释@Autowired来实现。
问:但是系统怎么知道,我需要选择这类辅助数据呢?
答:您将在spring.xml文件中提供该数据,或者通过对类使用原型注释,以便容器自己为您创建对象。
其他回答
这取决于您是想要注释路由还是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。
简单地说,自动连接链接,现在的问题是谁做这个,哪种连接。 答案是:容器是这样做的,次要类型的连接是支持的,原语需要手动完成。
问:容器如何知道哪种类型的布线?
答:我们将其定义为byType,byName,构造函数。
问:有没有办法我们不定义类型的自动装配?
答:是的,它通过一个注释@Autowired来实现。
问:但是系统怎么知道,我需要选择这类辅助数据呢?
答:您将在spring.xml文件中提供该数据,或者通过对类使用原型注释,以便容器自己为您创建对象。
标准方法:
@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 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.
有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;
}
}