什么是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>