什麼是springIoc
IOC(Inversion of Control)
意爲控制反轉,他是一種設計思想.並非實際的技術.最核心的思想就是將對象實例創建的控制權交給程序(IOC 容器)
IOC 容器:
一個管理所有控制反轉過程中創建的對象的 key-value 容器結構(可以簡單理解爲:hashMap)
Spring 的 IOC(控制反轉)中對象實例構建的方式有哪些?
1.無參構造創建
2.靜態工廠創建:類的靜態方法構造
3.實例工廠創建:對象的實例方法構造
演示代碼:
spring-ioc.xml 配置文件內容:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 創建方式1:空參構造創建 -->
<bean id="a" class="com.czy.project.demo6.A"/>
<!--
創建方式2:靜態工廠創建
調用A的createBObj方法來創建名爲b的對象放入容器
-->
<bean id="b" class="com.czy.project.demo6.A" factory-method="createBObj"/>
<!--
創建方式3:實例工廠創建
調用實例a的createCObj方法來創建名爲c的對象放入容器
-->
<bean id="c" factory-bean="a" factory-method="createCObj"/>
</beans>
Class A 代碼:
public class A {
public A() {
System.out.println("A 無參構造器被調用了.");
}
public static B createBObj() {
System.out.println("A 的靜態方法 createBObj 被調用了.");
return new B();
}
public C createCObj() {
System.out.println("A 的實例方法 createCObj 被調用了.");
return new C();
}
}
Class B 代碼:
public class B {
}
Class C 代碼:
public class C {
}
啓動類的代碼:
public class Main {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-ioc.xml");
A a = (A) context.getBean("a");
B b = (B) context.getBean("b");
C c = (C) context.getBean("c");
}
}
輸出結果:
通過上面的案例,我們已經知道了 IOC 的概念及實例構建的方式.接下來我們來探討一下他的核心實現方式.
手寫springIoc實現思路
說明:以下代碼需要的xml配置文件以及實體類還是用上邊的
spring中是要在xml中配置bean的,都知道面向對象的思想,那你肯定能想到肯定是要把xml文件中的內容讀取出來,並用對象接收。那麼
1.創建用於接收xml中bean的配置類
/**
* 用於接收xml中bean標籤的屬性
* 沒什麼東西,就是根據bean標籤中的屬性來定義的
* Author : czy
*/
public class BeanConfig {
private String id;
private String clazz;
private String factoryMethod;
private String factoryBean;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
public String getFactoryMethod() {
return factoryMethod;
}
public void setFactoryMethod(String factoryMethod) {
this.factoryMethod = factoryMethod;
}
public String getFactoryBean() {
return factoryBean;
}
public void setFactoryBean(String factoryBean) {
this.factoryBean = factoryBean;
}
}
spring中入口是不是new ClassPathXmlApplicationContext(“xml文件名”);
那咱們就用XmlApplicationContext(“xml文件名”); //Are You Ok?–>ok
2.創建XmlApplicationContext類
/**
* 相當於Springl中ClassPathXmlApplicationContext
* 1、解析xml文件
* 2、獲取xml文件所有的節點內容
* 3、利用java反射機制創建實例對象
* 4、將實例對象添加到ioc容器
* czy say :SO EASY
*/
public class XmlApplicationContext {
// xml路徑
private String xmlPath;
// 存放bean(也就是ioc的容器)
private static HashMap map = new HashMap();
//存放從xml中解析出來的所有bean
private List<BeanConfig> beanConfigs = new ArrayList<BeanConfig>();
//對外的構造函數
public XmlApplicationContext(String xmlPath) {
this.xmlPath = xmlPath;
try {
//解析xml到beanConfigs
getAllElements();
//初始化ioc容器
init();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 讀取xml文件所有的節點內容,並放入beanConfigs集合
*/
private void getAllElements() throws Exception {
//1.指定路徑加載xml到jvm,形成輸入流,xml中的所有內容都在這個流中
InputStream is = XmlApplicationContext.class.getClassLoader().getResourceAsStream(xmlPath);
//2.創建解析對象
SAXReader reader = new SAXReader();
//3.獲得文檔對象(整個xml文件->將輸入流轉換成文檔對象)
Document document = reader.read(is);
//4.獲取根節點,也就是對應spring-ioc.xml中的樹幹beans
Element rootElement = document.getRootElement();
//5.獲取元素迭代器,用於獲取所有節點內容
Iterator iterator = rootElement.elementIterator();
while (iterator.hasNext()) {
//用於接收xml中bean的屬性值
BeanConfig beanConfig = new BeanConfig();
//獲取beans的子節點bean
Element bean = (Element) iterator.next();
// 獲取bean的屬性迭代器
Iterator beanAttributes = bean.attributeIterator();
while (beanAttributes.hasNext()) {
Attribute attribute = (Attribute) beanAttributes.next();
String attributeName = attribute.getName();//屬性名
String attributeData = (String) attribute.getData();//屬性值
if ("id".equals(attributeName)) {
beanConfig.setId(attributeData);
} else if ("class".equals(attributeName)) {
beanConfig.setClazz(attributeData);
} else if ("factory-method".equals(attributeName)) {
beanConfig.setFactoryMethod(attributeData);
} else {
beanConfig.setFactoryBean(attributeData);
}
}
//將這條bean的屬性值放入beanConfigs集合
beanConfigs.add(beanConfig);
}
}
/**
* 這裏初始化ioc容器是關鍵
* 這裏就要利用java的反射機制來創建bean實例
*
* @throws Exception
*/
private void init() throws Exception {
/**
* 這裏說一點:
* 看spring-ioc.xml中:bean標籤有class屬性時就沒有factory-bean屬性,反之
* 有class屬性時,就要判斷是空參構造創建還是靜態工廠創建,也就是判斷有沒有factory-method屬性
* 有factory-bean時,必然是實例工廠創建-必須有factory-method
* 這點要捋清楚
*/
for (BeanConfig bean : beanConfigs) {
if (null != bean.getClazz()) {
//通過全限定類名拿到Class實例clazz
Class clazz = Class.forName(bean.getClazz());
if (null != bean.getFactoryMethod()) {//靜態工廠創建bean實例
//方法名獲取method對象
Method method = clazz.getDeclaredMethod(bean.getFactoryMethod());
putBean(bean.getId(), method.invoke(null));
} else {//構造器創建bean實例
putBean(bean.getId(), clazz.newInstance());
}
} else if (null != bean.getFactoryBean()) {//實例工廠創建bean實例
//從容器中拿到實體bean
Object obj = getBean(bean.getFactoryBean());
Method method = obj.getClass().getDeclaredMethod(bean.getFactoryMethod());
putBean(bean.getId(), method.invoke(obj));
} else {
System.out.println("czy不知道在搞什麼鬼");
}
}
}
/**
* 添加實例對象到ioc容器
*
* @param id:就是bean中自定義的id
* @param object :就是反射機制創建的實例對象
*/
public void putBean(String id, Object object) {
map.put(id, object);
}
/**
* 通過id獲取實例對象
*
* @param id:就是bean中自定義的id
* @return
*/
public Object getBean(String id) {
return map.get(id);
}
}
3.創建啓動類Main
是不是和上邊開局演示的入口代碼很像。
public class Main {
public static void main(String[] args) {
XmlApplicationContext context = new XmlApplicationContext("spring-ioc.xml");
A a = (A)context.getBean("a");
B b = (B) context.getBean("b");
C c = (C) context.getBean("c");
}
}
那就要檢驗結果是不是盡人意了。
4.測試
哈哈,測試交給讀者來測試吧!相信你也是很感興趣的。
5.總結
springIoc的思路大致就是這樣子的:
1、解析xml文件
2、獲取xml文件所有的節點內容
3、利用java反射機制創建實例對象
4、將實例對象添加到ioc容器
但是要知道spring的內部實現可不單單是這幾百行啊。
溫馨提示:自己建個工程,代碼直接拷下來就能用,並且讀起來更有感覺。代碼中的註釋純手寫,自我感覺還是比較詳細的,一行一行的讀。相信初學者看完後肯定會知道spring是如何實現ioc的思路的。並且也能掌握如何把xml中的內容讀取出來並用對象接收。
最後爲了方便大家創建工程:
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.8.RELEASE</version> </dependency> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> </dependencies>