Java—实现 IOC 功能的简单 Spring 框架

编写一个实现 IOC 功能的简单 Spring 框架,包含对象注册、对象管理、及暴 露给外部获取对象的功能,并编写测试程序。扩展注册器的方式,要求采用 XML 和 txt 文件。

源代码

package myspring;

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
   protected SourceReader reader; //对象注册读取器
   public AbstractBeanFactory(String filePath){
      this.filePath = filePath;
   }
   /**
    * 该方法为抽象方法,需有子类类实现,用于指定使用什么样的注册读取器
    * @param reader 指定的注册读取器
    */
   protected abstract void setSourceReader(SourceReader reader);
   // 从注册读取器中读取,注册对象的信息MAP
   public void registerBeans(){
      this.container = this.reader.loadBeans(filePath);
   }
   //  实现BeanFactory定义的根据名称获取指定对象的方法
   @Override
   public Object getBean(String name) {
      BeanInfo beaninfo = this.container.get(name); //根据对象名获取该对象的描述信息
      if(beaninfo == null){ //如果容器中不存在该对象的描述信息,则返回null,此处可以抛开一个异常
         return null;
      }else{ //根据对象信息,解析并生成指定对象实例,返回给用户
         return this.parseBean(beaninfo);
      }
   }  
   /**
    * 解析并生成对象实例
    * 该方法主要通过反射完成,步骤如下:
    * 1.根据类名,加载指定类,并获取该类的貌似Class对象clazz
    * 2.使用Class对象clazz实例化该类,获取一个对象,注意,这儿实例化对象时,采用的无参构造方法,因此要求注册的对象必须含有无参构造方法
    * 3.逐个设置对象字段的值,这儿采用setter Method方式,而不是直接使用Field对象的原因是,用户有可能在setter对象中,对注入的值进行额外处理,如格式化等
    * 4.返回对象实例
    * @param beaninfo 指定对象的描述信息
    * @return
    */
   protected Object parseBean(BeanInfo beaninfo){
      Class clazz; 
      try {
         clazz = Class.forName(beaninfo.getType()); //根据对象的全类名,指定类
         Object bean = clazz.newInstance(); //使用注册对象的无参构造函数,实例化对象实例
         Method[] methods = clazz.getMethods(); //获取该类声明的所有公共方法,其实Spring获取的是所有方法,包括非公有的
         for(String property : beaninfo.getProperties().keySet()){ //遍历对象的所有属性,进行赋值
            String setter = "set" + StringUtil.firstCharToUp(property); //获取属性的setter方法名称
            for(Method method : methods){ //遍历该类的所有公有方法
               String methodName = method.getName(); //获取方法名称
               if(methodName.equals(setter)){ //比较方法名与属性的setter方法名是否相同,如果相同则进行赋值
                  Object value = beaninfo.getProperties().get(property); //从对象描述信息中获取该属性的值
                  method.invoke(bean,value); //通过反射对属性进行赋值
                  continue; //对下一属性赋值
               }
            }
         }
         return bean; //返回指定的对象
      } catch (Exception e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      } 
      return null;
   }
   
}

package myspring;

/**
 * IOC容器的顶层接口
 */
public interface BeanFactory {
   /**
    * 根据对象的名称标识来获取对象实例
    * @param name 对象名称,即对象描述信息中的对象标识
    * @return 指定名称的对象实例
    */
   Object getBean(String name);
}

package myspring;

import java.util.HashMap;
import java.util.Map;

//该类用于描述注册在容器中的对象
public class BeanInfo {
   private String id; //对象的标识
   private String type; //对象的类型,即全类名
   private Map<String,Object> properties = new HashMap<String,Object>(); //对象的属性及值得集合 
   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 name, Object value){
      this.properties.put(name, value);
   }
}
package myspring;

public class Bootstrap {
   public static void main(String[] args) {
      BeanFactory factory = new TXTContext("bean.txt");
      Speakable s = (Speakable)factory.getBean("Person");
      s.speak("Lesson one!");
   }
}
package myspring;

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);
   }
}
package myspring;

import java.util.Map;

/**
 * 注册读取器接口
 * 负责从读取用户注册的对象
 * 继承该接口的类可以实现多种读取方式,如从配置文件中读取,根据标注读取,从网络中读取等
 */
public interface SourceReader {
   /**
    * 读取用户注册的对象信息
    * @param filePath 读取录取
    * @return 注册对象信息Map
    */
   Map<String,BeanInfo> loadBeans(String filePath);
}
package myspring;

public interface Speakable {
   public void speak(String message);
}
package myspring;

public class StringUtil {
   /*public static void main(String[] args) {
      System.out.println(StringUtil.firstCharToUp(str)); 
   }*/
   public static String firstCharToUp(String str){
      char ch[]=str.toCharArray();
      char ch1=Character.toUpperCase(ch[0]);
      ch[0]=ch1;
      String s=new String(ch);
      return s;
   }
   
