IOC
IoC,Inversion of Control,控制反转。控制反转是一种通过描述(在java中可以使XML或者注解)并通过第三方去获取特定对象的方式。简单地说就是将对象由虚拟机主动创建变为从IoC容器中获取,它是面向对象编程的一种思想,主要作用是降低开发难度、对模块解耦、有利于测试,
Spring IoC容器的设计
Spring IoC容器的设计主要是基于BeanFactory和ApplicationContext两个接口。BeanFactory是Spring IoC所定义的最底层的接口,ApplicationContext是其高级接口之一,是最常用的Spring IoC容器。Spring IoC容器设计图如下:
Spring IoC容器的初始化和依赖注入
1、Bean的定义和初始化
Bean的定义和初始化在Spring IoC中是两个步骤,它是先定义然后初始化和依赖注入的。Bean的定义分为3步:
Resource定位,这步是Spring IoC根据开发者的配置,进行资源定位。定位的内容由开发者提供。
BeanDefinition的载入,这个时候是将Resource定位的信息保存到Bean定义(BeanDefinition)中,此时不会创建Bean的实例。
BeanDefinition的注册,这个过程是将BeanDefinition的信息发布到Spring IoC容器中,此时仍没有对应的Bean的实例创建。
做完这三步,Bean就在Spring IoC容器中定义了,还没有初始化,更没有完成依赖注入。对于初始化和依赖注入,Spring Bean还有一个配置项——lazy-init,其含义是是否延迟初始化Spring Bean,默认值false,也就是默认自动初始化Bean,如果设置成true,那么Bean的初始化和依赖注入将在执行getBean方法时进行。
2、依赖注入
Spring实现IoC主要采用依赖注入,一般而言,依赖注入分为以下三种方式:
构造器注入:依赖于构造方法实现,构造方法可以使有参的和无参的。Spring内部采用反射的方式调用构造方法实现注入。XML方式对应c标签。
设值注入:设值注入依赖setter方法,是Spring最主流的注入方式。灵活且可读性高。其原理也是通过反射实现,设值注入依赖于无参构造器,当bean类声明了带参构造器时必须同时声明无参构造器。XML方式对应p标签。
接口注入:适用于来自于外界的资源,比如数据库连接资源可以在Tomcat下配置,然后通过JNDI的形式去获取它。此时可以使用借口注入。
IOC的简单实现
最简单的 IOC 容器只需4步即可实现,如下:
加载 xml 配置文件,遍历其中的标签
获取标签中的id,加载class属性对应的类,并创建 bean
遍历标签中的标签,获取属性值,并将属性值填充到 bean 中
将 bean 注册到 bean 容器中
要注入的Bean
public class Car {
private String name;
private String length;
private String width;
private String height;
private Wheel wheel;
// get set...
}
public class Wheel {
private String brand;
private String specification;
}
容器实现类:
public class SimpleIOC {
private Map<String, Object> beanMap = new HashMap<>();
public SimpleIOC(String location) throws Exception {
loadBeans(location);
}
public Object getBean(String name) {
Object bean = beanMap.get(name);
if (bean == null) {
throw new IllegalArgumentException("there is no bean with name " + name);
}
return bean;
}
private void loadBeans(String location) throws Exception {
// 加载 xml 配置文件
InputStream inputStream = new FileInputStream(location);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = factory.newDocumentBuilder();
Document doc = docBuilder.parse(inputStream);
Element root = doc.getDocumentElement();
NodeList nodes = root.getChildNodes();
// 遍历 <bean> 标签
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
String id = ele.getAttribute("id");
String className = ele.getAttribute("class");
// 加载 beanClass
Class beanClass = null;
try {
beanClass = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return;
}
// 创建 bean
Object bean = beanClass.newInstance();
// 遍历 <property> 标签
NodeList propertyNodes = ele.getElementsByTagName("property");
for (int j = 0; j < propertyNodes.getLength(); j++) {
Node propertyNode = propertyNodes.item(j);
if (propertyNode instanceof Element) {
Element propertyElement = (Element) propertyNode;
String name = propertyElement.getAttribute("name");
String value = propertyElement.getAttribute("value");
// 利用反射将 bean 相关字段访问权限设为可访问
Field declaredField = bean.getClass().getDeclaredField(name);
declaredField.setAccessible(true);
if (value != null && value.length() > 0) {
// 将属性值填充到相关字段中
declaredField.set(bean, value);
} else {
String ref = propertyElement.getAttribute("ref");
if (ref == null || ref.length() == 0) {
throw new IllegalArgumentException("ref config error");
}
// 将引用填充到相关字段中
declaredField.set(bean, getBean(ref));
}
// 将 bean 注册到 bean 容器中
registerBean(id, bean);
}
}
}
}
}
private void registerBean(String id, Object bean) {
beanMap.put(id, bean);
}
}
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="wheel" class="spring.ioc.Wheel">
<property name="brand" value="Michelin" />
<property name="specification" value="265/60 R18" />
</bean>
<bean id="car" class="spring.ioc.Car">
<property name="name" value="Mercedes Benz G 500" />
<property name="length" value="4717mm" />
<property name="width" value="1855mm" />
<property name="height" value="1949mm" />
<property name="wheel" ref="wheel" />
</bean>
</beans>
测试方法
public class SimpleIOCTest {
@Test
public void getBean() throws Exception {
System.out.println(SimpleIOC.class.getClassLoader().getResource(""));
String location = SimpleIOC.class.getClassLoader().getResource("ioc.xml").getFile();
SimpleIOC bf = new SimpleIOC(location);
Wheel wheel = (Wheel) bf.getBean("wheel");
System.out.println(wheel);
Car car = (Car) bf.getBean("car");
System.out.println(car);
}
}
参考
https://blog.csdn.net/weixin_41172473/article/details/81388198