手写一个spring框架简单IOC 基于xml方式和注解方式(源码地址)

Ioc是什么?

IOC的含义和理解点击这里

前提了解

IOC他获取到了对象他一定有一个地方去存储那个对象 beanName 和 Object 这样键值对存储 所以我们选择用map<String,Object>
来存储 ,我们这里解析xml使用的是dom4j 让我们一起了解如何解析xml和实例化吧

思路

通过解析xml拿到类的名字通过反射创建类 创建好类放入map中

主要类


import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

/*
 * 第二个版本 添加byTaype 和byNmae类型自动装配
 * */
public class YonliBeanFactory {
    Map<String,Object> map = new HashMap<String,Object>();

    public YonliBeanFactory(String xml){
        parseXml(xml);
    }


    private void parseXml(String xml)  {
        File file = new File(this.getClass().getResource("/").getPath()+"//"+xml); //获取xml路径
        SAXReader reader = new SAXReader();                             //dom4j 对象


        try {

            Document document  =  reader.read(file);                       //dom4j 读取xml 获取documen对象
            Element elementRoot = document.getRootElement();               //得到最外面的标签 <beans>
           Attribute aDefault= elementRoot.attribute("default");
            boolean flag=false;
            if (aDefault!=null){
                flag=true;
            }
            for (Iterator<Element> itFirlst = elementRoot.elementIterator();itFirlst.hasNext();){   //便利beans下面的标签 <bean>
                Object object = null;
                Element elementFirstChil  = itFirlst.next();
                Attribute idAttribute    =    elementFirstChil.attribute("id");              //得到bean标签 的属性对象
                Attribute classAttribute =    elementFirstChil.attribute("class");
                String idvalue   = idAttribute.getValue();                                     //获取属性对象的值
                String classValue   = classAttribute.getValue();
                Class clazz  =  Class.forName(classValue);                                    //加载类
                                                             //新建 calss 加载的类
                for (Iterator<Element> itSecond=elementFirstChil.elementIterator();itSecond.hasNext();){     //判断<bean> 下面是不是有子标签
                    Element itSecondChil  = itSecond.next();
                    if (itSecondChil.getName().equals("property")){         //如果有子标签名字为 property 表示这是一个 注入属性
                        //通过set get 注入
                        object= clazz.newInstance();
                        String   ref    =  itSecondChil.attributeValue("ref");  //获取注入属性值value
                        String   name   =  itSecondChil.attributeValue("name");  //获取注入属性的名字
                        Object  injetObject =  map.get(ref);                        //获取之前注入的bean
                        Field field =  clazz.getDeclaredField(name);
                        field.set(object,injetObject);  //调用方法赋值               //反射复制


                    }else{
                        //证明有特殊构造器
                        String refVlaue = elementFirstChil.attribute("ref").getValue();
                        Object injetObject= map.get(refVlaue) ;
                        Class injectObjectClazz = injetObject.getClass();
                        Constructor constructor = clazz.getConstructor(injectObjectClazz.getInterfaces()[0]);
                        object = constructor.newInstance(injetObject);

                    };



                }

                if (object == null){//没有赋值证明没有子标签
                    if (flag){   //证明有这个标签
                       if (aDefault.getValue().equals("byType")){
                           Field fields[] = clazz.getDeclaredFields();
                           for (Field field: fields) {
                               //得到屬性的類型,比如String aa那麽這裏的field.getType()=String.class
                               Class injectObjectClazz = field.getType();
                               /**
                                * 由於是bytype 所以需要遍历map当中的所有对象
                                * 判断对象的类型是不是和这个injectObjectClazz相同
                                */
                               int count = 0;
                               Object injectObject = null;
                               for (String key : map.keySet()) {
                                   Class temp = map.get(key).getClass().getInterfaces()[0];
                                   if (temp.getName().equals(injectObjectClazz.getName())) {
                                       injectObject = map.get(key);
                                       //记录找到一个,因为可能找到多个count
                                       count++;
                                   }
                               }

                               if (count > 1) {
                                   throw new YongliSpringException("需要一个对象,但是找到了两个对象");
                               } else {
                                   object = clazz.newInstance();
                                   field.setAccessible(true);
                                   field.set(object, injectObject);
                               }


                           }

                       }
                       if(aDefault.getValue().equals("byName")){ //装配是根据名字
                           object = clazz.newInstance();
                           Field fields[] = clazz.getDeclaredFields();
                           for (Field field: fields) {
                               String fieldName = field.getName();
                               for (String key : map.keySet()) {
                                   if (key.equals(fieldName)){
                                       field.set(object,map.get(key));
                                   }

                               }



                           }


                       }

                    }

                }



                if(object==null){//沒有子標簽
                    object = clazz.newInstance();
                }
                map.put(idvalue,object);  //将本次循环的object放到map中
            }

        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public  Object getBean(String beanName){

        return map.get(beanName);
    }


}

xml配置

<?xml version="1.0" encoding="UTF-8"?>

<!--
    1、哪些类需要我来关联
    2、怎么告诉我这些类(写bean)
    3、怎么维护依赖关系(settr、constr)
    4、怎么体现setter或者constr
 -->
<beans default="byName">
    <bean id="dao" class="com.yongli.dao.TestDaoImpl"></bean>

    <bean id="service" class="com.yongli.service.UserServiceImpl">

    </bean>


</beans>

xml需要注入的类

package com.yongli.dao;

public class TestDaoImpl implements TestDao {

    public void query() {
        System.out.println("test");
    }
}

package com.yongli.service;


import com.yongli.dao.TestDao;


public class UserServiceImpl implements UserService {


   public TestDao dao;


    public void find() {
        System.out.println("service");
        dao.query();
    }

   public void setDao(TestDao dao) {
        this.dao = dao;
    }
}

测试类

  public static void main(String[] args) throws Exception {

            YonliBeanFactory yonliBeanFactory = new YonliBeanFactory("spring.xml");

            TestDao testDao = (TestDao) yonliBeanFactory.getBean("dao");

            testDao.query();


        }

结果

在这里插入图片描述

基于注解注入

思路 通过传入的路径com.xx.xx 转化为路径 com\xx\xx 扫描 target 文件 来获得类的名字 通过反射拿到类对象 查看是否有指定的注解如果有 创建类实例 并且加入map

主要类

package com.org.springAnnotation;

import com.org.Yongli;

import java.io.File;

public class AnnotationConfigApplicationContext {

    public void scan(String basePackage){
        //获取这个类的路径
        String rootPath = this.getClass().getResource("/").getPath();
        //把得到的xx. 改成路径的 xxx/
        String  basePackagePath =basePackage.replaceAll("\\.","\\\\");
        //
        File file = new File(rootPath+"//"+basePackagePath);
        //返回文件下的文件名字
        String names[]=file.list();
        for (String name : names) {
            //移出.class 获取文件名字
            name=name.replaceAll(".class","");
            try {
                //拼接加载创建出来class
                Class clazz =  Class.forName(basePackage+"."+name);
                //判斷是否是屬於@[email protected]
                if(clazz.isAnnotationPresent(Yongli.class)){
                    //这里本来是存到map里面 spirng里面是存到数据结构里面没有写了
                    Yongli yongli = (Yongli) clazz.getAnnotation(Yongli.class);
                    System.out.println(clazz.newInstance());

                }

            } catch (Exception e) {
                //如果创建失败 到这里重新开始下一次循环
                e.printStackTrace();
            }
        }

    }
}

注解

package com.org;


import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
//注解生命周期
@Retention(RetentionPolicy.RUNTIME)
public @interface Yongli {
}

测试类

    public static void main(String[]args){

        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
        annotationConfigApplicationContext.scan("com.yongli.dao");

    }

结果图

在这里插入图片描述

结束语

spring基于xml 的是第二版增加了功能看起来不是那么容易 可以看第一版思路差不多 只是变了部分 注解版实在不想写了累了
源码地址 dome源码地址点击这里 有时候不知道为了什么去做这些只知道做下去迷茫~

猜你喜欢

转载自blog.csdn.net/weixin_43979902/article/details/120531780