控制反转(IoC)是什么?
文章目录
- 二、IoC是什么?
- 三、具体程序开发举例
- 四、控制反转(IoC)有什么用?
- 五、Bean的存储
- getBean
- getBean传参注意
- 小驼峰
- 两个连续大写字母开头的类名
- 1.1@Controller(控制器存储)
- 1.2@Service(服务存储)
- 1.3@Repository(仓库存储)
- 1.4@Component(组件存储)
- 1.5@Configuration(配置存储)
- 五种类注解的联系
- 方法注解@Bean
- 当有多个构造方法时,需要使用类名来获取bean对象。
- 当构造方法有参数时,需要创建参数对应类型的方法
- 在这里插入图片描述
学习Spring框架的小伙伴肯定都知道Spring框架的核心是IoC容器和AOP模块。
也都听说过
Spring是包含众多工具方法的IoC容器。
IoC容器是Spring框架的底层基础,负责管理对象的创建和对象依赖关系的维护
IoC是一种思想,而DI就是实现这个思想的一种方法。
那么本文就带大家来了解什么是IoC
二、IoC是什么?
Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。
在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制,对于spring框架来说,就是由Spring来负责控制对象的生命周期和对象间的关系。
在开发中的表现就是:
当需要一个对象时,不再自己通过new来创建对象,而是把创建对象的任务交给容器,在程序中只需要依赖
注入(DI)。这个容器就成为IoC容器。
三、具体程序开发举例
现在有这样的需求:造一辆车
我们根据需求来梳理思路:
我们先设计轮子(Tire),然后根据轮子的大小来设计地盘(Bottom),然后根据底盘设计车身(Framework),然后再由车身设计出整个汽车。
这里就出现了一个依赖关系:
汽车依赖车身,车身依赖底盘,底盘依赖轮子
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.run();
}
}
public class Car {
private Framework framework;
public Car() {
framework = new Framework();
System.out.println("car init ....");
}
public void run(){
System.out.println("car run ....");
}
}
public class Framework {
private Bottom bottom;
public Framework() {
bottom = new Bottom();
System.out.println("framework init....");
}
}
public class Bottom {
private Tire tire;
public Bottom() {
tire = new Tire();
System.out.println("bottom init....");
}
}
public class Tire {
private int size = 17;
public Tire( ){
System.out.println("Tire init....");
}
}
运行程序
当车轮的尺寸需要随着不同的需求而变更时,那么底盘,车身,汽车都要受到波动。
这样的设计,导致耦合太高,当一部分的代码需求变更时,会影响其他很多代码,增加了改动的成本
那么如何解决呢?
我们尝试换⼀种思路, 我们先设计汽⻋的⼤概样⼦,然后根据汽⻋的样⼦来设计⻋⾝,根据⻋⾝来设计
底盘,最后根据底盘来设计轮⼦. 这时候,依赖关系就倒置过来了:轮⼦依赖底盘, 底盘依赖⻋⾝,
⻋⾝依赖汽⻋
我们可以尝试不在每个类中⾃⼰创建下级类,如果⾃⼰创建下级类就会出现当下级类发⽣改变操作,
⾃⼰也要跟着修改.
此时,我们只需要将原来由⾃⼰创建的下级类,改为传递的⽅式(也就是注⼊的⽅式),因为我们不
需要在当前类中创建下级类了,所以下级类即使发⽣变化(创建或减少参数),当前类本⾝也⽆需修
改任何代码,这样就完成了程序的解耦.
public class Main {
public static void main(String[] args) {
Tire tire = new Tire(18,"red");
Bottom bottom = new Bottom(tire);
Framework framework = new Framework(bottom);
Car car = new Car(framework);
car.run();
}
}
这样做以后,我们传入的都是对象,当一个对象属性发生改变时,不会影响其他部分的正常运行。
达到解耦合的作用
我们发现了⼀个规律,通⽤程序的实现代码,类的创建顺序是反的,传统代码是 Car 控制并创建了Framework,Framework 创建并创建了 Bottom,依次往下,⽽改进之后的控制权发生的反转,不再是使⽤⽅对象创建并控制依赖对象了,⽽是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由当前类控制了.
这样的话, 即使依赖类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC 的实现思想。
四、控制反转(IoC)有什么用?
- 资源集中管理: IoC容器会帮我们管理⼀些资源(对象等), 我们需要使⽤时, 只需要从IoC容器中去取就可以了
- 解耦合:我们在创建实例的时候不需要了解其中的细节, 降低了使⽤资源双⽅的依赖程度
看到这是不是还有点懵,还没真正的搞懂什么是IoC,那么接下来就会使用来让你真正的认识IoC。
五、Bean的存储
在这一部分,会真正的使用IoC,来对IoC有一个深刻且全面的认识。
首先我们先搞清楚概念Bean是什么东西?
根据Bing搜索:
简而言之Bean就是容器内的对象。
这里的注解就是告诉Spring这里的内容已经放置到容器内了
要将创建对象的控制权交给Spring,就要事先声明给Spring哪些是需要进行控制的。
那么实现方式有两种
- 类注解:
@Controller、@Service、@Repository、@Component、@Configuration
- 方法注解:
@Bean
getBean
ApplicationContext context = SpringApplication.run(IoCApplication.class, args);
这是启动类的代码,这里返回的ApplicationContext
就是Spring的运行环境。
这里的context就相当于是一个容器了,那么要如何从容器中拿到对象呢?
这里就要使用getBean
这个方法了。
getBean方法括号内常用的传参方式有三种
- 根据bean的名称取
- 根据bean的类型取
- 根据bean的名称和类型取
举例如下
这是在后面要讲解的@Controller
注解
@Controller
public class Usercontroller {
public void DoController() {
System.out.println("Do Controller....");
}
}
@SpringBootApplication
public class IoCApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(IoCApplication.class, args);
Usercontroller bean = context.getBean(Usercontroller.class);
bean.DoController();
}
}
getBean传参注意
这里要注意,使用名称来获取时,使用小驼峰的命名规范。
当类名前两个字母是连续大写时,就不需要变成小驼峰。
如果需要调用方法,这里需要转型
小驼峰
这有两个注意点:
- 小驼峰
类的方法名是UserController
而我们使用getBean
的方法名来获取bean对象的时候,就需要将类名转换为小驼峰的形式,也就是userController
2. 类型转换
在使用类名获取bean对象时,我们还要进行类型的转换!
两个连续大写字母开头的类名
这时就不需要变成小驼峰的形式。
这是bean的名称的内部实现方式的源码
1.1@Controller(控制器存储)
@Controller
public class Usercontroller {
public void DoController() {
System.out.println("Do Controller....");
}
}
@SpringBootApplication
public class IoCApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(IoCApplication.class, args);
Usercontroller bean = context.getBean(Usercontroller.class);
bean.DoController();
}
}
在平常,我们调用对象的方法,首先需要先创建对象,然后再调用对应的方法。
现在我们
1.2@Service(服务存储)
@Service
public class UserService {
public void DoService(){
System.out.println("do service...");
}
}
@SpringBootApplication
public class IoCApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(IoCApplication.class, args);
//从context中获取对象
// Usercontroller bean = context.getBean(Usercontroller.class);
// bean.DoController();
UserService bean1 = context.getBean(UserService.class);
bean1.DoService();
}
}
getBean方法括号内常用的传参方式有三种
通过类型来获取
通过名称来获取
通过类型和名称一同获取
这里要注意,使用名称来获取时,使用小驼峰的命名规范。
当类名前两个字母是连续大写时,就不需要变成小驼峰。
如果需要调用方法,这里需要转型
1.3@Repository(仓库存储)
@Repository
public class UserRepository {
public void DoRepository() {
System.out.println("Do Repository...");
}
}
@SpringBootApplication
public class IoCApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(IoCApplication.class, args);
UserRepository bean2 = context.getBean(UserRepository.class);
bean2.DoRepository();
}
}
1.4@Component(组件存储)
@Component
public class UserComponent {
public void DoComponent(){
System.out.println("Do Component");
}
}
@SpringBootApplication
public class IoCApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(IoCApplication.class, args);
UserComponent bean3 = context.getBean(UserComponent.class);
bean3.DoComponent();
}
}
1.5@Configuration(配置存储)
@Configuration
public class UserConfiguration {
public void DoConfiguration() {
System.out.println("UserConfiguration");
}
}
@SpringBootApplication
public class IoCApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(IoCApplication.class, args);
UserConfiguration bean4 = context.getBean(UserConfiguration.class);
bean4.DoConfiguration();
}
}
五种类注解的联系
查看@Controller / @Service / @Repository / @Configuration
的源码发现,这些注解都包含一个@Component
的注解,说明四个注解都是@Component
的子类。
方法注解@Bean
@Bean
必须搭配类注解才能使用
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(IoCApplication.class, args);
UserInfo bean = context.getBean(UserInfo.class);
System.out.println(bean);
}
@Data
public class UserInfo {
private Integer id;
private String name;
private Integer age;
}
public class BeanConfig {
@Bean
public UserInfo getUserInfo() {
UserInfo userInfo = new UserInfo();
userInfo.setAge(18);
userInfo.setId(11);
userInfo.setName("张三");
return userInfo;
}
}
我们运行,出现如下报错,这是因为如果我们要使用@Bean
时, 必须要在类前,也就是在BeanConfig
这部分代码中,提前声明告诉Spring这里面有需要管理的对象。
此时在BeanConfig
中加入@Configuration
注解,就运行成功了。
当有多个构造方法时,需要使用类名来获取bean对象。
如上代码,有两个构造方法,此时再用类型传参就会报错
这时就需要使用类名来传参
UserInfo userInfo = (UserInfo)context.getBean("getUserInfo");
System.out.println(userInfo);
这时就能获取到UserInfo对象了。
当构造方法有参数时,需要创建参数对应类型的方法
这时就需要有参数的方法getUserInfo
时,就需要再使用@Bean
来创建一个String类型的name
对象。
以上就是本文所有内容,如果对你有帮助的话,点赞收藏支持一下吧!💞💞💞