Spring框架 之 反射机制简单实现基于XML标签的IOC与DI

Spring容器基于控制反转帮助开发者实现了对象的创建过程与调用过程解耦,使得开发更加便捷,并且便于代码维护。而DI则是IOC实现的核心,由于我们将类的实例化交由容器管理,实例与实例之间难免会存在依赖关系,这时依赖注入技术就将发挥其价值。
本章我们使用XML配置文件的方式,实现一个简单的能够实现控制反转和依赖注入的容器。代码较长,并且未经过整理重构,仅作为感兴趣的开发者朋友实现容器功能的简单参考,如果有问题感谢留言指出,博主日后会将代码整理并添加注释。
package runnable;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import annotation.Mark;
import test.packages.son02.User02;

/**

  • 简单的容器实现
    */
    public class Util {

    private Element root;
    private List<Element> beans;
    private static Map<String, Object> beansMap=new HashMap<String, Object>();

    public static void main(String[] args) throws Exception{
    Util util=new Util("utils.xml");

     User02 u02=util.getBean("user02", User02.class);  
     System.out.println(u02);  
       
     System.out.println(beansMap);  
     util.destroy();  
    

    }

    public Util() {
    super();
    }

    /*

    • @param file_Xml 配置文件的路径及名称
      */
      public Util(String file_Xml) {
      super();
      loadXml(file_Xml);
      init();
      }

    private void loadXml(String file_Xml) {
    SAXReader reader=new SAXReader();
    Document doc;
    try {
    doc = reader.read(getClass().getClassLoader().getResource(file_Xml));
    root=doc.getRootElement();
    beans=root.elements();
    } catch (DocumentException e) {
    e.printStackTrace();
    }
    }

    /*

    • 初始化函数,会直接加载xml配置文件
      */
      private void init(){
      this.createInstanceByXml();
      }

    /*

    • 销毁方法,调用此方法会执行所有在配置文件中注册过的销毁方法
      */
      public void destroy(){
      for(Element bean:beans) {
      String destroy_methodName=bean.attributeValue("destroy-method");
      if(destroy_methodName!=null) {
      String classId=bean.attributeValue("id");
      if(beansMap.containsKey(bean.attributeValue("id"))) {
      Class cls;
      try {
      cls = Class.forName(bean.attributeValue("class"));
      Method destroy_method=cls.getMethod(destroy_methodName);
      destroy_method.invoke(getBean(classId, cls));
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      }
      }
      beansMap.clear();
      beansMap=null;
      }

    /*

    • 获取实例的方法
      */
      public Object getBean(String classId){
      if(beansMap.containsKey(classId)){
      return beansMap.get(classId);
      }else{
      for(Element bean:beans){
      if(classId.equals(bean.attributeValue("id"))){
      Object obj=createInstanceByRef(classId);
      addBeanToMap(bean, obj);
      return obj;
      }
      }
      }
      return null;
      }

    /*

    • 获取携带类型信息的实例
      */
      public <T>T getBean(String classId,Class<T> cls){
      return (T)this.getBean(classId);
      }

    /*

    • 通过读取xml文件创建对象
      */
      private void createInstanceByXml(){
      boolean default_lazy_init=Boolean.parseBoolean(root.attributeValue("default-lazy-init"));
      for(Element bean:beans) {
      boolean lazy_init;
      if(bean.attributeValue("lazy-init")==null) {
      lazy_init=default_lazy_init;
      }else {
      lazy_init=Boolean.parseBoolean(bean.attributeValue("lazy-init"));
      }
      if(!lazy_init&&!"prototype".equals(bean.attributeValue("scope"))
      &&bean.attributeValue("class")!=null) {
      createInstance(bean);
      }
      }
      }

    /*

    • 通过property标签为实例赋值
      */
      private void setValueByProperties(Element bean, Object obj) {
      List<Element> properties=bean.elements("property");
      for(Element property:properties){
      String parameterName=property.attributeValue("name");
      String setMethodName="set"+parameterName.substring(0,1).toUpperCase()+parameterName.substring(1);
      System.out.println(setMethodName);
      String parameterValue=property.attributeValue("value");
      String parameterRef=property.attributeValue("ref");
      Class cls=obj.getClass();
      Method[] methods=cls.getDeclaredMethods();
      for(Method method:methods){
      if(setMethodName.equals(method.getName())){
      try {
      if(parameterValue!=null){
      method.invoke(obj,parameterValue);
      }else{
      if(beansMap.containsKey(parameterRef)){
      method.invoke(obj, beansMap.get(parameterRef));
      }else{
      Object obj_ref=createInstanceByRef(parameterRef);
      addBeanToMap(bean, obj_ref);
      method.invoke(obj, obj_ref);
      }
      }
      } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
      e.printStackTrace();
      }
      }
      }
      }

    }

    /*

    • 通过引用创建对象
      */
      private Object createInstanceByRef(String ref){
      for(Element bean:beans) {
      if(ref.equals(bean.attributeValue("id"))){
      try {
      Object obj=null;
      if(bean.elements("constructor-arg").size()==0) {
      obj=Class.forName(bean.attributeValue("class")).newInstance();
      setValueByProperties(bean,obj);
      }else {
      obj=setValueByConstructor(bean);
      setValueByProperties(bean,obj);
      }
      addBeanToMap(bean, obj);
      return obj;
      } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
      e.printStackTrace();
      }
      }
      }
      return null;
      }

    /*

    • 通过bean对象创建实例
      */
      private Object createInstance(Element bean){
      try {
      Object obj=null;
      if(bean.elements("constructor-arg").size()==0) {
      obj=Class.forName(bean.attributeValue("class")).newInstance();
      setValueByProperties(bean,obj);
      }else {
      obj=setValueByConstructor(bean);
      setValueByProperties(bean,obj);
      }
      addBeanToMap(bean, obj);
      return obj;
      } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
      e.printStackTrace();
      }
      return null;
      }

    /*

    • 通过扫描包的形式创建对象,可以通过createAll参数选择是否创建包下的所有类的实例,此处不同于Spring容器
      */
      private void createPackage(String packageName,Boolean createAll){
      String packagePath=packageName.replaceAll("\.", "/");
      URL packageUrl=this.getClass().getClassLoader().getResource(packagePath);
      File pkgFile=new File(packageUrl.getPath());
      File[] files_class=pkgFile.listFiles(new FileFilter() {
      @Override
      public boolean accept(File file) {
      if(file.getName().endsWith(".class")){
      return true;
      }else if(file.isDirectory()){
      try {
      createPackage(packageName+"."+file.getName(),createAll);
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      return false;
      }
      });
      for(File file:files_class){
      String className=file.getName().substring(0,file.getName().lastIndexOf("."));
      Object obj;
      if(createAll) {
      //创建包下的所有类的实例
      try {
      obj = Class.forName(packageName+"."+className).newInstance();
      String classId=className.substring(0,1).toLowerCase()+className.substring(1);
      beansMap.put(classId, obj);
      } catch (Exception e) {
      e.printStackTrace();
      }
      }else {
      //只创建有Mark注解的类的实例
      try {
      Class<?> cls=Class.forName(packageName+"."+className);
      if(cls.isAnnotationPresent(Mark.class)){
      obj=cls.newInstance();
      String classId=className.substring(0,1).toLowerCase()+className.substring(1);
      beansMap.put(classId,obj);
      }
      } catch (Exception e) {
      e.printStackTrace();
      }
      }

      }
      }

    /*

    • 不指定是否全部创建时,只创建被标记的
      */
      private void createPackage(String packageName) {
      this.createPackage(packageName, false);
      }

    /*

    • 使用有参构造器创建对象时调用此方法
      */
      private Object setValueByConstructor(Element bean) {
      try {
      //加载该类
      Class<?> cls=Class.forName(bean.attributeValue("class"));
      //过的该bean标签下的constructor-atg标签
      List<Element> con_args=bean.elements("constructor-arg");
      //对constructor-arg标签进行排序,结果保存为Object类型
      List<Object> con_arg_values=new ArrayList<>();
      for(int i=0;i<con_args.size();i++){
      con_arg_values.add(null);
      }
      for(Element con_arg:con_args) {
      String value=con_arg.attributeValue("value");
      if(con_arg.attributeValue("index")!=null) {
      int index=Integer.parseInt(con_arg.attributeValue("index"));
      if(value!=null)
      con_arg_values.set(index,value);
      else{
      String ref=con_arg.attributeValue("ref");
      if(beansMap.containsKey(ref)){
      con_arg_values.set(index, getBean(ref));
      }else{
      createInstanceByRef(ref);
      con_arg_values.set(index, getBean(ref));
      }
      }
      }else {
      for(int i=0;i<con_args.size();i++){
      if(con_arg_values.get(i)==null) {
      if(value!=null){
      con_arg_values.set(i,value);
      }else{
      String ref=con_arg.attributeValue("ref");
      if(beansMap.containsKey(ref)){
      con_arg_values.set(i, getBean(ref));
      }else{
      Object obj=createInstanceByRef(ref);
      con_arg_values.set(i, obj);
      }
      }
      break;
      }
      }
      }
      }
      //获得该类的所有构造器
      Constructor<?>[] cons=cls.getDeclaredConstructors();
      for(Constructor<?> con:cons) {
      //按照constructor-arg标签的顺序,尝试执行每一个形参个数相同的构造器
      if(con.getParameterCount()==con_args.size()) {
      List<Object> parametersList=new ArrayList<>();
      Class<?>[] parameterTypes=con.getParameterTypes();
      for(int i=0;i<parameterTypes.length;i++) {
      addToParametersList(con_arg_values.get(i),parameterTypes[i],parametersList);
      }
      try {
      return con.newInstance(parametersList.toArray());
      } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
      | InvocationTargetException e) {
      e.printStackTrace();
      }
      }
      }
      } catch (ClassNotFoundException e) {
      e.printStackTrace();
      }
      return null;
      }

    /*

    • 完成类型转换并将参数的值加入实参列表
      */
      private void addToParametersList(Object obj,Class cls,List<Object> parametersList){
      String parameterType=cls.getName();
      String value=String.valueOf(obj);
      if("java.lang.String".equals(parameterType)) {
      parametersList.add(value);
      }else if("int".equals(parameterType)) {
      parametersList.add(Integer.parseInt(value));
      }else if("short".equals(parameterType)) {
      parametersList.add(Short.parseShort(value));
      }else if("long".equals(parameterType)) {
      parametersList.add(Long.parseLong(value));
      }else if("double".equals(parameterType)) {
      parametersList.add(Double.parseDouble(value));
      }else if("float".equals(parameterType)) {
      parametersList.add(Float.parseFloat(value));
      }else if("char".equals(parameterType)&&value.length()==1) {
      parametersList.add(value.charAt(0));
      }else if("boolean".equals(parameterType)) {
      parametersList.add(Boolean.parseBoolean(value));
      }else{
      parametersList.add(obj);
      }
      }

    /*

    • 尝试将新创建的实例加入Map中(这里我们只考虑单例与多例两种作用域)
      */
      private void addBeanToMap(Element bean,Object obj){
      if(!"prototype".equals(bean.attributeValue("scope"))){
      beansMap.put(bean.attributeValue("id"), obj);
      }
      }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343