Spring IOC&DI(上)

Spring IOC&DI(上)


1. Spring IOC&DI

Spring 是包含了众多工具方法的 IOC 容器

1.1 容器

概念 :容器时用来容纳物品的装置。
例子:List/Map -> 数据存储容器;Tomcat -> Web 容器

1.2 IOC

概念 :全称:Inversion of Control(控制反转),是 Spring 的核心思想,把对象交给 Spring 管理,就是 IOC 思想。

总的来说,Spring 就是一个"控制反转"的容器。


2. IOC&DI 介绍

2.1 IOC 介绍

如果我们造一辆车

2.1.1 传统开发

思路:先设计轮子,然后根据轮子设计底盘,再根据底盘设计车身,再根据车身设计一辆整车。

所产生的依赖关系:

代码实现:

车:

java 复制代码
public class Car {
    private Framework framework;

    public Car(int size) {
        this.framework = new Framework(size);
        System.out.println("car init...");
    }

    public void run() {
        System.out.println("car run...");
    }
}

车身:

java 复制代码
public class Framework {
    private Bottom bottom;

    public Framework(int size) {
        this.bottom = new Bottom(size);
        System.out.println("framework init...");
    }
}

底盘:

java 复制代码
public class Bottom {
    private Tire tire;

    public Bottom(int size) {
        this.tire = new Tire(size);
        System.out.println("bottom init...");
    }
}

轮胎:

java 复制代码
public class Tire {
    private int size;

    public Tire(int size) {
        this.size = size;
        System.out.println("tire init...,size:" + size);
    }
}

2.1.2 问题分析

代码可维护性低,假如我们想要对轮子的颜色也有要求,就需要对上层的程序也进行修改。


当底层代码改动后,整个调用链上的所有代码都需要修改。

2.1.3 IOC 程序开发

如果反正设计车辆,依赖关系就会倒置,轮子依赖底盘,底盘依赖车身,车身依赖汽车。

代码实现:

主程序:

java 复制代码
public class Main {
    public static void main(String[] args) {
        Tire tire = new Tire(21);
        Bottom bottom = new Bottom(tire);
        Framework framework = new Framework(bottom);
        Car car = new Car(framework);
        car.run();
    }
}

车:

java 复制代码
public class Car {
    private Framework framework;

    public Car(Framework framework) {
        this.framework = framework;
    }

    public void run() {
        System.out.println("car run...");
    }
}

车身:

java 复制代码
public class Framework {
    private Bottom bottom;

    public Framework(Bottom bottom) {
        this.bottom = bottom;
        System.out.println("framework init...");
    }
}

底盘:

java 复制代码
public class Bottom {
    private Tire tire;

    public Bottom(Tire tire) {
        this.tire = tire;
        System.out.println("bottom init...");
    }
}

轮胎:

java 复制代码
public class Tire {
    private int size;

    public Tire(int size) {
        this.size = size;
        System.out.println("tire init...,size:" + size);
    }
}

代码经过以上调整,无论底层类如何变化,整个调用链不用做任何变化,这样就完成了代码之间的解耦。

2.1.4 IOC 的优势

资源不由使用双方管理,而由不使用资源的第三方管理的优势在于资源集中管理,实现了资源的可配置和易管理,降低了使用双方的依赖程度。

2.2 DI 介绍

概念 :全称:Dependency Injection(依赖注入),容器在运行期间,动态的为应用程序提供运行时所依赖的资源,称之为依赖注入。

上述代码时通过构造函数的方式,把依赖对象注入到需要使用的对象中的

IOC是一种思想,DI 就属于具体的实现,DI 是 IOC 的一种实现。


3. IOC & DI 使用

Spring 是一个 IOC 容器,作为容器,那么它就具备两个最基础的功能:存和取,Spring 容器管理的主要是对象,这些对象称为"Bean",并且交由 Spring 管理,由 Spring 来负责创建和销毁。

我们利用图书管理系统代码来展示 IOC & DI 的使用

java 复制代码
@Repository
public class BookDao {
    public List<BookInfo> mockData() {
        List<BookInfo> bookInfos = new ArrayList<>();
        for(int i = 1;i <= 15;i++) {
            BookInfo bookInfo = new BookInfo();
            bookInfo.setId(i);
            bookInfo.setBookName("图书" + i);
            bookInfo.setBookAuthor("作者" + i);
            bookInfo.setCount(new Random().nextInt(100));
            bookInfo.setPrice(new BigDecimal(new Random().nextInt(100)));
            bookInfo.setPublish("出版社" + i);
            bookInfo.setStatus(i%5==0?2:1);
            bookInfos.add(bookInfo);
        }
        return bookInfos;
    }
}
java 复制代码
@Service
public class BookService {
    @Autowired
    private BookDao bookDao;

    public List<BookInfo> getList() {
        List<BookInfo> bookInfos = bookDao.mockData();
        for(BookInfo bookInfo : bookInfos) {
            if(bookInfo.getStatus() == 1) {
                bookInfo.setStatusCN("可借阅");
            } else {
                bookInfo.setStatusCN("不可借阅");
            }
        }
        return bookInfos;
    }
}
java 复制代码
@RestController
@RequestMapping("/book")
public class BookController {
    @Autowired
    private BookService bookService;

    @RequestMapping("/getList")
    public List<BookInfo> getList() {
        List<BookInfo> bookInfos = bookService.getList();
        return bookInfos;
    }
}

运行程序


4. IOC 详解