   public static String firstCharToLower(String str){
      char ch[]=str.toCharArray();
      char ch1=Character.toLowerCase(ch[0]);
      ch[0]=ch1;
      String s=new String(ch);
      return s;
   }
}
package myspring;

public class TXTContext extends AbstractBeanFactory {
    /**
     * 上下文的构造方法
     * 该方法中指明注册读取器
     * 并在构造该方法时一次性的加载注册的对象
     * @param filePath
     */
    public TXTContext(String filePath) {
        super(filePath);
        this.setSourceReader(new TXTSourceReader()); //添加注册读取器,此处的注册读取器为XMLSourceReader
        this.registerBeans(); //加载注册的对象信息
    }
    //  设置注册读取器
    @Override
    protected void setSourceReader(SourceReader reader) {
        this.reader = reader;
    }
}

package myspring;

import org.w3c.dom.*;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.util.HashMap;
import java.util.Map;

public class TXTSourceReader implements SourceReader{
    /**
     * 实现读取注册对象信息方法
     * 此处只是模拟测试使用,感兴趣的同学可以自己书写通过配置文件读取的实现
     */
    @Override
    public Map<String, BeanInfo> loadBeans(String filePath) {
        //初始化一个对象信息

        Map<String,BeanInfo> beans = new HashMap<String,BeanInfo>(); //初始化一个对象信息Map
        BeanInfo beanInfo=new BeanInfo();
        File filename = new File("bean.txt"); // 要读取以上路径的input。txt文件
        InputStreamReader reader = null; // 建立一个输入流对象reader
        try {
            reader = new InputStreamReader(
                    new FileInputStream(filename));
            BufferedReader br = new BufferedReader(reader); // 建立一个对象,它把文件内容转成计算机能读懂的语言
            String line = "";
            line = br.readLine();
            while (line != null) {
                String[] t=line.split("=");
                System.out.println(t[0]+t[1]);
                if(t[0].equals("id"))
                {
                  beanInfo.setId(t[1]);
                }else if(t[0].equals("class"))
                {
                    beanInfo.setType(t[1]);
                }else beanInfo.addProperty(t[0],t[1]);

                line = br.readLine();
            }

        } catch (FileNotFoundException fileNotFoundException) {
            fileNotFoundException.printStackTrace();
        } catch (IOException ioException)
        {
            ioException.printStackTrace();
        }

        beans.put(beanInfo.getId(),beanInfo);
        return beans; //返回对象信息MAP
    }

package myspring;

public class XMLContext extends AbstractBeanFactory{
   /**
    * 上下文的构造方法
    * 该方法中指明注册读取器
    * 并在构造该方法时一次性的加载注册的对象
    * @param filePath
    */
   public XMLContext(String filePath) {
      super(filePath);
      this.setSourceReader(new XMLSourceReader()); //添加注册读取器,此处的注册读取器为XMLSourceReader
      this.registerBeans(); //加载注册的对象信息
   }  
   //  设置注册读取器
   @Override
   protected void setSourceReader(SourceReader reader) {
      this.reader = reader;
   }
}

package myspring;


import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.xpath.XPath;

import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * XML注册读取器
 * 该类继承了注册读取器接口,并模拟实现了读取注册对象信息的方法
 */
public class XMLSourceReader implements SourceReader {
   /**
    * 实现读取注册对象信息方法
    * 此处只是模拟测试使用,感兴趣的同学可以自己书写通过配置文件读取的实现
    */
   @Override
   public Map<String, BeanInfo> loadBeans(String filePath) {
      //初始化一个对象信息


      Map<String,BeanInfo> res = new HashMap<String,BeanInfo>(); //初始化一个对象信息Map



      try {
         SAXBuilder builder = new SAXBuilder();
         Document doc = null;
         doc = (Document) builder.build(new File("book.xml"));  XPath xpath = null;    xpath = XPath.newInstance("//bean");

         List beans = null;beans = xpath.selectNodes(doc);  Iterator i = beans.iterator();
         while (i.hasNext()) {
            BeanInfo beaninfo = new BeanInfo();
            Element bean = (Element) i.next();
            String id = bean.getAttributeValue("id");
            String cls = bean.getAttributeValue("class");
            beaninfo.setId(id);
            beaninfo.setType(cls);

            List<Element> list = bean.getChildren("property");
            for (Element el : list) {
               if (el.getAttribute("name") != null) {
                  beaninfo.addProperty(el.getAttributeValue("name"),el.getAttributeValue("value"));

               }

            }
            res.put(beaninfo.getId(),beaninfo);
         }

      } catch (JDOMException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      }
      return res;


   }
}

}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值