关注微信公众号 “程序员小胖” 每日技术干货,第一时间送达!
引言
在软件开发中,对象之间的依赖关系管理是一项至关重要的任务。传统的面向对象编程中,对象之间的依赖关系通常通过手动创建和管理对象实例来实现。然而,随着软件系统的复杂性不断增加,手动管理对象之间的依赖关系变得越来越困难,容易导致代码的复杂性和耦合度增加,降低系统的灵活性和可维护性。为了解决这个问题,出现了一种称为IOC(Inversion of Control,控制反转)的设计模式
在本系列文章中,我们将深入探讨IOC容器的原理、实现方式、使用方法以及常见的应用场景和最佳实践。
Spring IOC是什么
要说清楚IOC是什么,肯定少不了三个概念
- 什么是IOC容器
IOC容器: 实际上就是个map(key,value),里面存的是各种对象(在xml里配置的bean节点、@repository、@service、@controller、@component),在项目启动的时候会读取配置文件里面的bean节点,根据全限定类名使用反射创建对象放到map里、扫描到打上上述注解的类还是通过反射创建对象放到map里。
这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI注入(autowired、resource等注解,xml里bean节点内的ref属性,项目启动的时候会读取xml节点ref属性根据id注入,也会扫描这些注解,根据类型或id注入;id就是对象名)
代码示例:
// 定义一个接口
public interface MessageService {
String getMessage();
}
// 实现接口
public class EmailService implements MessageService {
@Override
public String getMessage() {
return "Email message";
}
}
// 另一个实现接口的类
public class SMSService implements MessageService {
@Override
public String getMessage() {
return "SMS message";
}
}
// 定义一个服务类,依赖于MessageService接口
public class MyService {
private MessageService messageService;
// 通过构造函数注入依赖
public MyService(MessageService messageService) {
this.messageService = messageService;
}
public void processMessage() {
String message = messageService.getMessage();
System.out.println("Processing message: " + message);
}
}
// 主应用程序类
public class MainApp {
public static void main(String[] args) {
// 创建IOC容器
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 从容器中获取MyService对象
MyService service = context.getBean(MyService.class);
// 调用服务方法
service.processMessage();
}
}
// 配置类,用于声明bean和它们之间的依赖关系
@Configuration
public class AppConfig {
// 声明EmailService为一个bean
@Bean
public MessageService emailService() {
return new EmailService();
}
// 声明SMSService为一个bean
@Bean
public MessageService smsService() {
return new SMSService();
}
// 声明MyService为一个bean,并注入依赖
@Bean
public MyService myService() {
return new MyService(emailService()); // 也可以替换成smsService()
}
}
在这个示例中,我们定义了一个接口MessageService和两个实现类EmailService和SMSService,它们分别代表发送电子邮件和短信的服务。然后,我们定义了一个服务类MyService,它依赖于MessageService接口。在MainApp类中,我们通过Spring IOC容器获取了MyService对象,并调用了其方法。通过配置类AppConfig,我们声明了这些类为Spring的bean,并定义了它们之间的依赖关系。在运行时,Spring框架会自动创建这些对象并进行依赖注入,从而实现了对象之间的解耦合。
- 什么是控制反转
以下是一个简单的Java示例,演示了控制反转的概念和实现方式:
假设我们有一个接口MessageService和一个实现类EmailService:
public interface MessageService {
String getMessage();
}
public class EmailService implements MessageService {
@Override
public String getMessage() {
return "Email message";
}
}
在传统的编程模型中,我们可能会这样使用EmailService:
public class MyApp {
public static void main(String[] args) {
MessageService service = new EmailService();
System.out.println(service.getMessage());
}
}
在这个例子中,MyApp类直接负责创建EmailService对象,并调用其方法。
现在,让我们使用控制反转来改进这个例子。我们可以定义一个外部容器(在这里是Spring IOC容器),它负责创建和管理对象。我们只需在容器中声明依赖关系,容器会自动完成对象的创建和初始化。例如,在Spring框架中,我们可以这样配置:
@Configuration
public class AppConfig {
@Bean
public MessageService emailService() {
return new EmailService();
}
}
然后,在我们的应用程序中,我们只需从IOC容器中获取对象:
public class MyApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MessageService service = context.getBean(MessageService.class);
System.out.println(service.getMessage());
}
}
在这个例子中,MyApp类不再直接创建EmailService对象,而是通过IOC容器来获取对象。通过这种方式,控制权从应用程序代码中转移到了外部容器中,实现了控制反转。
其实概念非常复杂,通过代码演示我们很快就能了解其使用方法,对于晦涩难懂的概念,如果感兴趣可以看下概念解释
<,
,
>
全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。
- 什么是依赖注入
依赖注入: 获得依赖对象的过程被反转了。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。依赖注入是实现IOC的方法,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。
以下是一个简单的Java示例,演示了依赖注入的概念和实现方式:
假设我们有一个服务接口MessageService和一个实现类EmailService:
public interface MessageService {
String getMessage();
}
public class EmailService implements MessageService {
@Override
public String getMessage() {
return "Email message";
}
}
在传统的编程模型中,我们可能会这样使用EmailService:
public class MyApp {
private MessageService service;
public MyApp() {
this.service = new EmailService();
}
public void processMessage() {
System.out.println(service.getMessage());
}
public static void main(String[] args) {
MyApp app = new MyApp();
app.processMessage();
}
}
在这个例子中,MyApp类直接在构造函数中创建了EmailService对象,这种方式使得MyApp类与EmailService类之间产生了强耦合。
现在,让我们使用依赖注入来改进这个例子。我们可以通过构造函数、Setter方法或接口注入的方式将依赖对象传递给MyApp类。以下是通过构造函数注入的示例:
public class MyApp {
private MessageService service;
// 通过构造函数注入依赖对象
public MyApp(MessageService service) {
this.service = service;
}
public void processMessage() {
System.out.println(service.getMessage());
}
public static void main(String[] args) {
// 创建依赖对象
MessageService service = new EmailService();
// 将依赖对象传递给MyApp类
MyApp app = new MyApp(service);
app.processMessage();
}
}
在这个例子中,MyApp类不再直接创建EmailService对象,而是通过构造函数接收一个MessageService类型的参数,并使用这个参数作为依赖对象。这样,我们可以轻松地替换依赖对象,实现了依赖关系的松耦合。
总的来说,IOC容器的作用是将对象的创建、管理和依赖注入的责任从代码中解放出来,集中管理应用程序中的对象及其之间的依赖关系,从而提高了代码的可维护性、可扩展性和灵活性。