手写IOC容器

Java
234
0
0
2024-02-26

实践

项目目录结构

项目下载

https://github.com/cbeann/Demoo/tree/master/ioc-demo

pom

<!--解析XML的依赖-->
        <!-- https://mvnrepository.com/artifact/org.jdom/jdom -->
        <dependency>
            <groupId>org.jdom</groupId>
            <artifactId>jdom</artifactId>
            <version>2.0.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/jaxen/jaxen -->
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.6</version>
        </dependency>

实体类

BeanDefinationn :Bean的定义信息
package myioc.configbean;

import java.util.HashMap;
import java.util.Map;

/**
 * Bean的定义信息
 */
public class BeanDefinationn {


    private String id;//名称
    private String clazz;//类型
    private Boolean isSinglen;//是否单例
    private Map<String, Object> properties = new HashMap<>();//非引用参数集合
    private Map<String, Object> refs = new HashMap<>();//引用参数集合

    public BeanDefinationn() {

    }

    @Override
    public String toString() {
        return "BeanDefinationn{" +
                "id='" + id + '\'' +
                ", clazz='" + clazz + '\'' +
                ", isSinglen=" + isSinglen +
                ", properties=" + properties +
                ", refs=" + refs +
                '}';
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getClazz() {
        return clazz;
    }

    public void setClazz(String clazz) {
        this.clazz = clazz;
    }

    public Boolean getSinglen() {
        return isSinglen;
    }

    public void setSinglen(Boolean singlen) {
        isSinglen = singlen;
    }

    public Map<String, Object> getProperties() {
        return properties;
    }

    public void setProperties(Map<String, Object> properties) {
        this.properties = properties;
    }

    public Map<String, Object> getRefs() {
        return refs;
    }

    public void setRefs(Map<String, Object> refs) {
        this.refs = refs;
    }
}
Book
package myioc.entity;

/**
 * @author CBeann
 * @create 2019-12-18 10:47
 */
public class Book {
    public void speak(){
        System.out.println("------Book-------");
    }
}
Student
package myioc.entity;

/**
 * @author CBeann
 * @create 2019-12-18 9:57
 */

public class Student {
    private String name;
    private String age;

    public Student() {

    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}
StudentDao
package myioc.entity;

/**
 * @author CBeann
 * @create 2019-12-18 10:06
 */
public class StudentDao {
    public StudentDao() {

    }

    public void speak() {
        System.out.println("-----StudentDao------");
    }
}
StudentService
package myioc.entity;

/**
 * @author CBeann
 * @create 2019-12-18 10:06
 */
public class StudentService {

    private StudentDao studentDao;
    public void speak() {
        System.out.println("-----StudentService------");
    }

    public StudentService() {
    }

    public StudentDao getStudentDao() {
        return studentDao;
    }

    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }
}

IOC工厂类

工厂接口
package myioc.factory;

/**
 * @author CBeann
 * @create 2019-12-18 9:46
 */
public interface IOCFactory {

    /**
     * 根据名称获得Bean
     */
    public Object getBean(String beanName) throws Exception;


}
工厂实现类
package myioc.factory.impl;

import myioc.configbean.BeanDefinationn;
import myioc.factory.IOCFactory;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author CBeann
 * @create 2019-12-18 10:08
 */
public class IOCFactoryImpl implements IOCFactory {

    private static Object NO_SINGLEN = new Object();


    //IOC容器
    private Map<String, Object> iocMap = new HashMap<>();
    //BeanDefination容器
    private Map<String, BeanDefinationn> beanDefinationnMap = new HashMap<>();


