Spring IOC
什么是IOC
IOC的全称叫Inversion Of Control (控制反转),是一种对象注入技术,用于实现组件之间的解耦和依赖关系的管理。
在传统的面向对象编程中,对象之间通常是通过直接创建和调用其他对象来实现相互依赖的。这种依赖关系是编码时静态确定的,因此对于对象之间的复杂依赖关系的管理往往比较困难。而IoC则将对象之间的依赖关系交给Spring容器来管理,在运行时动态地注入对象之间的依赖关系,从而实现对象之间的松耦合和灵活性。
在Spring中,IoC是通过IOC容器实现的。IoC容器负责实例化、配置、组装和管理Spring应用程序中的对象。它使用依赖注入(Dependency Injection,DI)将对象之间的依赖关系动态地注入到相关的对象中,从而实现对象之间的低耦合度。这样,对象之间的依赖关系将由容器在运行时来管理,而不是由开发者在编写代码时静态地确定。
在传统的程序开发中,当需要调用对象时,通常由调用者来创建被调用者的实例,即对象室友调用者主动new出来的。
但在Spring框架中创建对象的工作不在由调用者来完成,而是交给IOC容器来创建,再推送给调用者,整个流程完成反转,因此是控制反转
核心思想
把对象的管理权限交给容器,应用程序如果需要使用某个对象的实例那么直接从IOC容器里获取就可以了
好处在于降低了对象与对象之间的耦合性,使得程序的整个体系结构变得更加灵活
底层原理
工厂模式加反射机制
-
工厂模式:通过工厂模式实现对象的创建和管理
-
反射机制:通过反射机制实现对象的动态创建和初始化
- 反射与new的区别
- 反射和new都是创建对象实例的,但是new对象无法调用该类里面私有private的属性,而反射可以调用类中private的属性!
- new属于静态编译。就是在编译的时候把所有的模块都确定,如果有添加或删除某些功能,需要重新编译。但系统不可能一次就把把它设计得很完美,当发现需要更新某些功能时,采用静态编译的话,需要把整个程序重新编译一次才可以实现功能的更新。也就是说,用户需要把以前的软件卸载了,再重新安装才会重新编译!这样的系统耦合严重,难以扩展!
- 反射属于动态编译。在运行时确定类型并创建对象,通过反射指定模板,动态的向模板中传入要实例化的对象。动态编译最大限度发挥了Java的灵活性,体现了多态的应用,有以降低类之间的藕合性。其中spring中ioc的核心就是利用了反射解耦合。
- 反射与new的区别
-
通过工厂加new创建对象
public interface Product {
void printInfo();
}
public class ProductA implements Product {
@Override
public void printInfo() {
System.out.println("This is Product A");
}
}
public class ProductB implements Product {
@Override
public void printInfo() {
System.out.println("This is Product B");
}
}
public class ProductFactory {
public static Product createProduct(String type) {
if (type.equals("A")) {
return new ProductA();
} else if (type.equals("B")) {
return new ProductB();
} else {
throw new IllegalArgumentException("Invalid product type");
}
}
}
public class Main {
public static void main(String[] args) {
String productType = "A";
// 使用工厂类创建产品对象
Product product = ProductFactory.createProduct(productType);
// 调用产品对象的方法
product.printInfo();
}
}
- 通过工厂加反射来创建对象
public interface Product {
void printInfo();
}
public class ProductA implements Product {
@Override
public void printInfo() {
System.out.println("This is Product A");
}
}
public class ProductB implements Product {
@Override
public void printInfo() {
System.out.println("This is Product B");
}
}
public class ProductFactory {
public static Product createProduct(String className) {
try {
Class<?> clazz = Class.forName(className);
Object object = clazz.newInstance();
if (object instanceof Product) {
return (Product) object;
} else {
throw new IllegalArgumentException("Invalid product type");
}
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
throw new IllegalArgumentException("Invalid product type", e);
}
}
}
public class Main {
public static void main(String[] args) {
String productClassName = "com.example.ProductA";
// 使用工厂类和反射机制创建产品对象
Product product = ProductFactory.createProduct(productClassName);
// 调用产品对象的方法
product.printInfo();
}
}
Bean
声明Bean的方式
- 在xml配置文件里面通过的标签,
- 或者通过@Service,@Repository,@Controller等注解
- @Configuration配置类里通过@Bean注解去声明
spring在启动的时候会去解析这些bean然后保存的IOC容器里面
实例化Bean的方式
-
无参构造方法实例化(默认)
-
工厂静态方法实例化
(1)创建手机工厂类PhoneFactory,创建静态方法获取手机对象getInstance,代码如下:
public class PhoneFactory { //静态方法创建手机 public static Phone getInstance(){ System.out.println("手机工厂调用静态方法直接创建···"); return new HuaWeiPhone(); } }
(2)在applicationContext.xml中创建bean,标签中class属性为手机工厂类,添加factory-method属性,并设置属性值为手机工厂类中的静态方法getInstance。
-
工厂动态方法实例化
(1)手机工厂类PhoneFactory,创建通过动态方法获取手机对象getPhone,代码如下:
public class PhoneFactory { //动态方法创建手机 public Phone getPhone(){ System.out.println("手机工厂对象调用动态方法创建···"); return new HuaWeiPhone(); } }
(2)修改applicationContext.xml中配置PhoneFactory的bean标签,再创建Phone的bean标签,指定工厂方法实例和动态工厂方法。
Bean的依赖注入方式
- 构造方法注入
- set方法注入(命名空间注入本质也是set注入)
Bean依赖注入的数据类型
- 普通数据类型
- 引用数据类型
- 集合数据类型
工作流程
两个阶段
-
IOC容器的初始化阶段,主要根据程序里面定义的xml文件或者注解等Bean的声明方式,通过解析和加载后生成BeanDeginition,然后把BeanDefinition注册到IOC容器里面,通过注解或者XML声明的Bean都会解析得到一个BeanDefinition实体,这个实体里面会包含bean的一些定义和基本的一些属性,最后把这个BeanDefinition保存到一个Map集合里面从而去完成IOC的一个初始化,IOC容器的作用就是对这些注册的Bean的定义信息进行处理和维护,它是IOC容器控制反转的一个核心
-
完成Bean的初始化和依赖注入
- 通过反射去针对没有设置lazy-init属性的单例Bean进行初始化
- 完成Bean的依赖注入
Bean的使用
- 通常通过@Autowired这样一个注解或者通过BeanFactory.getBean(),从IOC容器里取获取一个指定Bean的一个实例,另外针对设置了lazy-init属性以及非单例bean的一个实例化是在每一次获取Bean对象的时候,调用Bean的初始化方法来完成实例化的,并且springIOC容器不会去管理这些Bean