目录
1.什么是IoC
2.IoC应用场景(案例分析)
2.1传统程序开发
2.2问题分析
2.3解决方案
2.4IoC 优势
3. DI概念
4.IoC详解
4.1Bean的存储
4.2@Controller(控制器存储)
4.3获取Bean
4.4Bean相关注解
1.什么是IoC
Spring是⼀个开源框架, 他让我们的开发更加简单、Spring 是包含了众多工具⽅法的 IoC 容器 、
IoC(Inversion of Control,控制反转)是Spring的核心思想,在传统的编程模式中,对象的创建和依赖关系的管理通常由程序自身来完成。而在使用了 IoC 思想的框架(如 Spring)中,这些对象的创建以及对象之间依赖关系的建立等控制权由框架来接管,而不是由应用程序代码直接控制
所谓的IOC称之为控制反转,简单来说就是将对象的创建的权力及对象生命周期的管理过程交由Spring框架来处理,从此在开发过程中不在需要关注对象的创建和生命周期的管理,而是在需要的时候由Spring框架提供,这个由Spring框架管理对象创建和生命周期的机制称之为控制反转。
2.IoC应用场景(案例分析)
2.1传统程序开发
需求:造出一辆能跑的车
先设计轮⼦(Tire) ,然后根据轮⼦的⼤⼩设计底盘(Bottom) ,接着根据底盘设计⻋⾝(Framework) ,最后根据⻋⾝设计好整个汽⻋(Car)。
这⾥就出现了⼀个"依赖"关系:汽车依赖车身,车身依赖底盘 ,底盘依赖轮⼦ .
最终程序的实现代码如下:
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 {public Tire() {System.out.println("tire init...size:"+size);}
}
2.2问题分析
这样的设计看起来没问题 ,但是可维护性却很低.接下来需求变更,我们需要加工多种尺寸的轮胎.
那这个时候就要对上⾯的程序进⾏修改了 ,修改后的代码如下所示:
修改之后, 其他调⽤程序也会报错, 我们需要继续修改
从以上代码可以看出以上程序的问题是: 程序的耦合度⾮常⾼,当最底层代码改动之后 ,整个调用链上的所有代码都需要修改.
2.3解决方案
我们尝试换⼀种思路, 我们先设计汽车的⼤概样子 ,然后根据汽车的样子来设计车身,根据车身来设计 底盘 ,最后根据底盘来设计轮子 . 这时候 ,依赖关系就倒置过来了:轮⼦依赖底盘 , 底盘依赖车身,车身依赖汽车
此时 ,我们只需要将原来由⾃⼰创建的下级类 ,改为传递的⽅式(也就是注入的⽅式), 因为我们不需要在当前类中创建下级类了 ,所以下级类即使发⽣变化(创建或减少参数), 当前类本⾝也⽆需修改任何代码 ,这样就完成了程序的解耦.
public class Car {private Framework framework;public Car(Framework framework) {this.framework = framework;System.out.println("car init...");}public void run(){System.out.println("car run...");}
}
public class Framework {private Bottom bottom;public Framework(Bottom bottom) {this.bottom = bottom;System.out.println("framework init....");}
}
public class Bottom {private Tire tire;public Bottom(Tire tire) {this.tire = tire;System.out.println("bottom init....");}
}
public class Tire {private int size;public Tire(int size) {this.size = size;this.color= color;System.out.println("tire init...size:"+size);}
}
代码经过以上调整 ,⽆论底层类如何变化 ,整个调⽤链是不⽤做任何改变的 ,这样就完成了代码之间的解耦 ,从而实现了更加灵活、通⽤的程序设计了。
2.4IoC 优势
在传统的代码中对象创建顺序是:Car -> Framework -> Bottom -> Tire
改进之后解耦的代码的对象创建顺序是:Tire -> Bottom -> Framework -> Car
通⽤程序的实现代码 ,类的创建顺序是反的 ,传统代码是 Car 控制并创建了Framework ,Framework 创建并创建了 Bottom ,依次往下 ,⽽改进之后的控制权发⽣的反转 ,不是使⽤⽅对象创建并控制依赖对象了,是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由当前类控制
这样的话, 即使依赖类发⽣任何改变 , 当前类都是不受影响的 ,这就是典型的控制反转 ,也就是 IoC 的实现思想。
IoC 的优点主要包括以下几点:
- 降低耦合度:使组件之间的依赖关系更加松散,便于系统的扩展和维护。
- 提高代码复用性:组件可以更方便地在不同场景中复用。
- 增强灵活性:可以更轻松地替换或修改依赖的对象,而不影响其他部分代码。
- 便于测试:可以方便地模拟和替换依赖对象,进行单元测试。
- 提升开发效率:开发者无需花费大量时间处理对象创建和依赖管理的繁琐细节。
- 更好的架构管理:有助于构建更清晰、合理的系统架构。
3. DI概念
IoC 是一种设计思想,而 DI(Dependency Injection,依赖注入)是实现 IoC 的一种重要方式。
DI 具体指的是将对象之间的依赖关系,通过一定的方式(如构造器注入、Setter 方法注入、字段注入等)在运行时动态地注入到对象中。
可以说 DI 是 IoC 思想在具体技术实现层面的体现,它们紧密相关,共同作用来实现对象创建和依赖管理的解耦与灵活配置。
创建Student类:
public class Student {private String name;public Student(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
创建一个Course类,并通过注解注入Student对象:
@Service
public class Course {@Autowiredprivate Student student;public void showDetails() {System.out.println("依赖注入学生姓名:" + student.getName());}
}
4.IoC详解
通过上⾯的案例, 我们已经知道了Spring IoC 和DI的基本操作, 接下来我们来系统的学习Spring IoC和DI 的操作.前⾯我们提到IoC控制反转 ,就是将对象的控制权交给Spring的IOC容器 , 由IOC容器创建及管理对象。也就是bean的存储.
4.1Bean的存储
要把某个对象交给IOC容器管理 ,需要在类上添加⼀个注解:@Component
而Spring框架为了更好的服务web应⽤程序, 提供了更丰富的注解.
共有两类注解类型可以实现:
类注解:@Controller、@Service、@Repository、@Component、@Configuration.
⽅法注解:@Bean.
4.2@Controller(控制器存储)
使用@Controller 存储 bean 的代码如下所示:
@Controller // 将对象存储到 Spring 中public class UserController {public void sayHi(){System.out.println("hi,UserController...");}}
如何观察这个对象已经存在Spring容器当中了呢? 接下来我们学习如何从Spring容器中获取对象
@SpringBootApplicationpublic class SpringIocDemoApplication { public static void main(String[] args) { //获取Spring上下⽂对象
ApplicationContext context = SpringApplication.run(TestSpringTwoApplication.class,args);//从Spring上下⽂中获取对象UserController userController = context.getBean(UserController.class);//使⽤对象userController.sayHello();}
}
}
观察运⾏结果, 发现成功从Spring中获取到Controller对象, 并执⾏Controller的sayHello⽅法
如果把@Controller删掉, 再观察运⾏结果
报错信息显⽰: 找不到类型是: com.example.demo.controller.UserController的bean
获取bean对象的其他⽅式:
上述代码是根据类型来查找对象, 如果Spring容器中, 同⼀个类型存在多个bean的话, 怎么获取呢
ApplicationContext 也提供了其他获取bean的⽅式, ApplicationContext 获取bean对象的功能, 是父类BeanFactory提供的功能.
// 1. 根据bean名称获取beanObject getBean(String var1) throws BeansException;// 2. 根据bean名称和类型获取bean<T> T getBean(String var1, Class<T> var2) throws BeansException;// 3. 按bean名称和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的beanObject getBean(String var1, Object... var2) throws BeansException;// 4. 根据类型获取bean<T> T getBean(Class<T> var1) throws BeansException;// 5. 按bean类型和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的bea<T> T getBean(Class<T> var1, Object... var2) throws BeansException;
常⽤的是上述1,2,4种, 这三种⽅式,获取到的bean是⼀样的,其中1,2种都涉及到根据名称来获取对象. bean的名称是什么呢?
Spring bean是Spring框架在运⾏时管理的对象, Spring会给管理的对象起⼀个名字. ⽐如学校管理学⽣ , 会给每个学⽣分配⼀个学号, 根据学号, 就可以找到对应的学⽣ .
Spring也是如此, 给每个对象起⼀个名字, 根据Bean的名称(BeanId)就可以获取到对应的对象.
4.3获取Bean
@SpringBootApplicationpublic class SpringIocDemoApplication { public static void main(String[] args) { //获取Spring上下⽂对象ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class,args); //从Spring上下⽂中获取对象//根据bean类型, 从Spring上下⽂中获取对象UserController userController1 = context.getBean(UserController.class); //根据bean名称, 从Spring上下⽂中获取对象UserController userController2 = (UserController) context.getBean("userController"); //根据bean类型+名称, 从Spring上下⽂中获取对象UserController userController3 = context.getBean("userController",UserController.class);System.out.println(userController1);System.out.println(userController2);System.out.println(userController3);
运⾏结果:
地址⼀样, 说明是同一个对象
继承关系和功能⽅⾯来说:
Spring 容器有两个顶级的接⼝: BeanFactory 和ApplicationContext。
其中 BeanFactory 提供了基础的访问容器的能力,
ApplicationContext 属于 BeanFactory 的⼦类 ,它除了继承了 BeanFactory 的所有功能之外, 它还拥有独特的特性 ,还添加了对国际化⽀持、资源访问⽀持、 以及事件传播等⽅⾯的⽀持
从性能⽅⾯来说:ApplicationContext 是⼀次性加载并初始化所有的 Bean 对象 ,⽽ BeanFactory 是需要那个才去加载那个 ,因此更加轻量. (空间换时间)
4.4Bean相关注解
在 Spring 中,有一些与 Bean 相关的重要注解,比如:
@Component:这是一个通用的组件注解,用于将类标识为可被 Spring 容器管理的 Bean。
@Service:通常用于标识服务层的 Bean,业务逻辑层, 处理具体的业务逻辑.
@Repository:一般用于标识数据访问层(如数据库操作相关)的 Bean。
@Controller:控制层, 接收请求, 对请求进⾏处理, 并进⾏响应.
@Configuration :配置层. 处理项⽬中的⼀些配置信息.
此外,还有@AutoWired用于自动注入依赖等注解,它们共同协作来实现对 Bean 的管理和配置
程序员看到类注解之后 ,就能直接了解当前类的用途.