深入理解SpringIOC容器
转载来源:【https://www.cnblogs.com/fingerboy/p/5425813.html】
前言:
在逛博客园的时候突然发现一篇关于事务的好文章,说起spring事物就离不开AOP和动态代理,在研究的过程中突然想起spring的两大特性IOC和AOP,之前对这方面也是理解很浅,于是在网上查了一下关于springIOC的文章,发现了一篇好文章,自己也跟着手动创建了一个项目,打断点随着流程走了一遍,这篇文章对IOC的理解可以起到很大的作用,在这里转载记录一下:
主要思想:
提到IOC,第一反应就是控制反转,我以前以为SpringIOC就是控制反转,控制反转就是SpringIOC,当然这种理解是错误的,控制反转是一种思想,一种模式,而Spring的IOC容器是实现了这种思想这种模式的一个载体.
使用过Spring的人都熟知,SpringIOC容器可以在对象生成或初始化时就直接将数据注入到对象中,如果对象A的属性是另一个对象B,还可以将这个对象B的引用注入到注入到A的数据域中.
如果在初始化对象A的时候,对象B还没有进行初始化,而A又需要对象B作为自己的属性,那么就会用一种递归的方式进行注入,这样就可以把对象的依赖关系清晰有序的建立起来.
IOC容器解决问题的核心就是把创建和管理对象的控制权从具体的业务对象手中抢过来.由IOC容器来管理对象之间的依赖关系,并由IOC容器完成对象的注入.这样就把应用从复杂的对象依赖关系的管理中解放出来,简化了程序的开发过程.
下图是这个简单IOC容器的类图(原谅我真没学过UML,凑合看吧):
程序中所有的Bean之间的依赖关系我们是放在一个xml文件中进行维护的,就是applicationContext.xml,ConfigManager类完成的功能是读取xml,并将所有读取到有用的信息封装到我们创建的一个Map<String,Bean>集合中,用来在初始化容器时创建bean对象.
定义一个BeanFactory的接口,接口中有一个getBean(String name)方法,用来返回你想要创建的那个对象.
然后定义一个该接口的实现类ClassPathXmlApplicationContext.就是在这个类的构造方法中,初始化容器,通过调用ConfigManager的方法返回的Map集合,通过反射和内省一一创建bean对象.这里需要注意,对象的创建有两个时间点,这取决与bean标签中scope属性的值:
如果scope="singleton",那么对象在容器初始化时就已创建好,用的时候只需要去容器中取即可.
如果scope="prototype",那么容器中不保存这个bean的实例对象,每次开发者需要使用这个对象时再进行创建.
使用的主要知识点:
dom4j解析xml文件
xpath表达式(用于解析xml中的标签
java反射机制
内省(获取Bean属性的set方法进行赋值)
项目结构图及介绍如下:
项目需要的jar包与项目结构已经在上图中介绍了,这个项目所能实现的功能如下:
1、IOC容器能管理对象的创建以及对象之间的依赖关系.
2、能够实现数据的自动类型转换(借助BeanUtils).
3、能够实现scope="singleton"和scope="prototype"的功能,即能够控制对象是否为单例.
下面介绍代码部分:
application.xml:
<?xml version="1.0" encoding="utf-8"?>
<beans>
<bean name="student" class="com.entiy.Student" >
<property name="name" value="逝清雪"></property>
</bean>
<bean name="teacher" class="com.entiy.Teacher">
<property name="student" ref="student"></property>
</bean>
<bean name="person" class="com.entiy.Person" scope="prototype">
<property name="teacher" ref="teacher"></property>
<property name="student" ref="student"></property>
</bean>
</beans>
实体类Student,Teacher,Person:
public class Person { private Student student; private Teacher teacher; public Student getStudent() {
return student;
} public void setStudent(Student student) {
this.student = student;
} public Teacher getTeacher() {
return teacher;
} public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
} public class Student { private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} } public class Teacher { private Student student; public Student getStudent() {
return student;
} public void setStudent(Student student) {
this.student = student;
} }
用于封装Bean标签信息的Bean类:
public class Bean { private String name; private String className; private String scope="singleton"; private List<Property> properties = new ArrayList<Property>(); public String getScope() {
return scope;
} public void setScope(String scope) {
this.scope = scope;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getClassName() {
return className;
} public void setClassName(String className) {
this.className = className;
} public List<Property> getProperties() {
return properties;
} public void setProperties(List<Property> properties) {
this.properties = properties;
}
}
用与封装Bean子标签property内容的Property类:
public class Property { private String name; private String value; private String ref; public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref = ref;
}
}
ConfigManager类:
public class ConfigManager { private static Map<String, Bean> map = new HashMap<String, Bean>(); // 读取配置文件并返回读取结果
// 返回Map集合便于注入,key是每个Bean的name属性,value是对应的那个Bean对象
@SuppressWarnings("unchecked")
public static Map<String, Bean> getConfig(String path) {
/*
* dom4j实现 1.创建解析器 2.加载配置文件,得到document对象 3.定义xpath表达式,取出所有Bean元素
* 4.对Bean元素继续遍历 4.1将Bean元素的name/class属性封装到bean类属性中
* 4.2获得bean下的所有property子元素 4.3将属性name/value/ref分装到类Property类中
* 5.将property对象封装到bean对象中 6.将bean对象封装到Map集合中,返回map
*/
// 1.创建解析器
SAXReader reader = new SAXReader();
// 2.加载配置文件,得到document对象
InputStream is = ConfigManager.class.getResourceAsStream(path);
Document doc = null;
try {
doc = reader.read(is);
} catch (DocumentException e) {
e.printStackTrace();
throw new RuntimeException("请检查您的xml配置是否正确");
}
// 3.定义xpath表达式,取出所有Bean元素
String xpath = "//bean"; // 4.对Bean元素继续遍历
List<Element> list = doc.selectNodes(xpath);
if (list != null) {
// 4.1将Bean元素的name/class属性封装到bean类属性中 // 4.3将属性name/value/ref分装到类Property类中
for (Element bean : list) {
Bean b = new Bean();
String name = bean.attributeValue("name");
String clazz = bean.attributeValue("class");
String scope = bean.attributeValue("scope");
b.setName(name);
b.setClassName(clazz);
if (scope != null) {
b.setScope(scope);
}
// 4.2获得bean下的所有property子元素
List<Element> children = bean.elements("property"); // 4.3将属性name/value/ref分装到类Property类中
if (children != null) {
for (Element child : children) {
Property prop = new Property();
String pName = child.attributeValue("name");
String pValue = child.attributeValue("value");
String pRef = child.attributeValue("ref");
prop.setName(pName);
prop.setRef(pRef);
prop.setValue(pValue);
// 5.将property对象封装到bean对象中
b.getProperties().add(prop);
}
}
// 6.将bean对象封装到Map集合中,返回map
map.put(name, b);
}
}
return map;
} }
BeanFactory接口:
public interface BeanFactory {
//核心方法getBean
Object getBean(String name);
}
ClassPathXmlApplicationContext类:
public class ClassPathXmlApplicationContext implements BeanFactory { // 获得读取的配置文件中的Map信息
private Map<String, Bean> map;
// 作为IOC容器使用,放置sring放置的对象
private Map<String, Object> context = new HashMap<String, Object>(); public ClassPathXmlApplicationContext(){
super();
} public ClassPathXmlApplicationContext(String path) {
// 1.读取配置文件得到需要初始化的Bean信息
map = ConfigManager.getConfig(path);
// 2.遍历配置,初始化Bean
for (Entry<String, Bean> en : map.entrySet()) {
String beanName = en.getKey();
Bean bean = en.getValue(); Object existBean = context.get(beanName);
// 当容器中为空并且bean的scope属性为singleton时
if (existBean == null && bean.getScope().equals("singleton")) {
// 根据字符串创建Bean对象
Object beanObj = createBean(bean); // 把创建好的bean对象放置到map中去
context.put(beanName, beanObj);
}
} } // 通过反射创建对象
@SuppressWarnings("rawtypes")
private Object createBean(Bean bean) {
// 创建该类对象
Class clazz = null;
try {
clazz = Class.forName(bean.getClassName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new RuntimeException("没有找到该类" + bean.getClassName());
}
Object beanObj = null;
try {
beanObj = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("没有提供无参构造器");
}
// 获得bean的属性,将其注入
if (bean.getProperties() != null) {
for (Property prop : bean.getProperties()) {
// 注入分两种情况
// 获得要注入的属性名称
String name = prop.getName();
String value = prop.getValue();
String ref = prop.getRef();
// 使用BeanUtils工具类完成属性注入,可以自动完成类型转换
// 如果value不为null,说明有
if (value != null) {
Map<String, String[]> parmMap = new HashMap<String, String[]>();
parmMap.put(name, new String[] { value });
try {
BeanUtils.populate(beanObj, parmMap);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("请检查你的" + name + "属性");
}
} if (ref != null) {
// 根据属性名获得一个注入属性对应的set方法
// Method setMethod = BeanUtil.getWriteMethod(beanObj,
// name);
// 看一看当前IOC容器中是否已存在该bean,有的话直接设置没有的话使用递归,创建该bean对象
Object existBean = context.get(prop.getRef());
if (existBean == null) {
// 递归的创建一个bean
existBean = createBean(map.get(prop.getRef()));
// 放置到context容器中
// 只有当scope="singleton"时才往容器中放
if (map.get(prop.getRef()).getScope()
.equals("singleton")) {
context.put(prop.getRef(), existBean);
}
}
try {
// setMethod.invoke(beanObj, existBean)通过BeanUtils为beanObj设置属性
BeanUtils.setProperty(beanObj, name, existBean);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("您的bean的属性" + name
+ "没有对应的set方法");
} } }
} return beanObj;
} @Override
public Object getBean(String name) {
Object bean = context.get(name);
// 如果为空说明scope不是singleton,那么容器中是没有的,这里现场创建
if (bean == null) {
bean = createBean(map.get(name));
} return bean;
}
最后就是一个测试类TestBean:
public class TestBean { @Test
public void test(){ BeanFactory bf= new ClassPathXmlApplicationContext("/applicationContent.xml");
Person s=(Person)bf.getBean("person");
Person s1=(Person)bf.getBean("person");
System.out.println(s==s1);
System.out.println(s1);
Student stu1=(Student) bf.getBean("student");
Student stu2=(Student) bf.getBean("student");
String name=stu1.getName();
System.out.println(name);
System.out.println(stu1==stu2);
}
}
测试结果:
false
com.entiy.Person@79b4d0f
逝清雪
true
深入理解SpringIOC容器的更多相关文章
- 深入理解Spring--动手实现一个简单的SpringIOC容器
接触Spring快半年了,前段时间刚用Spring4+S2H4做完了自己的毕设,但是很明显感觉对Spring尤其是IOC容器的实现原理理解的不到位,说白了,就是仅仅停留在会用的阶段,有一颗想读源码的心 ...
- 基于nutz框架理解Ioc容器
同样我们从问题入手去验证以及去理解Ioc容器都做了哪些事情: 1.nutz是有几种方式获取需要容器管理bean的信息? 第一种是使用json格式的文件进行配置,如: 第二种:使用注解@IocBean ...
- 通过单元测试理解spring容器以及dubbo+zookeeper单元测试异常处理
一.先说一个结论:单元测试与主项目的spring容器是隔离的,也就是说,单元测试无法访问主项目spring容器,需要自己加载spring容器. 接下来是代码实例,WEB主项目出于运行状态,单元测试中可 ...
- 理解docker容器和镜像(layer,ufs)和docker命令解释
博客好文1:http://blog.csdn.net/x931100537/article/details/49633107(理解docker容器和镜像,理解简单,从原理入手,什么是layer,什么是 ...
- SpringIOC容器装配Bean
Spring 的core Container(Spring的核心容器)有四大部分:bean.context.core.expression 在进行Bean的配置时候,需要添加四个jar包 如下: 分别 ...
- 高并发秒杀系统--junit测试类与SpringIoc容器的整合
1.原理是在Junit启动时加载SpringIoC容器 2.SpringIoC容器要根据Spring的配置文件加载 [示例代码] package org.azcode.dao; import org. ...
- 【转】Spring学习---SpringIOC容器的初始化过程
[原文]https://www.toutiao.com/i6594400249429623304/ SpringIOC容器的初始化过程 简单来说,IoC容器的初始化是由refresh()方法来启动的, ...
- 【原创】深入理解Docker容器和镜像 -- 分析了docker的命令含义
10张图带你深入理解Docker容器和镜像 镜像(Image)就是一堆只读层(read-only layer)的统一视角 要点:容器 = 镜像 + 读写层.并且容器的定义并没有提及是否要运行容器. 一 ...
- Spring - SpringIOC容器详解
一.什么是Spring IOC: Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想. 在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是 ...
随机推荐
- GlideDemo【Glide3.7.0版本的简单使用以及圆角功能】
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 本Demo主要记录Glide3.7.0版本的简单运用和实现圆角方案. 效果图 代码分析 Glide的centerCrop()和fit ...
- LindDotNetCore~ISoftDelete软删除接口
回到目录 概念 ISoftDelete即软删除,数据在进行delete后不会从数据库清除,而只是标记一个状态,在业务范围里都不能获取到这个数据,这在ORM框架里还是比较容易实现的,对传统的ado来说需 ...
- SmartSql Cache标签
Demo <Cache Id="T_Test.LruCache" Type="Lru"> <Parameter Key="Cache ...
- QuickBI助你成为分析师——计算字段功能
摘要: 在用户创建报表时,通过现有字段数据不能直接满足展示需求,需要进行一定建模操作.目前产品支持在数据集编辑界面进行初步建模,下面主要介绍新建字段功能,以达到展示需求. 在用户创建报表时,有时通过现 ...
- 如何将workerman部署到windows服务器上面
一直以来对php的即时通讯都很好奇,其实是不知道应该怎么来实现,后来了解到了swoole和workerman这两个神器,他们都可以实现即时通信的功能,其中swoole是C语言编写的php扩展,其配置起 ...
- 移动端video不全屏播放
<div class="m-video"> <video x5-playsinline="" playsinline="" ...
- git 取消对某个文件的跟踪
git rm --cached <fileName> 例如 : git add 后,不想提交在status里的.idea/vcs.xml.操作如下:
- vuex最简单、最直白、最全的入门文档
前言 我们经常用element-ui做后台管理系统,经常会遇到父组件给子组件传递数据,下面一个简单的例子,点击按钮,把弹框显示变量数据通过子组件的props属性传递,子组件通过$emit事件监听把数据 ...
- 五分钟读懂UML类图(转)
平时阅读一些远吗分析类文章或是设计应用架构时没少与UML类图打交道.实际上,UML类图中最常用到的元素五分钟就能掌握,下面赶紧来一起认识一下它吧: 一.类的属性的表示方式 在UML类图中,类使用包含类 ...
- 一起学Android之Layout
本文简述在Android开发中布局的简单应用,属于基础知识,仅供学习分享使用. 概述 在Android UI开发中,布局类型主要有两种:LinearLayout(线性布局)和RelativeLayou ...