简单Spring容器实现
来源:互联网 发布:如何下载淘宝宝贝主图 编辑:程序博客网 时间:2024/06/06 13:18
在写完《IOC原理分析》,本来就准备写这个《简单Spring实现》的,
但是因为复习数据结构和Collection,就一直拖到现在了,今天还是抽一天时间来把这个的总结和代码都写了吧.
恩,《IOC原理分析》中已经很详细的说明了Spring的IOC理念,在这里,就不累述了。
今天我们要做以下几件事 :
一.进行OO分析, 给出代码框架
二.代码实现
三.测试
OO分析以及代码实现:
首先:
在IOC原理分析中,我们曾指出,系统是通过一个外部的XML文件,来控制对象之间的引用关系。
我们需要通过配置一个XML文件来控制对象之间的关系,这个XML文件的结构,我们先设计一个外部DTD来确定.
- <?xml version="1.0" encoding="utf-8"?>
- <!--自己定义的Spring中使用的xml模板-->
- <!--因为这只是自己练习的DEMO,所以定义的肯定不够完善,只是使之先能简单的完成一些功能
- 如果要更丰富的话,可以定义更多规则的,比如每个类对象是不是单例的等等,这里就不写了-->
- <br><!ELEMENT beans (bean*)>
- <!ELEMENT bean (property)>
- <br><!--property的子属性
- 要么是直接使用的值传递类型,此时,设置value属性的类型为#PCDATA类型
- 要么是需要指向其他引用的引用类型,此时,设置该元素为空元素,
- -->
- <!ELEMENT property (value|ref)>
- <!ELEMENT value (#PCDATA)>
- <!ELEMENT ref EMPTY>
- <br><!--设置bean的属性
- 每个bean必须有个ID属性,因为这个属性是唯一的表示这个bean,所以属性类型为ID,特征为#REQUIRED,每个bean还必须有class属性,每个类可以有多个对象,所以此属性不唯一,但是必须-->
- <!ATTLIST bean id ID #REQUIRED>
- <!ATTLIST bean class CDATA #REQUIRED>
- <!--设置每个property的属性
- -->
- <!ATTLIST property name CDATA #REQUIRED>
- <!--设置ref元素的id属性类型为IDREF,值类型为#REQUIRED
- 通过让它指向别的Bean的ID来完成完成引用(依赖就是这样的注入)-->
- <!ATTLIST ref id IDREF #REQUIRED>
然后:
在XML中,每个对象都是用一个Bean标签包裹的.
我们还需要一个Bean类来保存XML中声明的每一个对象,Bean类中应该有ID,TYPE,Properties等三个属 性.
ID是这个Bean的唯一标识
Type是这个Bean的类型(全类名)
Properties用来保存这个Bean对象中的所有属性,用HashMap<String,Object>来实现
1.key是这个属性的属性名
2.value是属性的属性值,如果value是字符等值类型,则支持存入该值,如果value是引用传递类型(对象),则value是这个对象对应的bean
- package cn.javamzd.MySpring;
- import java.util.HashMap;
- import java.util.Map;
- /**
- * XML中,每一个bean对象的结构
- */
- public class Bean {
- private String ID;//对象名
- private String type;//类型
- private Map<String,Object> properties=new HashMap<String,Object>();//对象的各个属性列表
- /**
- * 设置这个Bean的key属性,值为value
- * @param key
- * @param value
- */
- public void setProperty(String key,Object value){
- properties.put(key, value);
- }
- //ID、type对应的setter()、getter()方法
- }
其次:
因为一个XML声明文件中可能会很多个Bean标签。
所以我们需要一个Beans类,用来保存所有Bean信息。
用个一个HashMap<String,Bean>实现,key为bean的ID,Value为这个Bean对象.
- package cn.javamzd.MySpring;
- import java.util.HashMap;
- import java.util.Map;
- /**
- * 用来保存配置文件中的所有Bean对象
- *
- */
- public class Beans {
- //保存所有从Bean对象ID到Bean对象的映射
- private Map<String,Bean> beanMap=new HashMap<String,Bean>();
- public Map<String, Bean> getBeanMap() {
- return beanMap;
- }
- /**
- * 加入一个Bean对象
- * @param name 对象名
- * @param obj 对象
- * @return 加入状态
- */
- public boolean setBean(String name,Bean bean){
- beanMap.put(name, bean);
- return true;
- }
- /**
- * 根据Bean对象名,取得该Bean对象
- * @param name 对象名
- * @return 对象
- */
- public Bean getBean(String name){
- return beanMap.get(name);
- }
- }
再次:
有了XML文件,有了解析需要得到的Bean对象和Beans对象。
我们还需要一个XMLParser类,用来解析XML文件,得到我们配置好的Beans对象。
- package cn.javamzd.MySpring;
- import java.io.File;
- import java.io.FileReader;
- import javax.xml.parsers.SAXParser;
- import javax.xml.parsers.SAXParserFactory;
- import org.xml.sax.InputSource;
- import org.xml.sax.XMLReader;
- import cn.javamzd.spring.Util.SpringConfigCH;
- /**
- * 此类是个工具类
- * 用来设置通过SAX解析配置的XML文件
- * 得到其中的各个Bean的属性,并且保存到Beans当中去
- */
- public class XMLParser {
- /**
- * 通过XML的文件路径,解析该XML文件,返回Beans对象
- * @param path
- * @return
- */
- public Beans parser(String path) {
- // 设置接收XML中返回的对象队列的队列
- //因为一个XML文档对应一个Context对象,每个Context对象对应一个beans对象
- //所以,在方法内部生成产生此beans
- Beans beans=new Beans();
- try {
- // 得到解析工厂对象
- SAXParserFactory factory = SAXParserFactory.newInstance();
- // 得到一个解析对象
- SAXParser parser = factory.newSAXParser();
- // 得到解析器
- XMLReader reader = parser.getXMLReader();
- // 设置处理器
- // 此处是重点。利用值传递
- reader.setContentHandler(new SpringConfigCH(beans));
- //将文件路径封装为满足解析需要的InputSource类型
- InputSource source=new InputSource(new FileReader(new File(path)));
- // 设置需要解析的文档对象
- reader.parse(source);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return beans;
- }
- }
再次:
有了解析XML文件得到的Beans对象后,我们得到的还只是在XML文件中配置的每个对象的属性,并没有得到真正的对象
我们还需要一个Encoder方法,将Bean对象中的信息通过反射,动态生成我们需要的对象.
Encoder得到配置的每个类的具体对象后,这个对象还不太方便给我们的系统灵活运用。
我们还需要一个Context对象,将我们Encoder得到的每一个对象,放入这个Context对象中,可以通过对象的ID,取得这个对象。
显然,我们应该让系统中,没一个XML配置文档对应一个Context对象,文档中配置的所有对象都可以在Context中找到
和Beans类似,我们应该在Context中用一个Map来保存从ID到每一个Obj的映射
- package cn.javamzd.MySpring;
- import java.lang.reflect.Field;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
- public class Context {
- // 用来保存解码得到的从对象ID到这个对象对应的对象的映射
- private Map<String, Object> container = new HashMap<String, Object>();
- /**
- * 每一个XML文档对应一个Context对象 ,此Context对象中保存XML文档中声明的所有对象
- * 在实例化该类时传入XML文件地址,根据XML文件地址将所有对象都解析放入Context对象中的容器当中
- * @param path
- */
- public Context(String path) {
- // 解析path路径对应的XML文档,得到beans对象
- XMLParser parser = new XMLParser();
- Beans beans = parser.parser(path);
- // 遍历Beans取出每个Bean,解码Bean
- Map<String, Bean> beanMap = beans.getBeanMap();
- // 1.2.得到迭代器
- Iterator<String> it = beanMap.keySet().iterator();
- // 1.3.遍历迭代器,取出其中的key和value
- while (it.hasNext()) {
- String beanID = it.next();
- Bean bean = beanMap.get(beanID);
- //解码bean对象,得到Object对象
- Object obj=encoder(bean);
- //将beanID和Obj放入beans当中
- this.setBean(beanID, obj);
- }
- }
- /**
- * 解析Bean对象,生成对应的Object对象
- *
- * @param bean
- * @return
- */
- private Object encoder(Bean bean) {
- String id = bean.getId();
- String type = bean.getType();
- try {
- Class c = Class.forName(type);
- Object obj = c.newInstance();
- //一.取出bean当中设置的属性信息,设置到Obejct对象当中
- //1.取出所有属性
- //1.1.取得Bean中设置的所有属性
- Map<String, Object> properties = bean.getProperties();
- //1.2.得到迭代器
- Iterator<String> it=properties.keySet().iterator();
- //1.3.遍历迭代器,取出其中的key和value
- while(it.hasNext()){
- String fieldName=it.next();
- Object value=properties.get(fieldName);
- //如果属性是值传递类型,此时value的值,就是在XML中对其设定的值
- //如果属性是引用类型,value此时还是解析XML时存入在properties当中的一个个Bean对象
- if(value instanceof Bean){
- //取得value对应的Object对象
- value=getBean(((Bean) value).getId());
- }
- //2.将取得的所有属性,调用对象的setter()方法,设置到对象当中
- // 2.1.根据属性名,取得对象的setter()方法名
- String methodName=getMethodName4Field(fieldName);
- //2.1通过属性名,找到对应的属性的类型(通过方法名找方法时,需要方法的参数)
- Field field=c.getDeclaredField(fieldName);
- Class fType=field.getType();
- // 2.2.根据方法名,取得对应方法
- Method method= c.getMethod(methodName, fType);
- // 2.3.调用setter方法
- method.invoke(obj, value);
- }
- return obj;
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (SecurityException e) {
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- } catch (NoSuchFieldException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return null;
- }
- /**
- * 根据属性名,拼凑得到setter()方法名
- * @param name
- * @return
- */
- private String getMethodName4Field(String name) {
- String methodName = "set" + name.substring(0,1).toUpperCase()
- + name.substring(1, name.length());
- return methodName;
- }
- /**
- * 设置对象的ID,和这个ID对应的对象的映射关系
- *
- * @param id
- * @param bean
- * @return
- */
- public void setBean(String id, Object bean) {
- container.put(id, bean);
- }
- /**
- * 通过对象的ID,取得这个对象
- *
- * @param id
- * @return
- */
- public Object getBean(String id) {
- return container.get(id);
- }
- }
测试
Spring容器写完后,我们可以给个样例进行测试
假设有个Person类,Person类可以有宠物 ,可以让宠物叫
- package cn.javamzd.spring.pojo;
- public class Person {
- private Animal pet;//宠物
- public void setPet(Animal pet) {
- this.pet = pet;
- }
- public void petSay(){
- pet.say();
- }
- }
宠物Animal为个借口,各个具体的宠物Dog,Cat等等位实现类
- package cn.javamzd.spring.pojo;
- public interface Animal {
- public void say();
- }
- package cn.javamzd.spring.pojo;
- public class Dog implements Animal{
- private String name;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- @Override
- public void say() {
- System.out.println("Dog--->"+name);
- }
- }
我们用一下条件进行测试,可以覆盖到注入的值类型和引用类型两种情况
测试的XML文件为
- <?xml version="1.0" encoding="utf-8"?>
- <!DOCTYPE beans SYSTEM "G:\\Android\\MySpring\\src\\cn\\javamzd\\MySpring\\SpringConfig.dtd">
- <!-- 此配置文件来用配置对象之间的关系,以及每一个对象的内容 -->
- <beans>
- <bean id="wangcai" type="cn.javamzd.spring.pojo.Dog">
- <property name="name">
- <value>wangcai</value>
- </property>
- </bean>
- <bean id="zhangsan" type="cn.javamzd.spring.pojo.Person">
- <property name="pet">
- <ref id="wangcai"/>
- </property>
- </bean>
- </beans>
用来测试的Test类
- package cn.javamzd.MySpring;
- import cn.javamzd.spring.pojo.Person;
- public class Test {
- /**
- * @param args
- */
- public static void main(String[] args) {
- Context c=new Context("G:\\Android\\MySpring\\src\\cn\\javamzd\\MySpring\\config.xml");
- Person zhangsan=(Person)c.getBean("zhangsan");
- zhangsan.petSay();
- }
- }
好了,结果很显然,我就不说了
至此,我们的一个简单的Spring容器已经算是完全实现好了,我们可以很爽的进行IOC了。
- 简单Spring容器实现
- Spring 容器IOC解析及简单实现
- Spring的Ioc容器简单实现
- Spring容器简单理解
- spring容器简单使用
- 深入理解Spring--动手实现一个简单的SpringIOC容器
- 简单模拟 spring的bean容器的实现
- 深入理解Spring IOC,实现简单IOC容器
- Spring IOC容器实现
- Spring实现IOC容器
- Spring IOC容器实现
- 自己简单封装spring容器
- Spring容器的简单了解
- spring ioc容器简单理解
- spring IOC容器实现探讨
- Spring IOC容器的实现
- 测试spring父子容器实现
- Spring的IoC容器实现
- leetcode之 House Robber III
- LeetCode 056 Merge Intervals
- Android源码访问者模式---HtmlDocument
- Netty 介绍
- SSM+Maven环境搭建
- 简单Spring容器实现
- android6.23 locate定位服务
- Exec 与 eval 语句
- JavaMail:用Authenticator的子类进行身份验证及策略模式
- 实现淘宝订单(比如订单生成、未支付的订单等等)自定义view的实现
- OC原理分析
- WPF Image控件 Source: Byte[] ,BitmapImage 相互转换
- ACM应该学什么(知乎学长)
- PDO