实现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框架,决定分享一下我搭建过程中学习到的一些东西.我觉得不管你是个初级程序员还是高级程序员抑或 ...
随机推荐
- 基于GitLab的Code Review教程
一.前言 1.本文主要内容 GitLab Code Review机制说明 Git Workflow 与 Git Code Review Workflow GitLab Code Review 配置说明 ...
- 对css盒模型的理解
介绍一下标准css的盒子模型?低版本IE的盒子模型有什么不同的? 1.有两种:IE盒子模型(怪异模式).W3c盒子模型(标准模式). 2.盒模型组成:内容(content).内边距(padding). ...
- Aspnet mvc移除WebFormViewEngine
为了提高mvc的速度,在Global.asax中移除WebFormViewEngine protected void Application_Start() { RemoveWebFormEngine ...
- 西湖论剑2019复现-Web之首家线上赌场上线啦
首页打开 经过测试发现name和code参数可控,但尝试注入没有发现注入点,于是直接扫描目录找思路 一扫描,果然有问题 目录扫描里面可以看到有一个/.DS_Store的文件,DS_Store是Mac ...
- 《神经网络算法与实现-基于Java语言》的读书笔记
文章提纲 全书总评 读书笔记 C1.初识神经网络 C2.神经网络是如何学习的 C3.有监督学习(运用感知机) C4.无监督学习(自组织映射) Rreferences(参考文献) 全书总评 书本印刷质量 ...
- java 开发工具记录
jenkins 持续构建项目 lombok 优雅代码插件 sonarqube 代码检测插件
- 如何使用ASP.NET Core、EF Core、ABP(ASP.NET Boilerplate)创建分层的Web应用程序(第一部分)
本文是为了学习ABP的使用,是翻译ABP官方文档的一篇实战教程,我暂时是优先翻译自己感兴趣或者比较想学习的部分,后续有时间希望能将ABP系列翻译出来,除了自己能学习外,有可能的话希望帮助一些英文阅读能 ...
- maven依赖scope配置项讲解(转)
原文:https://blog.csdn.net/lisongjia123/article/details/56299006 <scope>的分类一.complie编译域,这个是Maven ...
- 第一个Appium脚本
测试环境 Win 10 64bit Python 3.5 Appium 1.7.2 Andriod 5.1.1 模拟器& Android 5.1 MX4 测试App:考研帮Android版 3 ...
- monkeyrunner简介
monkeyrunner简介 MonkeyRunner工具是使用Jython(使用Java编程语言实现的Python)写出来的,它提供了多个API,通过monkeyrunner API 可以写一个Py ...