手写spring+springmvc+mybatis框架篇——springIOC容器

启动IOC容器为initBean方法。下面贴一下这两个类的关系图 

首先是applicationContext











其次是InitBean


    XmlApplicationContext :为解析xml文件的类,在spring源码中Resouce接口是用来解析多种文件格式的xml文件的接口,可能参数时inputStream,也可能是byteArray等,但是我们这里比较简单,直接用new File()传递xml文件。将读取到的对象用如下对象来保存

 Map<String, GenericBeanDefinition> beanDefinitionXmlMap = new ConcurrentHashMap<>(256);

spring也是这么做的。这个也就是我们说的容器。这里介绍一下我定义的这个beanDefinitionXmlMap 对象,beanDefinitionXmlMap 中的key为我们xml文件中的id,value为GenericBeanDefinition 对象,每一个GenericBeanDefinition 对象其实就代表一个beanGenericBeanDefinition 对象中,className就是对应的实体类的class的名字,而一个ChildBeanDefinition 对象就代表一个子元素(一个property:属性注入 或者一个constructor-arg元素:构造器注入)

package spring.factory;

import lombok.Data;
import java.util.List;
/**
 * Created by Xiao Liang on 2018/6/29.
 * 用来存放xml中注入的bean
 */
@Data
public class GenericBeanDefinition {
    /**
     * className和xml中的class对应
     */
    private String className;

    /**
     *  这是bean下面的属性集合
     */
    private List<ChildBeanDefinition> childBeanDefinitionList;
}
package spring.factory;

import lombok.Data;
/**
 * Created by Xiao Liang on 2018/6/27.
 */
@Data
public class ChildBeanDefinition {
    private String childrenType;// 这个是property或者constructor-arg类型
    private String attributeOne;//这个是第一个值
    private String attributeTwo;//这个是第二个值
}

    我定义的IOCRULES是用枚举来表示的,下面贴一下我定义的注入规则。

package spring.xmlRules;

import lombok.Getter;
/**
 *  @Author xiao liang
 * Ioc中xml配置的规则
 */
@Getter
public enum IocRules {
    BEAN_RULE("bean", "id", "class"),
    SNAN_RULE("component-scan", "base-package", "null"),
    /**
     * set注入的规则
     */
    SET_INJECT("property", "name", "value"),
    /**
     * 构造器注入的规则,使用构造器注入的时候必须指定顺序。
     */
    CONS_INJECT("constructor-arg", "value", "index");
    private String type;
    private String name;
    private String value;
    IocRules(String property, String name, String value) {
        this.type  = property;
        this.name  = name;
        this.value = value;
    }
}

XmlApplicationContext:。其实这里最好是只提供接口,然后让子类来实现。但是为了简化,方便起见,直接写在了这里。最关键的是标红的两个方法,一个是getBeanDefinitionMap,一个是getComponentList。第一个是解析xml文件并且将属性值保存在容器(beanDefinitionXmlMap对象)中,第二个是获取一个链表,这个链表是存在注解扫描的并且排列好实例化顺序后的链表。这个扫描+实例化顺序的核心功能在ScanUtil工具类中。这个类也是难点。

package spring.xml;

import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import spring.Utils.StringUtils;
import spring.Utils.scan.ScanUtil;
import spring.constants.Constants;
import spring.exception.XmlException;
import spring.factory.ChildBeanDefinition;
import spring.factory.GenericBeanDefinition;
import spring.xmlRules.IocRules;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * Created by Xiao Liang on 2018/6/28.
 * 封装解析xml的方法,模仿Ioc注入 BeanDefinition。实际注入的是GenericBeanDefinition
 */
