自己手写Spring容器实现对对象的管理

手写模拟Spring IOC 基本原理

①先了解IOC是干嘛的

IOC定义

Spring IoC负责创建对象、管理对象(通过依赖注入(DI)、装配对象、配置对象,并且管理这些对象的整个生命周期。

简单说就是将我们的对象都交由一个容器来管理.那我们为什么要将对象交由对象来管理,我们自己管理有什么问题吗?接下来我们来看看不使用容器管理会有哪些问题

@Test
public void serviceTest() {
    System.out.println("我自己创建对象");
    UserService service = new UserServiceImpl();
    System.out.println(service.userLogin());
}

问题: 代码耦合度高,会出现改一处,处处改的问题

思考: 能否使用工厂模式来解决这个问题?

@Test
public void simpleTest() {
    System.out.println("使用了简单工厂");
    UserService s = (UserService) ServiceFactory.getInstance("UserService");
    System.out.println(s.userLogin());
}

总结: 使用了简单工厂之后确实为我们降低了一些耦合度,但是我们仔细看看其实还有存在问题,或者是一些比较繁琐的操作.

问题: 每次通过工厂获取对象,工厂都需要事先知道key也就是上述代码的"UserService",当我们的业务足够庞大的时候,我们会发现这个东西是有可能写错的,并且IDEA并不会提示我们写错了.而且每次都要强转类型很是繁琐.当然最主要的问题是,如果我们项目已经编译打包之后我们需要去改动业务,那我们还得重新编译打包,这样很不人性化,也就是我们通常说的维护困难.

思考: 以上问题如何解决?

思路: 既然是维护困难,那相信大家在使用数据库的时候其实就已经遇到这个问题的,那就是我们的数据库连接配置,为了方便配置我们采用了配置文件.

总结: 其实Spring的容器也只是帮助我们搭建好了以上的工厂而已,那既然我们知道了原理,我们能否自己来写一个呢?

拓展

  • 配置文件类型
    1. properties
    2. xml
    3. json
    4. yaml

②自己实现Spring容器管理对象

我们模仿mybatis也采用xml来试着模拟Spring的容器

Bean标签步骤解析

  • 创建xml文件

  • 读取xml文件

  • 导入dom4j坐标,解析xml文件

    • 读取bean标签中的id=UserService,class=com.achao.service.impl.UserServiceImpl

    • 读取到class中的值,利用反射将其实例化

    • id作为key,class作为值,添加到一个map中

  • 通过工厂拿对象

A. 创建xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <!--bean的声明-->
    <bean id="UserService" class="com.achao.service.impl.UserServiceImpl"/>
</beans>
B. 读取xml文件
/**
* 解析xml文件,返回一个Document
* @param configName
* @return
*/
private static Document parse(String configName) {
    SAXReader reader = new SAXReader();
    try {
        document = reader.read(configName);
    } catch (DocumentException e) {
        e.printStackTrace();
    }
    return document;
}
C. 解析xml文件
/**
 * 解析<beans></beans>标签
 */
private static void parsBeansElement() {
    List<Node> nodes = document.selectNodes("//beans/bean");
    // 遍历拿到所有的bean标签,将标签转换成元素,从元素中获取标签值
    System.out.println("nodes集合的长度: " + nodes.size());
    for (Node node : nodes) {
        Element element = (Element) node;
        String id = element.attributeValue("id");
        String className = element.attributeValue("class");
        map.put(id, getInstance(className, false));
    }
}
D. 工厂帮我们拿对象
/**
 * 使用泛型封装简单工厂
 * @param tClass
 * @param <T>
 * @return
 */
public static <T> T getBean(Class<T> tClass) {
    String className = tClass.getSimpleName();
    System.out.println("通过反射获取的类名: "+className);
    Object o = map.get(className);
    if (o == null) {
        System.err.println("工厂没有注册该实现类...");
        return null;
    } else {
        return (T) o;
    }
}

Package标签步骤解析

  • 读取xml文件(同上)

  • 解析xml文件

    • 解析,拿到包的路径(“com.achao.service”)
    • 根据包路径拿到io流读取的文件路径
    • 递归文件,如果是文件夹再次调用自己,并且包名加上自己
    • 利用反射将注解的值作为key,实例作为value存入map中
  • 通过工厂拿对象

A. 创建xml文件
<?xml version="1.0" encoding="UTF-8"?>

<beans>
    <!--bean的声明-->
    <package name = "com.achao.service"/>
</beans>
B. 读取xml文件

同上

C. 解析xml文件
/**
 * 解析<package></package>标签
 */
private static void parsPageElement() {
    // 1.读取配置文件
    List<Node> nodes = document.selectNodes("//beans/package");
    for (Node node : nodes) {
        Element element = (Element) node;
        // 2.解析,拿到包的路径("com.achao.service")
        String packageName = element.attributeValue("name");
        // 3.路径转换格式("")
        String path = packagePath(packageName);
        System.out.println("io流要的路径: " + path);
        // 4.根据第二第三步获取的路径和包名,拿到["com.achao.service.AdminService",...]
        List<String> classPathFrom = getClassPathFrom(path, packageName);
        for (String s : classPathFrom) {
            System.out.println(s);
            // 5.通过注解和反射,将其一一对应加入map中
            getInstance(s, true);
        }
    }
}

递归拿包

/**
 * 获取文件下的所有文件
 *
 * @param file
 * @return
 */
public static void getAllFile(File file, String packageName) {
    File[] listFiles = file.listFiles();
    for (File listFile : listFiles) {
        if (listFile.isDirectory()) {
            String s = packageName +"."+ listFile.getName();
            getAllFile(listFile, s);
        } else {
            String name = listFile.getName();
            int i = name.lastIndexOf(".");
            if (name.substring(i).equals(".java")) {
                String rs = packageName + "." + name;
                String classPath = rs.substring(0, rs.length() - 5);
                classPaths.add(classPath);
            }
        }
    }

}
D. 工厂帮我们拿对象
/**
 * 使用泛型封装简单工厂
 * @param tClass
 * @param <T>
 * @return
 */
public static <T> T getBean(Class<T> tClass) {
    String className = tClass.getSimpleName();
    System.out.println("通过反射获取的类名: "+className);
    Object o = map.get(className);
    if (o == null) {
        System.err.println("工厂没有注册该实现类...");
        return null;
    } else {
        return (T) o;
    }
}

以上就是我个人对Spring容器的理解,源码详见我的Git

源码连接

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值