我对控制反转( IoC
) 的工作原理有点困惑Spring
。
假设我有一个名为UserServiceImpl
实现UserService
接口的服务类。
这会@Autowired
怎样?
而在我的Controllers
,我将如何instantiate
获得instance
这项服务?
我会做以下事情吗?
UserService userService = new UserServiceImpl();
我对控制反转( IoC
) 的工作原理有点困惑Spring
。
假设我有一个名为UserServiceImpl
实现UserService
接口的服务类。
这会@Autowired
怎样?
而在我的Controllers
,我将如何instantiate
获得instance
这项服务?
我会做以下事情吗?
UserService userService = new UserServiceImpl();
首先,也是最重要的——所有 Spring bean 都是托管的——它们“存在”在一个容器中,称为“应用程序上下文”。
其次,每个应用程序都有一个指向该上下文的入口点。Web 应用程序有一个 Servlet,JSF使用一个 el-resolver,等等。此外,还有一个地方可以引导应用程序上下文并且所有 bean 都自动装配。在 Web 应用程序中,这可以是启动侦听器。
自动装配是通过将一个 bean 的实例放入另一个 bean 实例中的所需字段来实现的。两个类都应该是bean,即它们应该被定义为存在于应用程序上下文中。
什么是应用程序上下文中的“生活”?这意味着上下文实例化了对象,而不是你。即 - 你永远不会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
中,您应该启用 ,<context:component-scan>
以便扫描类以查找@Controller
、@Service
等注释。UserServiceImpl
也应该定义为 bean - 使用<bean id=".." class="..">
或使用@Service
注解。由于它将是 的唯一实现者UserService
,因此将被注入。@Autowired
注解之外,Spring 还可以使用 XML 可配置的自动装配。在这种情况下,所有名称或类型与现有 bean 匹配的字段都会自动注入 bean。事实上,这就是自动装配的最初想法——让字段注入依赖项而无需任何配置。也可以使用其他注释,如@Inject
, 。@Resource
取决于您是想要注释路由还是 bean XML 定义路由。
假设您在applicationContext.xml
:
<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
除非您自己设置,否则它不会拾取。
@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
运作?
例子:
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>
如果仍有疑问,请通过下面的现场演示
您只需要使用注释来注释您的服务类UserServiceImpl
:
@Service("userService")
Spring 容器将在注册为服务时处理此类的生命周期。
然后在您的控制器中,您可以自动连接(实例化)它并使用它的功能:
@Autowired
UserService userService;
Spring 依赖注入可帮助您从类中移除耦合。而不是像这样创建对象:
UserService userService = new UserServiceImpl();
在介绍 DI 后,您将使用它:
@Autowired
private UserService userService;
为此,您需要在ServiceConfiguration
文件中创建服务的 bean。之后,您需要将该ServiceConfiguration
类导入您的WebApplicationConfiguration
类,以便您可以像这样将该 bean 自动装配到您的控制器中:
public class AccController {
@Autowired
private UserService userService;
}
您可以在此处找到基于 Java 配置的 POC 示例。
有 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在创建userService
时查找并注入。UserController
2.@Autowired
关于二传手
注解可@Autowired
用于 setter 方法。在下面的示例中,当在 setter 方法上使用注解时,将使用userService
when的实例调用 setter 方法UserController
:
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;
}
}
简而言之,自动布线,自动布线链接,现在来了谁做这个以及哪种布线的问题。答案是:容器会这样做,并且支持次要类型的布线,原语需要手动完成。
问题:容器如何知道什么类型的接线?
答:我们定义为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 文件。但是,new
当您不想加载整个 spring 上下文时,在单元测试中手动(使用关键字)实例化类并不是一个坏主意。
请记住,您必须通过将元素添加到 spring 配置文件中来启用@Autowired
注释。<context:annotation-config/>
这将注册AutowiredAnnotationBeanPostProcessor
负责处理注释的。
然后您可以使用字段注入方法自动连接您的服务。
public class YourController{
@Autowired
private UserService userService;
}