@Slf4j
public class XmlApplicationContext {
    /**
     * @Description 将xml中的bean元素注入到容器中的方法
     *
     * @return 返回值是指定xml中的bean的容器
     */
    public  Map<String, GenericBeanDefinition> getBeanDefinitionMap(String contextConfigLocation) {
        Map<String, GenericBeanDefinition> beanDefinitionXmlMap = new ConcurrentHashMap<>(256);
        List<Element> elementsList = getElements(contextConfigLocation);
        //遍历每一个bean,注入beanDefinitionMap
        for (Element element :
                elementsList) {
            if (element.getName().equals("bean")){
                //声明一个bean的map,用来盛放当前bean子元素的容器
                GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
                List<ChildBeanDefinition> childBeanDefinitionList = new ArrayList<>();
                String beanId = element.attributeValue(IocRules.BEAN_RULE.getName());
                String beanClass = element.attributeValue(IocRules.BEAN_RULE.getValue());
                //保证子元素确实存在
                if (!StringUtils.isEmpty(beanId) && !StringUtils.isEmpty(beanClass)) {
                    //当前bean的className
                    genericBeanDefinition.setClassName(beanClass);
                    //当前bean的所有子元素
                    List<Element> elements = element.elements();
                    if (elements != null) {
                        for (Element childrenElement :
                                elements) {
                            //如果匹配set注入规则,则注入到容器
                            if (childrenElement.getName().equals(IocRules.SET_INJECT.getType())) {
                                ChildBeanDefinition childBeanDefinition = new ChildBeanDefinition();
                                childBeanDefinition.setChildrenType(IocRules.SET_INJECT.getType());
                                String name = IocRules.SET_INJECT.getName();
                                String value = IocRules.SET_INJECT.getValue();
                                setChildBeanDefinitionByType(childrenElement, childBeanDefinition, name, value, childBeanDefinitionList);
                            }
                            //如果匹配构造器注入规则,则注入到容器
                            else if (childrenElement.getName().equals(IocRules.CONS_INJECT.getType())) {
                                ChildBeanDefinition childBeanDefinition = new ChildBeanDefinition();
                                childBeanDefinition.setChildrenType(IocRules.CONS_INJECT.getType());
                                String name = IocRules.CONS_INJECT.getName();
                                String value = IocRules.CONS_INJECT.getValue();
                                setChildBeanDefinitionByType(childrenElement, childBeanDefinition, name, value, childBeanDefinitionList);
                            }
                        }
                    } else {
                        log.info("{}下面没有子元素", beanId);
                    }
                    genericBeanDefinition.setChildBeanDefinitionList(childBeanDefinitionList);
                    beanDefinitionXmlMap.put(beanId, genericBeanDefinition);
                }
            }
        }
        return beanDefinitionXmlMap;
    }
    /**
     * @Description 根据指定的xml,获得注解扫描的bean容器
     * @param contextConfigLocation
     * @return
     */
    public List<String> getComponentList(String contextConfigLocation){
        List<String> componentList = new ArrayList<>();
        List<Element> elementsList = getElements(contextConfigLocation);
        for (Element element :
                elementsList) {
            if (element.getName().equals(IocRules.SNAN_RULE.getType())) {
                String packageName = element.attributeValue(IocRules.SNAN_RULE.getName());
                componentList.addAll(resolveComponentList(packageName));
            }
        }
        return componentList;
    }
    /**
     * 根据要扫描的包名,返回有注解扫描的类
     * @param packageName
     * @return
     */
    public List<String> resolveComponentList(String packageName){
        if (StringUtils.isEmpty(packageName)){
            throw new XmlException("请正确设置"+IocRules.SNAN_RULE.getType()+"的属性");
        }
        List<String> componentList = new ArrayList<>();
        List<String> componentListAfter = ScanUtil.getComponentList(packageName);
        componentList.addAll(componentListAfter);
        return  componentList;
    }
    /**
     * 将每个bean的子元素注入容器
     *
     * @param element
     * @param childBeanDefinition
     * @param name
     * @param value
     * @param childBeanDefinitionList
     */
    private void setChildBeanDefinitionByType(Element element, ChildBeanDefinition childBeanDefinition, String name, String value,
                              List<ChildBeanDefinition> childBeanDefinitionList) {
        if (childBeanDefinition != null) {
            childBeanDefinition.setAttributeOne(element.attributeValue(name));
            childBeanDefinition.setAttributeTwo(element.attributeValue(value));
            childBeanDefinitionList.add(childBeanDefinition);
        } else {
            throw new XmlException("未按照格式配置xml文件或者暂不支持改配置属性");
        }
    }

