您当前的位置: 首页 >  spring

源码分析系列教程(04) - 手写SpringIOC

杨林伟 发布时间:2019-11-01 18:31:59 ,浏览量:2

代码已上传到GitHub,有兴趣的同学可以下载来看看:https://github.com/ylw-github/Java-CodeAnalysis-Demo

在上一节,我们知道SpringIOC的原理是使用了XML技术+发射技术,本文主要讲解如何手写SpringIOC。

步骤一:添加用到的maven依赖
 
        
            org.springframework
            spring-core
            4.3.7.RELEASE
        
        
            org.springframework
            spring-beans
            4.3.7.RELEASE
        
        
            org.springframework
            spring-context
            4.3.7.RELEASE
        

        
        
            org.springframework
            spring-jdbc
            4.3.7.RELEASE
        
        
            org.springframework
            spring-tx
            4.3.7.RELEASE
        


        
        
            org.aspectj
            aspectjrt
            1.9.1
        

        
        
            com.mchange
            c3p0
            0.9.5.2
        

        
            mysql
            mysql-connector-java
            5.1.25
        

        
        
            dom4j
            dom4j
            1.6.1
        
        
        
            commons-lang
            commons-lang
            2.6
        

        
        
            org.springframework
            spring-test
            4.2.5.RELEASE
            test
        

        
            junit
            junit
            4.12
            test
        

步骤二:编写XML解析文件

先看看ClassPathXmlApplicationContext源码: 在这里插入图片描述 我们会发现ClassPathXmlApplicationContext 继承了 AbstractXmlApplicationContext,一路追踪下去,会发现集成关系是这样的(继承了很多层):

ClassPathXmlApplicationContext 

extends -> AbstractXmlApplicationContext 

extends ->AbstractRefreshableConfigApplicationContext 

extends ->AbstractRefreshableApplicationContext

extends ->AbstractApplicationContext

extends ->DefaultResourceLoader

下面来简单的写一个ClassPathXmlApplicationContext:

package com.ylw.ioc;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.util.StringUtils;

import java.io.InputStream;
import java.util.List;

public class ClassPathXmlApplicationContext {
    // xml路径地址
    private String xmlPath;

    public ClassPathXmlApplicationContext(String xmlPath) {
        this.xmlPath = xmlPath;
    }

    public Object getBean(String beanId) throws Exception {
        // 1. 读取配置文件
        List elements = readerXml();
        if (elements == null) {
            throw new Exception("该配置文件没有子元素");
        }
        // 2. 使用beanId查找对应的class地址
        String beanClass = findXmlByIDClass(elements, beanId);
        if (StringUtils.isEmpty(beanClass)) {
            throw new Exception("未找到对应的class地址");
        }
        // 3. 使用反射机制初始化,对象
        Class forName = Class.forName(beanClass);
        return forName.newInstance();
    }

    // 读取配置文件信息
    public List readerXml() throws DocumentException {
        SAXReader saxReader = new SAXReader();
        if (StringUtils.isEmpty(xmlPath)) {
            new Exception("xml路径为空...");
        }
        Document read = saxReader.read(getClassXmlInputStream(xmlPath));
        // 获取根节点信息
        Element rootElement = read.getRootElement();
        // 获取子节点
        List elements = rootElement.elements();
        if (elements == null || elements.isEmpty()) {
            return null;
        }
        return elements;
    }

    // 使用beanid查找该Class地址
    public String findXmlByIDClass(List elements, String beanId) throws Exception {
        for (Element element : elements) {
            // 读取节点上是否有value
            String beanIdValue = element.attributeValue("id");
            if (beanIdValue == null) {
                throw new Exception("使用该beanId为查找到元素");
            }
            if (!beanIdValue.equals(beanId)) {
                continue;
            }
            // 获取Class地址属性
            String classPath = element.attributeValue("class");
            if (!StringUtils.isEmpty(classPath)) {
                return classPath;
            }
        }
        return null;
    }

    // 读取xml配置文件
    public InputStream getClassXmlInputStream(String xmlPath) {
        InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream(xmlPath);
        return resourceAsStream;
    }
}

步骤三:编写ClassPathXmlApplicationContext 注解

1.通用的反射工具类

package com.ylw.ioc.util;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ClassUtil {
    /**
     * 取得某个接口下所有实现这个接口的类
     */
    public static List getAllClassByInterface(Class c) {
        List returnClassList = null;

        if (c.isInterface()) {
            // 获取当前的包名
            String packageName = c.getPackage().getName();
            // 获取当前包下以及子包下所以的类
            List allClass = getClasses(packageName);
            if (allClass != null) {
                returnClassList = new ArrayList();
                for (Class classes : allClass) {
                    // 判断是否是同一个接口
                    if (c.isAssignableFrom(classes)) {
                        // 本身不加入进去
                        if (!c.equals(classes)) {
                            returnClassList.add(classes);
                        }
                    }
                }
            }
        }

        return returnClassList;
    }

    /*
     * 取得某一类所在包的所有类名 不含迭代
     */
    public static String[] getPackageAllClassName(String classLocation, String packageName) {
        // 将packageName分解
        String[] packagePathSplit = packageName.split("[.]");
        String realClassLocation = classLocation;
        int packageLength = packagePathSplit.length;
        for (int i = 0; i             
关注
打赏
1688896170
查看更多评论
0.0545s