IOC 控制反转就是将对象的控制权交给 Spring 的 IOC 容器,由 IOC 容器创建及管理对象,就是 Bean 的存储。

4.1 Bean 的存储

把对象交给 IOC 容器管理,需要两类注解

(1)类注解:@Controller、@Service、@Repository、@Component、@Configuration

(2)方法注解:@Bean

4.1.1 @Controller

使用 @Controller 存储 bean 代码

java 复制代码
@Controller
public class UserController {
    public void sayHello() {
        System.out.println("hello");
    }
}

从 Spring 容器中获取对象

ConfigurableApplicationContext 类继承了 ApplicationContext 类,ApplicationContext 类 又继承了BeanFactory类,BeanFactory 类提供了大量获取 bean 的方法


java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {

	public static void main(String[] args) {
		ConfigurableApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		UserController userController = context.getBean(UserController.class);
		userController.sayHello();
	}
}

观察运行结果,发现成功从 Spring 中获取到 Controller 对象,并执行 Controller 的 sayHello 方法

如果把 @Controller 删掉,再观察运行结果

报错信息显示:找不到类型是:com.example.demo.controller.UserController 的 bean

获取 bean 对象的其他方式

ApplicationContext 也提供了其他获取 bean 的方式,ApplicationContext 获取 bean 对象的功能,是父类 BeanFactory 提供的功能

Bean 命名的约定

官方文档:https://docs.spring.io/spring-framework/reference/core/beans/definition.html#beans-beanname

命名约定使用 Java 标准约定作为实例字段名,bean 名称以小写字母开头,然后使用驼峰式大小写。当有多个字符并且第一个和第二个字符都是大写时,将保留原始的大小写。

根据这个命名规则,我们来获取 bean

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
	public static void main(String[] args) {
		ConfigurableApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		UserController userController1 = context.getBean(UserController.class);
		UserController userController2 = (UserController) context.getBean("userController");
		UserController userController3 = context.getBean("userController", UserController.class);
		System.out.println(userController1);
		System.out.println(userController2);
		System.out.println(userController3);
	}
}

运行结果

4.1.2 @Service

使用 @Service 存储 bean 的代码

java 复制代码
@Service
public class UserService {
    public void sayHi() {
        System.out.println("Hi");
    }
}

读取 bean 代码

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
	public static void main(String[] args) {
		ConfigurableApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		UserService userService = context.getBean(UserService.class);
		userService.sayHi();
	}
}

观察结果,发现成功从 Spring 中获取到 UserService 对象,并执行了 sayHi 方法

4.1.3 @Repository

使用 @Repository 存储 bean 的代码

java 复制代码
@Repository
public class UserRepository {
    public void sayHi() {
        System.out.println("Hi");
    }
}

读取 bean 代码

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
	public static void main(String[] args) {
		ConfigurableApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		UserRepository userRepository = context.getBean(UserRepository.class);
		userRepository.sayHi();
	}
}

运行结果

4.1.4 @Component

使用 @Component 存储 bean

java 复制代码
@Component
public class UserComponent {
    public void sayHi() {
        System.out.println("Hi");
    }
}

读取 bean 代码

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
	public static void main(String[] args) {
		ConfigurableApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		UserComponent userComponent = context.getBean(UserComponent.class);
		userComponent.sayHi();
	}
}

运行结果:

4.1.5 @Configuration

使用 @Configuration 存储 bean 的代码

java 复制代码
@Configuration
public class UserConfiguration {
    public void sayHi() {
        System.out.println("Hi");
    }
}

读取 bean 代码

java 复制代码
@SpringBootApplication
public class SpringIocDemoApplication {
	public static void main(String[] args) {
		ConfigurableApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		UserConfiguration userConfiguration = context.getBean(UserConfiguration.class);
		userConfiguration.sayHi();
	}
}

运行结果

4.2 类注解的关系

@Controller:控制层,接收请求,对请求进行处理,并进行响应。

@Service:业务逻辑层,处理具体的业务逻辑。

@Repository:数据访问层,负责数据访问操作。

@Configuration:配置层,处理项目中的一些配置信息。



查看@Controller、@Service、@Repository、@Configuration 等注解的源代码发现,这些注释里面都有一个 @Component 注解,说明他们本身都属于 @Component 的子类,@Controller、@Service、@Repository、@Configuration 被称为衍生注解。

相关推荐
cyforkk4 小时前
10、Java 基础硬核复习:多线程(并发核心)的核心逻辑与面试考点
java·开发语言·面试
wstcl4 小时前
像asp.net core webapi一样在asp.net frameworks中使用Swagger,方便调试接口
后端·asp.net·swagger·webapi
Remember_9934 小时前
Java 工厂方法模式:解耦对象创建的优雅方案
java·开发语言·python·算法·工厂方法模式
小楼v4 小时前
使用Nacos实现动态IP黑名单过滤
java·后端·微服务·nacos
A懿轩A4 小时前
【Java 基础编程】Java 运算符完全指南:算术/关系/逻辑/位运算与优先级,避免常见坑
java·开发语言
Anastasiozzzz4 小时前
Nginx和Ribbon的区别
后端·spring cloud·ribbon
时艰.4 小时前
Java 并发编程核心知识点
java·开发语言
雾削木4 小时前
使用 ESPHome 的核心指令
java·前端·javascript·单片机·嵌入式硬件
Dylan的码园5 小时前
深入浅出Java排序:从基础算法到实战优化(下)
java·算法·排序算法