    public IOCFactoryImpl(String configPath) throws Exception {

        try {
            //解析配置文件
            xmlParse(configPath);
            //初始化Bean
            prepareSingleBean();


        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }


    /*解析XML方法*/
    private void xmlParse(String configPath) throws Exception {
        File file = new File(configPath);
        SAXBuilder builder = new SAXBuilder();
        Document document = builder.build(file);
        // 创建XPath对象,反射获取XPath对象
        XPathFactory factory = XPathFactory.instance();

        XPathExpression expression = factory.compile("//bean");
        List<Element> beans = expression.evaluate(document);
        for (Element beanElement : beans) {
            //获取id
            String id = beanElement.getAttributeValue("id");
            //获取类全路径
            String clazz = beanElement.getAttributeValue("class");
            //是否单例
            String singleton = beanElement.getAttributeValue("scope");
            //获取参数
            List<Element> properties = beanElement.getChildren("property");

            //初始化Bean定义对象
            BeanDefinationn beanDefination = new BeanDefinationn();
            beanDefination.setId(id);
            beanDefination.setClazz(clazz);
            beanDefination.setSinglen((singleton != null && "singleton".equals(singleton)));
            for (Element property : properties) {
                String name = property.getAttributeValue("name");
                String value = property.getAttributeValue("value");
                String ref = property.getAttributeValue("ref");


                if (null != value) {
                    //如果是非引用类型
                    beanDefination.getProperties().put(name, value);
                } else {
                    //如果是引用类型
                    beanDefination.getRefs().put(name, ref);
                }

            }
            beanDefinationnMap.put(beanDefination.getId(), beanDefination);
        }


    }

    //初始化单例类
    private void prepareSingleBean() throws Exception {

        //循环遍历Bean的定义信息
        Set<Map.Entry<String, BeanDefinationn>> entries = beanDefinationnMap.entrySet();
        for (Map.Entry<String, BeanDefinationn> entry : entries) {
            //获取Bean的唯一ID名称
            String id = entry.getKey();
            BeanDefinationn value = entry.getValue();
            //创建Bean
            Object bean = getBean(id);
            //如果是单例,放入IOC容器中
            if (value.getSinglen()) {
                iocMap.put(id, bean);
            } else {
                //非单例则放入一个静态类,如果放null,则容易和没有定义的类混淆
                iocMap.put(id, NO_SINGLEN);
            }
        }

    }

    /*
    获取Bean
    如果单例,在IOC容器获取并且返回,如果获取不到,创建并且并且返回
     */
    @Override
    public Object getBean(String beanName) throws Exception {
        BeanDefinationn beanDefinationn = beanDefinationnMap.get(beanName);

        //如果是单例
        if (beanDefinationn.getSinglen()) {
            //如果容器中有该Bean,直接返回
            if (null != iocMap.get(beanName)) {
                return iocMap.get(beanName);
            } else {
                //如果容器中没有该Bean,则创建Bean
                return doCreateBean(beanName);
            }

        } else {
            //如果不是单例,直接创建,不在IOC容器中获取
            return doCreateBean(beanName);
        }
    }

    /*
    创建Bean
     */
    private Object doCreateBean(String beanName) throws Exception {
        //获取Bean的定义信息
        BeanDefinationn beanDefinationn = beanDefinationnMap.get(beanName);
        // 反射拿到类的相应信息,首先是拿到类的实例对象
        Class clazz = Class.forName(beanDefinationn.getClazz());
        Object object = clazz.newInstance();
        // 获取类的所有方法,然后通过set方法给这个对象设置属性值
        Method[] methods = clazz.getDeclaredMethods();
        //获取所有的参数和引用
        Map<String, Object> properties = beanDefinationn.getProperties();
        Map<String, Object> refs = beanDefinationn.getRefs();


        //给对象封装引用参数和非引用参数
        for (int i = 0; i < methods.length; i++) {
            //获得方法的名称
            String methodName = methods[i].getName();
            // 属性名
            String beanPropertyName = "";
            // 这里检索set方法
            if (methodName.startsWith("set")) {
                // 根据set方法获取属性名->这里就只截取set方法的方法名并且转换为小写的名字
                //setStudentDao--->studentDao
                beanPropertyName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
                if (properties.containsKey(beanPropertyName)) {
                    //如果这个参数是非引用参数
                    //在参数列表中获取value
                    Object proVal = properties.get(beanPropertyName);
                    //通过反射执行此方法
                    methods[i].invoke(object, proVal);


                } else if (refs.containsKey(beanPropertyName)) {
                    //如果这个参数是引用参数
                    //在ioc容器中获取value
                    Object proVal = getBean(refs.get(beanPropertyName).toString());
                    //通过反射执行此方法
                    methods[i].invoke(object, proVal);

                } else {
                    //什么也不做
                }


            }


        }
        //返回这个类型
        return object;

    }
}

myioc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id="student" class="myioc.entity.Student" scope="prototype">
        <property name="name" value="CBeann"/>
        <property name="age" value="18"/>
    </bean>
    <bean id="book" class="myioc.entity.Book" scope="singleton">

    </bean>
    <bean id="studentService" class="myioc.entity.StudentService" scope="singleton">
        <property name="studentDao" ref="studentDao"/>
    </bean>
    <bean id="studentDao" class="myioc.entity.StudentDao" scope="singleton">
    </bean>
</beans>

启动类

package myioc.app;


import myioc.entity.Book;
import myioc.entity.Student;
import myioc.entity.StudentDao;
import myioc.entity.StudentService;
import myioc.factory.IOCFactory;
import myioc.factory.impl.IOCFactoryImpl;

/**
 * @author CBeann
 * @create 2019-12-18 9:49
 */
public class Start {
    public static void main(String[] args) throws Exception {

        IOCFactory factory = new IOCFactoryImpl("E:\\IntelliJ IDEA 2019.1.3Workspace\\Demoo\\demo\\src\\main\\java\\myioc\\myioc.xml");

        System.out.println("----单例----");
        Book book1 = (Book) factory.getBean("book");
        Book book2 = (Book) factory.getBean("book");
        System.out.println(book1.hashCode());
        System.out.println(book2.hashCode());

        System.out.println("------非单例------");
        Student student1 = (Student) factory.getBean("student");
        Student student2 = (Student) factory.getBean("student");
        System.out.println(student1.hashCode());
        System.out.println(student2.hashCode());

        System.out.println("-------依赖注入-------");
        StudentService studentService = (StudentService) factory.getBean("studentService");
        System.out.println(studentService.getStudentDao().hashCode());
        StudentDao studentDao = (StudentDao) factory.getBean("studentDao");
        System.out.println(studentDao.hashCode());


    }
}

总结

(1)

 <property name="age" value="18"/>

如果上图的age是String类型,那运行正常;如果是int类型,那就会报错。现在还没有找到解决办法。。。

@Component
public class IntegerDemo {

    @Value("#{18}")
    private Integer age;
}

我看的Spring源码是最后通过BeanPostProcesser一层层调用最后到unsafe类

unsafe.putObject(var1, this.fieldOffset, var2);

debug发现var1是对象InegerDemo,var2是 18, this.fieldOffest(fieldOffest是传入age获得的)初步判断是插入编译后的字节码偏移量,而且这个方法在跟进去就是native方法

(2)

我认为大多数的手写都是模仿,因为物质决定意识,你见过的才会有这种思路,只有见的多了,才会有创建

(3)

可以看看Spring源码