实现IOC功能的简单Spring框架
需求分析
设计一个含有IOC的简单Spring,要求含有对象注册、对象管理以及暴露给外部的获取对象功能。
项目设计
- 对于注册的对象用一个类BeanInfo来描述其信息,包括对象标识、全类名以及属性名与值的Map。
- 对于IOC容器设定一个顶层接口BeanFactory,定义通过对象标识获取对象示例的方法getBean(String id)。AbstractBeanFactory实现该接口,在该类中实现解析生成目标对象,以及获取对象方法,并在该类中添加注册器接口,以便能从注册器中读取注册的对象。
- 对于注册器,提供一个顶层接口SourceReader,并在其中添加加载用户注册的对象的方法loadBeans(String filePath)。本项目中使用读取XML的方式,从XML中读取出注册的对象,并把它们封装成BeanInfo放入Map中。
- 设定一个上下文XMLContext,继承AbstractBeanFactory,负责选择使用哪种注册方式,并决定何时加载注册的对象。
代码实现
BeanInfo类
package ex1;
import java.util.HashMap;
import java.util.Map;
/**
* 该类用于描述注册在容器中的对象
*/
public class BeanInfo {
private String id; //对象ID,名字
private String type; //全类名
private Map<String, Object> properties = new HashMap<>(); //属性名与值的映射集合
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Map<String, Object> getProperties() {
return properties;
}
public void setProperties(Map<String, Object> properties) {
this.properties = properties;
}
public void addProperty(String key,Object value) {
properties.put(key,value);
}
}
BeanFactory接口
package ex1;
public interface BeanFactory {
/**
* 根据对象的名称标识来获取对象实例
* @param id 对象名称,即对象描述信息中的对象标识
* @return 指定名称的对象实例
*/
Object getBean(String id);
}
AbstractBeanFactory类
package ex1;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
/**
* 最顶层的IOC实现
* 该类负责从注册器中取出注册对象
* 实现从对象描述信息转换为对象实例的过程
* 实现根据名称获取对象的方法
*/
public abstract class AbstractBeanFactory implements BeanFactory {
private String filePath; //注册文件路径
private Map<String, BeanInfo> container; //注册对象信息Map(IOC容器)
protected SourceReader reader; //对象注册读取器
public AbstractBeanFactory(String filePath) {
this.filePath = filePath;
}
/**
* 抽象方法,需由子类实现,用于指定使用什么样的注册读取器
* @param reader 指定的注册读取器
*/
protected abstract void setReader(SourceReader reader);
//从注册读取器中读取注册对象的信息Map
public void registerBeans() {
this.container = this.reader.loadBeans(this.filePath);
}
//实现BeanFactory定义的根据名称获取指定对象的方法
@Override
public Object getBean(String id) {
BeanInfo beanInfo = this.container.get(id); //根据对象名称获取该对象的描述信息
if (beanInfo == null) {
return null;
} else {
//根据对象信息,解析并生存指定对象实例,返回给用户
return this.parseBean(beanInfo);
}
}
/**
* 解析并生成对象实例
* 该方法主要通过反射完成,步骤如下:
* 1.根据类名,加载指定类,并获取该类的Class对象clazz
* 2.使用clazz实例化该类,获取一个对象,注意,这里采用无参构造方法
* 3.逐个设置对象子段的值,这里采用setter Method方式,而不是直接使用Field对象
* 4.返回对象实例
* @param beanInfo 指定对象的描述信息
* @return
*/
protected Object parseBean(BeanInfo beanInfo) {
Class clazz;
Object bean = null;
try {
clazz = Class.forName(beanInfo.getType()); //根据对象的全类名,指定类
bean = clazz.newInstance(); //使用注册对象的无参构造函数,实例化对象
Method[] methods = clazz.getMethods(); //获取所有公共方法(其实Spring获取的是所有方法,包括非公有是)
for (String property : beanInfo.getProperties().keySet()) {
//首字母大写
String name = property.toUpperCase().charAt(0) + property.toLowerCase().substring(1);
//获取属性的setter方法名称(命名规范)
String setter = "set" + name;
for (Method method : methods) {
if (method.getName().equals(setter)) {
Object value = beanInfo.getProperties().get(property);
method.invoke(bean, value); //通过反射对属性赋值
break;
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return bean;
}
}
SourceReader接口
package ex1;
import java.util.Map;
/**
* 注册读取器接口
* 复制读取用户注册的对象
* 继承该接口的类可以实现多种读取方式,如从配置文件中读取,根据标注读取,从网络中读取等
*/
public interface SourceReader {
/**
* 读取用户注册的对象信息
* @param filePath 注册路径
* @return 注册对象信息Map
*/
Map<String, BeanInfo> loadBeans(String filePath);
}
XMLSourceReader类
这里实现从xml配置文件中读取。需要用到dom4j包,用来解析XML文件。本项目中XML文件放置在根目录下,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="Person" class="ex1.Person">
<property name="name" value="fang"/>
</bean>
</beans>
实现代码
package ex1;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* XML注册读取器
* 该类继承了注册读取器接口,并模拟实现了读取注册对象信息的方法
*/
public class XMLSourceReader implements SourceReader {
/**
* 实现读取注册对象信息方法
* 自己编写通过配置文件读取的实现
*/
@Override
public Map<String, BeanInfo> loadBeans(String filePath) {
BeanInfo info = new BeanInfo(); //注册对象的info
InputStream is = XMLContext.class.getClassLoader().getResourceAsStream(filePath);//获取xml文件
SAXReader reader = new SAXReader();
Map<String,BeanInfo> beanMap = new HashMap<>();
try {
Document document = reader.read(is);
Element root = document.getRootElement(); //获取根标签,这里是beans
//遍历所有bean
for(Iterator iterator = root.elementIterator("bean");iterator.hasNext();){
Element element = (Element)iterator.next();
//获取id和class
Attribute id = element.attribute("id");
Attribute clazzName = element.attribute("class");
info.setId(id.getText());
info.setType(clazzName.getText());
//遍历该bean的property
for(Iterator it=element.elementIterator("property");it.hasNext();){
Element tmp = (Element)it.next();
//获取name和value
Attribute name = tmp.attribute("name");
Attribute value = tmp.attribute("value");
info.addProperty(name.getText(),value.getText());
}
beanMap.put(id.getText(),info);
}
} catch (DocumentException e) {
e.printStackTrace();
}
return beanMap;
}
}
XMLContext类
package ex1;
public class XMLContext extends AbstractBeanFactory {
/**
* 上下文的构造方法
* 该方法中指明注册读取器
* 并在构造该方法时一次性加载注册的对象
* @param filePath
*/
public XMLContext(String filePath) {
super(filePath);
this.setReader(new XMLSourceReader());//添加注册读取器
this.registerBeans(); //加载注册的对象信息
}
//设置注册读取器
@Override
protected void setReader(SourceReader reader) {
this.reader = reader;
}
}
测试类
Speakable接口
package ex1;
public interface Speakable {
void speak(String message);
}
Person类
package ex1;
public class Person implements Speakable {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void speak(String message) {
System.out.println(this.name + " say: " + message);
}
}
Bootstrap类
package ex1;
public class Bootstrap {
public static void main(String[] args) {
BeanFactory beanFactory = new XMLContext("bean.xml");
Speakable speakable = (Speakable) beanFactory.getBean("Person");
speakable.speak("Experience One!");
}
}
运行结果
fang say: Experience One!
实现IOC功能的简单Spring框架的更多相关文章
- Spring框架系列(2) - Spring简单例子引入Spring要点
上文中我们简单介绍了Spring和Spring Framework的组件,那么这些Spring Framework组件是如何配合工作的呢?本文主要承接上文,向你展示Spring Framework组件 ...
- Spring框架系列(3) - 深入浅出Spring核心之控制反转(IOC)
在Spring基础 - Spring简单例子引入Spring的核心中向你展示了IoC的基础含义,同时以此发散了一些IoC相关知识点; 本节将在此基础上进一步解读IOC的含义以及IOC的使用方式.@pd ...
- Java开发工程师(Web方向) - 04.Spring框架 - 第2章.IoC容器
第2章.IoC容器 IoC容器概述 abstract: 介绍IoC和bean的用处和使用 IoC容器处于整个Spring框架中比较核心的位置:Core Container: Beans, Core, ...
- spring-framework-中文文档一:IoC容器、介绍Spring IoC容器和bean
5. IoC容器 5.1介绍Spring IoC容器和bean 5.2容器概述 本章介绍Spring Framework实现控制反转(IoC)[1]原理.IoC也被称为依赖注入(DI).它是一个过程, ...
- Spring框架源码阅读之Springs-beans(一)容器的基本实现概述(待续)
去年通过实际框架代码的阅读,以及结合<Spring源码深度解析>和<Spring技术内幕>的阅读,对Spring框架内Bean模块有了一个整体性的认识.对此进行的总结性整理和回 ...
- 自定义beans.xml文件实现Spring框架
经过一天的补习,学习文件加载,java反射,JDom等知识,到了晚上终于能够搭出一个基于配置文件的简单spring框架实现! 首先我们先看看这个问题: 下面是两副图左边是项目结构图,右边是UML图: ...
- Spring框架-IOC和AOP简单总结
参考博客: https://blog.csdn.net/qq_22583741/article/details/79589910 1.Spring框架是什么,为什么,怎么用 1.1 Spring框架是 ...
- Spring框架的IOC核心功能快速入门
2. 步骤一:下载Spring框架的开发包 * 官网:http://spring.io/ * 下载地址:http://repo.springsource.org/libs-release-local/ ...
- Spring 框架的架包分析、功能作用、优点,及jar架包简介
Spring 框架的架包详解 Spring的作用 Spring的优势 由于刚搭建完一个MVC框架,决定分享一下我搭建过程中学习到的一些东西.我觉得不管你是个初级程序员还是高级程序员抑或 ...
随机推荐
- 数据库原理 - 序列5 - 事务是如何实现的? - Undo Log解析
本文节选自作者书籍<软件架构设计:大型网站技术架构与业务架构融合之道>.作者微信公众号:架构之道与术.公众号底部菜单有书友群可以加入,与作者和其他读者进行深入讨论.也可以在京东.天猫上购买 ...
- [20190417]隐含参数_SPIN_COUNT.txt
[20190417]隐含参数_SPIN_COUNT.txt--//在探究latch spin计数之前,先简单探究_SPIN_COUNT.实际上oracle现在版本latch spin的数量不再是200 ...
- git常用命令说明教程
git常用命令说明教程 git介绍 是一个分布式的,版本控制软件.每台使用git的电脑都是一个分版本库.svn是集中管理的. 安装git 一 git相关操作 1.官网下载最新版安装https://gi ...
- Linux:Day20(上) openssh和CA
ssh:secure shell protocol,22/tcp,安全的远程登陆 OpenSSH:ssh协议的开源实现: dripbear:另一个开源实现: SSH协议版本 v1:基于CRC-32做M ...
- 逆卷积的详细解释ConvTranspose2d(fractionally-strided convolutions)
1.首先先定义进行卷积的参数: 输入特征图为高宽一样的Hin*Hin大小的x 卷积核大小kernel_size 步长stride padding填充数(填充0) 输出特征图为Hout*Hout大小的y ...
- 这可能是最简单的Page Object库
做过web自动化测试的同学,对Page object设计模式应该不陌生. Page object库应该根据以下目标开发: Page object应该易于使用 清晰的结构 PageObjects 对于页 ...
- 随心测试_软测基础_004<测试人员工作职责>
接上篇续,依据_软测基础体系:<依据不同的测试对象,选取适合的方法,按照设计的流程完成测试工作,检验整个过程是否达到测试的目的>.“学以致用”,实践于工作职责 常见面试题: —— 诸如以下 ...
- linq中如何在join中指定多个条件
public ActionResult Edit(int id) { using (DataContext db = new DataContext(ConfigurationManager.Conn ...
- Linux基础优化与安全归纳总结
一名运维工程师在运维岗位上时间久了,就会发现Linux优化的重要性,同时会给运维工作带来很多的便利性.本人逐渐认识到了这一点,所以特意在工作闲暇之余,通过阅读Linux相关书籍及向同事.同行高手咨询, ...
- python操作Excel、openpyxl 之图表,折线图、饼图、柱状图等
一.准备 需要模块: from openpyxl.workbook import Workbook from openpyxl.chart import Series,LineChart, Refer ...