    /**
     * 解析xml的工厂,根据路径名获取根元素下面的所有子元素
     * @param contextConfigLocation
     * @return
     */
    private List<Element> getElements(String contextConfigLocation) {
        // 创建saxReader对象
        SAXReader reader = new SAXReader();
        // 通过read方法读取一个文件 转换成Document对象
        Document document = null;
        String pathName = Constants.PATH + contextConfigLocation;
        try {
            document = reader.read(new File(pathName));
        } catch (DocumentException e) {
            log.error("文件没有找到,{}", pathName);
        }
        //获取根节点元素
        Element node = document.getRootElement();
        //获取所有的bean
        List<Element> elementsList = node.elements();
        return elementsList;
    }
}

难点一:ScanUtil类,这个类是spring容器的核心以及难点,我说一下过程,按照这个过程来看代码比较好理解。分为几个步骤

1 首先通过getClassName获取指定包下的所有类名的集合。

2 遍历每个类名的集合,用resolveComponent方法来扫描注解在类上的注解@MyController @MyService @MyRepository,

3 如果类上面有这些注解,则开始扫描此类上的属性上有没有@MyAutowired注解,如果存在@MyAutowired注解,则去属性对应的类上面递归上面的过程。直到类中没有@MyAutowired注解的时候。将类名添加到ComponentList链表中。在此过程中,如果类名是接口,并且有实现类的时候,添加到ComponentList链表中的是实现类的名字。为了完成此步骤,我设计了一个makeInterfaceAndImplMap方法用来绑定接口和其实现类,所以此框架目前只支持一个接口,一个实现类。

4 在第三步中,如果类上没有注解,并且是一个接口,此时默认是需要动态代理的接口,将此类名直接添加到ComponentList链表中。

package spring.Utils.scan;

import lombok.extern.slf4j.Slf4j;
import spring.Utils.AnnotationUtils;
import spring.Utils.ListAddUtils;
import spring.annotation.MyAutowired;
import spring.annotation.MyController;
import spring.annotation.MyRepository;
import spring.annotation.MyService;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
 * Created by Xiao Liang on 2018/6/27.
 * 扫描工具类(核心方法是getClassName和getComponentList)
 * 1 扫描包下的注解
 * 2 扫描包下的类名
 */
