Spring 是包含众多工具方法的 IoC 容器.
容器就是存储用的, 比如像以前学的 List / Map 就是存储数据的容器, 再像 Tomcat 就是 Web 容器.
容器最核心就是两点 : 存储和获取.
那 IoC 容器又是啥意思呢 ?
IoC = Inversion of Control 就是 “控制(权)反转” 的意思.
理解 :
以往我们获取对象都是通过 new 关键字来创建对象并获取的, 这样通过自己写代码控制类对象的生命周期, 我们是掌握对象控制权的, 而控制权反转就是指控制权不交由程序员控制了, 而是由 Spring(Spring容器 / IoC容器) 来控制.
IoC 容器就是存放 bean(对象) 的容器, 我们如果需要就去容器获取, 不需要自己 new 对象了.
这样有什么好处呢 ?
对代码进行解耦, 即使底层类发生改变, 整个调用链都不需要改动, 使程序设计更加通用, 灵活.
举个例字 :
我有两个类 :
public class B {
private int b = 25;
public B() {
System.out.println("b : " + b);
}
}
public class A {
public A() {
B b = new B(); // A 类依赖 B 类
}
}
测试类 :
public class Test {
public static void main(String[] args) {
A a = new A(); //传统是直接 new 对象
}
}
如果我要对 B 类的 b 进行修改, 那只能修改底层的 B 类代码 :
public class B {
private int b = 25;
public B(int m) {
b = m;
System.out.println("b : " + b);
}
}
B 的构造方法变了, A 类的构造方法也要变 :
public class A {
public A(int m) {
B b = new B(m);
}
}
测试类 :
public class Test {
public static void main(String[] args) {
A a = new A(66);
}
}
我们只是改动底层代码一点点, 就牵一发而动全身了, 这显然很麻烦, 它们的耦合太强了, IoC 容器就解决了这一点, 让程序员修改底层代码而不动整个调用链.
Spring 是⼀个 IoC 容器,对象的创建和销毁都交给 Spring 来管理了,它本身⼜具备了存储对象和获取对象的能⼒。
DI 是 Dependency Injection 的缩写, 翻译成中⽂是“依赖注⼊”的意思.
所谓依赖注⼊, 就是由 IoC 容器在运⾏期间, 动态地将某种依赖关系注⼊到对象之中.
DI 与 IoC 区别:
DI 和 IoC 是从不同角度描述同一件事情,IoC 是一种思想,它就是要实现对像解耦,不管用什么样的方法;DI 则是一种具体手段,来实现对象解耦.
在项⽬的 pom.xml 中添加 Spring 框架的⽀持,xml 配置如下 :
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
</dependencies>
添加的框架有 spring-context:spring 上下⽂,还有 spring-beans:管理对象的模块。
注意 : 这里只是声明要这些依赖, 以及依赖所在地, 并不是添加完上述代码就添加完了依赖, 还得刷新一下, 让 idea 去下载这些依赖.
最后在创建好的项⽬ java ⽂件夹下创建⼀个启动类,包含 main ⽅法即可:
public class App {
public static void main(String[] args) {
}
}
想存储 bean 对象, 首先得有 bean 才行, 因此首先得创建一个 bean, 然后将 bean 注册到 Spring 容器中.
bean 就是 Java 中 一个普通对象, 创建 bean 就是创建对象.
在创建好的项⽬中添加 Spring 配置⽂件 spring-config.xml,将此⽂件放到 resources 的根⽬录下,
如下图所示:
在 spring-config.xml 中添加 Spring 配置⽂件 :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:content="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
</beans>
再将 Student 对象注册到 Spring 中就可以,具体操作如下:
注意 :
class 是对应你要注册的类的路径, 当我们获取 bean 的时候就会去这个路径中扫描, 如果扫描不到就会报错;
id 这里是可以随便取的, 只是给这个路径中注册的 bean 对象取个名字, 方便后续获取.
如果存在多个 bean 需注册, 则添加多个 < bean > 标签
获取并使⽤ Bean 对象,分为以下 3 步:
public class App {
public static void main(String[] args) {
//1. 获取 spring 上下文对象, 注意后面需填写添加配置的文件名称
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//2. 获取 bean 对象, 后面的字符串要与我们之前取的名字相匹配,
// 获取到的 bean 是 object 类型的, 需强制类型转换
Student student = (Student)context.getBean("student");
//检验对象是否获取到
System.out.println(student.sayHi("佩奇"));
}
}
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//这里是直接通过类类型来获取 bean 对象, 所以返回的值不需要转换
Student student = context.getBean(Student.class);
//Student student = (Student)context.getBean("student");
System.out.println(student.sayHi("佩奇"));
}
}
当我们 Student 仅有一个 bean 对象时, 这样直接通过类名来获取 bean 对象显然没问题, 但若 Student 存在多个 bean 对象呢 ? 它会获取到哪个 bean 对象呢 ?
这里我们载注册一个 Student 类的 bean 对象, 并取名为 student2.
来看看获取效果 :
报错信息是 : 找到两个匹配结果, 但它只期望有一个结果.
如果是用 id 来获取就不会出现这种情况了, 因为每个 bean 都有唯一的 id.
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//通过 id 获取 bean, 并将所得 bean 转为 Student 类型, 无需类型转换了
Student student = context.getBean("student", Student.class);
//Student student = context.getBean(Student.class);
//Student student = (Student)context.getBean("student");
System.out.println(student.sayHi("佩奇"));
}
}
其实 BeanFactory 也可以获取 Bean 对象.
public class App {
public static void main(String[] args) {
BeanFactory context = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
//ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//通过 id 获取 bean, 并将所得 bean 转为 Student 类型, 无需类型转换了
Student student = context.getBean("student", Student.class);
//Student student = context.getBean(Student.class);
//Student student = (Student)context.getBean("student");
System.out.println(student.sayHi("佩奇"));
}
}
ApplicationContext 和 BeanFactory 效果是⼀样的, 那它们之间的区别是什么呢 ?
区别 :
- ApplicationContext 是 BeanFactory 的子类, ApplicationContext 不仅拥有父类的所有方法, 还扩展了很多父类没有的方法. (国际化支持、资源访问支持、事件传播支持等)
- ApplicationContext 是⼀次性加载并初始化所有的 Bean 对象, ⽽ BeanFactory 是懒加载(需要哪个才去加载哪个), 因此更加轻量.