我对 inversion of control (IoC
) 在 Spring
中的工作方式有些困惑。
假设我有一个名为 UserServiceImpl
的服务类,它实现了 UserService
接口。
这会是什么@Autowired
?
在我的 Controllers
中,我将如何instantiate
获得这项服务的instance
?
我会做以下事情吗?
UserService userService = new UserServiceImpl();
首先,也是最重要的——所有 Spring bean 都是托管的——它们“存在”在一个容器中,称为“应用程序上下文”。
其次,每个应用程序都有一个指向该上下文的入口点。 Web 应用程序有一个 Servlet,JSF 使用一个 el-resolver,等等。此外,还有一个地方可以引导应用程序上下文并且所有 bean - 自动装配。在 Web 应用程序中,这可以是启动侦听器。
自动装配是通过将一个 bean 的实例放入另一个 bean 实例中的所需字段来实现的。两个类都应该是bean,即它们应该被定义为存在于应用程序上下文中。
什么是应用程序上下文中的“生活”?这意味着 context 实例化了对象,而不是你。即 - 你永远不会创建 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 中,您应该启用
Spring-MVC 应用程序的入口点是 DispatcherServlet,但它对您隐藏,因此应用程序上下文的直接交互和引导发生在幕后。
UserServiceImpl 也应该定义为 bean - 使用
除了 @Autowired 注解之外,Spring 还可以使用 XML 可配置的自动装配。在这种情况下,所有名称或类型与现有 bean 匹配的字段都会自动注入 bean。事实上,这就是自动装配的最初想法——让字段注入依赖项而无需任何配置。也可以使用其他注解,如@Inject、@Resource。
取决于您是想要注释路由还是 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
。
applicationContext.xml
中定义 bean id
有什么用。我们必须用 UserService
类型定义 userService
变量。那么为什么要在 xml
文件中输入。
@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(););
}
}
@Autowired
并不意味着“您可以使用类 A
中的 B
类中的所有函数(方法)和变量”。它所做的是将 A
的实例带入 B
的实例中,因此您可以从 B
执行 a.getId()
。
@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>
如果仍有疑问,请通过下面的现场演示
How does @Autowired work internally ?
您只需要使用注解来注解您的服务类 UserServiceImpl
:
@Service("userService")
Spring 容器将在注册为服务时处理此类的生命周期。
然后在您的控制器中,您可以自动连接(实例化)它并使用它的功能:
@Autowired
UserService userService;
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 example。
您可以通过 3 种方法使用 @Autowired
创建实例。
<强> 1。 @Autowired
属性
注释可以直接在属性上使用,因此不需要 getter 和 setter:
@Component("userService")
public class UserService {
public String getName() {
return "service name";
}
}
@Component
public class UserController {
@Autowired
UserService userService
}
在上面的示例中,Spring 在创建 UserController
时查找并注入 userService
。
<强> 2。 @Autowired
在二传手上
@Autowired
注释可用于 setter 方法。在下面的示例中,当在 setter 方法上使用注解时,会在创建 UserController
时使用 userService
的实例调用 setter 方法:
public class UserController {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
}
<强> 3。 @Autowired
关于构造函数
@Autowired
注释也可用于构造函数。在下面的示例中,当在构造函数上使用注解时,会在创建 UserController
时将 userService
的实例作为参数注入构造函数:
public class UserController {
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService= userService;
}
}
简而言之,自动布线,自动布线链接,现在来了谁做这个以及哪种布线的问题。答案是:容器会这样做,并且支持次要类型的布线,原语需要手动完成。
问题:容器如何知道什么类型的接线?
答:我们定义为byType,byName,constructor。
问题:有没有我们不定义自动装配类型的方法?
答案:是的,通过一个注释@Autowired 就可以了。
问题:但是系统怎么知道,我需要选择这种辅助数据?
答:您将在 spring.xml 文件中提供该数据,或者通过对您的类使用 sterotype 注释来提供该数据,以便容器自己可以为您创建对象。
标准方式:
@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 或字段上使用。
控制反转的整个概念意味着您无需手动实例化对象并提供所有必要的依赖项。当您使用适当的注释(例如 @Service
)注释类时,Spring 将自动为您实例化对象。如果您不熟悉注解,您也可以使用 XML 文件。但是,当您不想加载整个 spring 上下文时,在单元测试中手动实例化类(使用 new
关键字)并不是一个坏主意。
请记住,您必须通过将元素 <context:annotation-config/>
添加到 spring 配置文件中来启用 @Autowired
注释。这将注册负责处理注释的 AutowiredAnnotationBeanPostProcessor
。
然后您可以使用字段注入方法自动连接您的服务。
public class YourController{
@Autowired
private UserService userService;
}
我从帖子 Spring @autowired annotation 中找到了这个
不定期副业成功案例分享