@Slf4j
public class ScanUtil {
    private static List<String> listClassName = new ArrayList<>();
    private static List<String> componentList = new ArrayList<>();
    private static Map<String, String> interfaceAndImplMap = new ConcurrentHashMap<>();
    /**
     * 扫描指定包下面的所有类名
     *
     * @param packageName,包名
     * @return 类名的集合,
     */
    public static List<String> getClassName(String packageName) {
        Enumeration<URL> urls = null;
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        String newPackageName = packageName.replace(".", "/");
        try {
            urls = contextClassLoader.getResources(newPackageName);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                File packageFile = new File(url.getPath());
                File[] files = packageFile.listFiles();
                if (files == null) {
                    break;
                }
                for (File file :
                        files) {
                    //如果是class,则添加到list中返回
                    if (file.getName().endsWith(".class")) {
                        String templeName = (packageName.replace("/", ".") + "." + file.getName());
                        String newTempleName = templeName.substring(0, templeName.lastIndexOf("."));
                        listClassName.add(newTempleName);
                    }
                    //如果是package,则继续遍历
                    else {
                        String nextPackageName = newPackageName + "." + file.getName();
                        getClassName(nextPackageName);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return listClassName;
    }
    /**
     * 返回 有注解的实例化顺序的链表
     */
    public static List<String> getComponentList(String packageName) {
        //获取所有类
        List<String> classNameList = getClassName(packageName);
        //将扫描的接口和其实现类,使用map对应上,模仿spring接口注入,复杂的原因是java不支持从接口获取实现类
        makeInterfaceAndImplMap(classNameList);
        for (String className :
                classNameList) {
            try {
                //实例化每个类
                resolveComponent(className);
            } catch (ClassNotFoundException e) {
                log.error("扫描注解的时候,{}没有找到", className);
                e.printStackTrace();
            }
        }
        return componentList;
    }
    /**
     * getComponentList();递归调用的子方法
     *
     * @param className
     */
    public static void resolveComponent(String className) throws ClassNotFoundException {
        Class<?> aClass = Class.forName(className);
        //在此处添加要识别的注解,也是每次扫描的顺序,最好遵循习惯
        addNewAnnotation(MyController.class, aClass);
        addNewAnnotation(MyService.class, aClass);
        addNewAnnotation(MyRepository.class, aClass);
    }
    public static <A extends Annotation> void addNewAnnotation(Class<A> annotationClass, Class<?> aClass) throws ClassNotFoundException {
        //如果类上有注解,判断属性上有没有注解
        if (!AnnotationUtils.isEmpty(aClass.getAnnotation(annotationClass))) {
            Field[] fields = aClass.getDeclaredFields();
            if (fields == null || fields.length == 0) {
                ListAddUtils.add(componentList, aClass.getName());
            } else {
                //跳出递归的语句,也就是最底层的类,如果所有属性没有@MyAutowired注解,则注入到链表中
                if (isEmptyAutowired(fields)) {
                    ListAddUtils.add(componentList, aClass.getName());
                } else {
                    //如果属性上有@MyAutowired,则继续递归
                    for (Field field :
                            fields) {
                        //递归具体的查找到底哪个属性上有@MyAutowired。
                        if (field.getAnnotation(MyAutowired.class) != null) {
                            //如果有则根据类名查找类,然后去对应的类中递归此过程
                            String newFieldName = field.getType().getName();
                            //如果是接口,则用其实现类注入
                            if (Class.forName(newFieldName).isInterface()) {
                                String nextName = convertInterfaceToImpl(newFieldName);
                                if (!componentList.contains(nextName)) {
                                    resolveComponent(nextName);
                                }
                            } else {
                                resolveComponent(newFieldName);
                            }
                        }
                    }
                    ListAddUtils.add(componentList, aClass.getName());
                }
            }
        }
        //如果是需要动态的代理注入的接口,加入到实例化的链表中
        else if (aClass.isInterface() && interfaceAndImplMap.get(aClass.getName()).equals("proxy")) {
            ListAddUtils.add(componentList, aClass.getName());
        }
    }
    /**
     * 判断一组属性里面有没有注解
     *
     * @param fields
     * @return
     */
    private static boolean isEmptyAutowired(Field[] fields) {
        for (Field field :
                fields) {
            if (!AnnotationUtils.isEmpty(field.getAnnotation(MyAutowired.class))) {
                return false;
            }
        }
        return true;
    }
    /**
     * 工具类,组装接口和实现类
     *
     * @param classNameList
     * @return
     */
    private static Map<String, String> makeInterfaceAndImplMap(List<String> classNameList) {
        Class<?> aClass = null;
        //interfaceNameList是所有接口类名的链表
        List<String> interfaceNameList = new ArrayList<>();
        //这个链表保存的是有实现类的接口的链表名,默认没有实现类的接口即为需要动态注的链表
        List<String> interfaceExist = new ArrayList<>();
        //循环类名,将类名注入到链表中
        for (String className :
                classNameList) {
            try {
                aClass = Class.forName(className);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            if (aClass.isInterface()) {
                interfaceNameList.add(aClass.getName());
            }
        }
        for (String className :
                classNameList) {
            Class<?> bClass = null;
            try {
                bClass = Class.forName(className);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            Class<?>[] interfaces = bClass.getInterfaces();
            //如果是接口的实现类
            if (interfaces != null && interfaces.length != 0) {
                for (String interfaceName :
                        interfaceNameList) {
                    for (Class<?> interfaceClass :
                            interfaces) {
                        //如果既有接口,也有实现类,则组成map
                        if (interfaceName.equals(interfaceClass.getName())) {
                            interfaceAndImplMap.put(interfaceName, className);
                            interfaceExist.add(interfaceName);
                        }
                    }
                }
            }
        }
        //需要动态代理注入的接口,在map中用value = proxy来识别
        interfaceNameList.removeAll(interfaceExist);
        if (interfaceNameList != null && interfaceNameList.size() > 0) {
            for (String spareInterfaceName :
                    interfaceNameList) {
                interfaceAndImplMap.put(spareInterfaceName, "proxy");
            }
            System.out.println("已经存在的" + interfaceNameList);
        }
        return null;
    }
    /**
     * 工具类:接口转换为实现类
     *
     * @param newFileName
     * @return
     */
    private static String convertInterfaceToImpl(String newFileName) {
        Set<Map.Entry<String, String>> entries = interfaceAndImplMap.entrySet();
        for (Map.Entry<String, String> entry :
                entries) {
            if (newFileName.equals(entry.getKey()) && !entry.getValue().equals("proxy")) {
                return entry.getValue();
            } else if (newFileName.equals(entry.getKey()) && entry.getValue().equals("proxy")) {
                return entry.getKey();
            }
        }
        return null;
    }
}

这样获得实例化顺序的链表ComponentList之后,开始实例化,也就是initBean这个类。实例化通过反射new Instance()方法获得对象,绑定后的对象用beanContainerMap来保存,但是这里存在一个问题,就是我们动态代理添加到ComponentList的是接口名称,接口名称不能直接new Instance(),所以这里标红处用的是动态代理实例化的对象,这部分代码是针对Mybatis的接口注入的。先不用管具体是什么意思,后续会讲解。

这里还要注意的是先解析xml还是先解析注解扫描的问题,spring是优先解析xml文件的bean,然后执行的注解注入。和这里的顺序一致。

package spring.factory;

import lombok.extern.slf4j.Slf4j;
import spring.Utils.AnnotationUtils;
import spring.annotation.MyAutowired;
import spring.constants.Constants;
import spring.mybatis.MySqlSession;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
 * Created by Xiao Liang on 2018/6/27.
 */
@Slf4j
public class InitBean extends BeanDefinition {
    //初始化后的bean容器 key为class名,value为实例化对象
    public Map<String, Object> beanContainerMap = new ConcurrentHashMap<>();
    /**
     * 初始化bean容器方法
     * 注意,扫描的bean会覆盖xml中配置的bean,spring也是这样,扫描的注入和装配都是在xml之后
     * MyAutowired暂时是根据名称装配和扫描
     */
    public void initBeans() {
        //初始化xml配置
        initXmlBeans(Constants.contextConfigLocation);
        initXmlBeans(Constants.springmvcConfigLocation);
        //初始化扫描注解的配置
        initAutowiredBeans(Constants.contextConfigLocation);
    }
    /**
     * 初始化xml中bean内容的方法
     */
    public void initXmlBeans(String contextConfigLocation) {
        ApplicationContext applicationContext = new ApplicationContext(contextConfigLocation);
        Class<?> aClass = null;
        //从容器中取出bean,用application的getbean方法依次加载bean
        Map<String, GenericBeanDefinition> beanDefinitionMap = super.getbeanDefinitionXmlMap(contextConfigLocation);
        Set<Map.Entry<String, GenericBeanDefinition>> entries = beanDefinitionMap.entrySet();
        for (Map.Entry<String, GenericBeanDefinition> entry :
                entries) {
            String beanId = entry.getKey();
            String className = entry.getValue().getClassName();
            try {
                aClass = Class.forName(className);

            } catch (ClassNotFoundException e) {
                log.error("xml中{}无法实例化", className);
                e.printStackTrace();
            }
            beanContainerMap.put(className, aClass.cast(applicationContext.getBean(beanId)));
        }
    }
    /**
     * 将所有的componentList(也就是加注解的类)里面的bean实例化
     *
     * @return
     */
    public void initAutowiredBeans(String contextConfigLocation) {
        List<String> componentList = super.getComponentList(contextConfigLocation);
        System.out.println("实例化的顺序" + componentList);
        //扫描到有注解的类,初始化类的名单
        for (String className :
                componentList) {
            //将每一个类初始化
            try {
                initClass(className);
            } catch (ClassNotFoundException e) {
                log.error("{}没有找到", className);
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 初始化每一个类的方法,初始化的时候由于spring要实现使用接口注入,所以比较麻烦
     * 需要根据类名来判断是否有接口,然后在将接口名和实现类对应上装配到容器中
     *
     * @param className
     */
    public void initClass(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class<?> aClass = Class.forName(className);
        //先判断这个类有没有接口,如果有接口,将接口装配
        Class<?>[] interfaces = aClass.getInterfaces();
       //如果类是接口,注入的对象是动态代理的对象
        if (aClass.isInterface()){
            MySqlSession mySqlSession = new MySqlSession();
            beanContainerMap.put(aClass.getName(),mySqlSession.getMapper(aClass, Constants.mybatisConfigLocation));
        }
       //如果不是接口的实现类,也就是controller层
        else if (interfaces == null || interfaces.length == 0) {
            noInterfaceInit(className, className);
        }
        else {
            for (Class<?> interfaceClass :
                    interfaces) {
                boolean flag = isExistInContainer(className);
                //容器中如果有,则直接使用这个对象进行装配
                if (flag) {
                    beanContainerMap.put(interfaceClass.getName(), aClass.newInstance());
                } else {
                    //如果容器中没有,则先实例化实现类,然后再装配到容器中
                    noInterfaceInit(className, interfaceClass.getName());
                }
            }
        }
    }
    /**
     * @param className
     * @param interfaceName
     */
    public void noInterfaceInit(String className, String interfaceName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class<?> aClass = Class.forName(className);
        //bean实例化
        System.out.println("实例化的名字"+aClass.getName());
        Object object = aClass.newInstance();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field :
                declaredFields) {
            //如果属性上有MyAutowired注解,则先将属性注入进去
            if (!AnnotationUtils.isEmpty(field.getAnnotation(MyAutowired.class))) {
                //System.out.println("发现注解");
                //设置私有属性可见
                field.setAccessible(true);
                //如果有注解,在实例化链表里面搜寻类名
                Set<Map.Entry<String, Object>> entries = beanContainerMap.entrySet();
                for (Map.Entry<String, Object> entry :
                        entries) {
                    String type = field.getType().getName();
                    if (entry.getKey().equals(type)){
                        field.set(object, entry.getValue());
                    }
                }
            }
        }
        beanContainerMap.put(interfaceName, object);
    }
    /**
     * 属于工具类,不是很重要
     * 在实例化该类之前先判断该类在容器中是否存在
     *
     * @param className
     * @return
     */
    public boolean isExistInContainer(String className) {
        Set<Map.Entry<String, Object>> entries = beanContainerMap.entrySet();
        if (entries != null) {
            for (Map.Entry<String, Object> map :
                    entries) {
                if (map.getKey().equals(className)) {
                    return true;
                } else {
                    return false;
                }
            }
        }
        return false;
    }
}

至此,spring容器的开发暂时告一段落,下一篇介绍springmvc的实现。

我将此项目上传到了github,需要的童鞋可以自行下载。

https://github.com/836219171/MySSM


猜你喜欢

转载自blog.csdn.net/qq_27631217/article/details/80975621