手写IOC容器

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 手写IOC容器

实践


项目目录结构


2.png


项目下载


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源码


目录
相关文章
|
5月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
4月前
|
XML Java 开发者
经典面试---spring IOC容器的核心实现原理
作为一名拥有十年研发经验的工程师,对Spring框架尤其是其IOC(Inversion of Control,控制反转)容器的核心实现原理有着深入的理解。
183 3
|
3月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
94 0
|
5月前
|
XML Java 数据格式
Spring5入门到实战------3、IOC容器-Bean管理XML方式(一)
这篇文章详细介绍了Spring框架中IOC容器的Bean管理,特别是基于XML配置方式的实现。文章涵盖了Bean的定义、属性注入、使用set方法和构造函数注入,以及如何注入不同类型的属性,包括null值、特殊字符和外部bean。此外,还探讨了内部bean的概念及其与外部bean的比较,并提供了相应的示例代码和测试结果。
Spring5入门到实战------3、IOC容器-Bean管理XML方式(一)
|
5月前
|
XML Java 数据格式
Spring5入门到实战------5、IOC容器-Bean管理(三)
这篇文章深入探讨了Spring5框架中IOC容器的高级Bean管理,包括FactoryBean的使用、Bean作用域的设置、Bean生命周期的详细解释以及Bean后置处理器的实现和应用。
Spring5入门到实战------5、IOC容器-Bean管理(三)
|
5月前
|
XML Java 数据格式
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
这篇文章是Spring5框架的实战教程,主题是IOC容器中Bean的集合属性注入,通过XML配置方式。文章详细讲解了如何在Spring中注入数组、List、Map和Set类型的集合属性,并提供了相应的XML配置示例和Java类定义。此外,还介绍了如何在集合中注入对象类型值,以及如何使用Spring的util命名空间来实现集合的复用。最后,通过测试代码和结果展示了注入效果。
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
|
5月前
|
XML Java 数据格式
Spring5入门到实战------6、IOC容器-Bean管理XML方式(自动装配)
这篇文章是Spring5框架的入门教程,详细讲解了IOC容器中Bean的自动装配机制,包括手动装配、`byName`和`byType`两种自动装配方式,并通过XML配置文件和Java代码示例展示了如何在Spring中实现自动装配。
Spring5入门到实战------6、IOC容器-Bean管理XML方式(自动装配)
|
5月前
|
XML Java 数据格式
Spring5入门到实战------2、IOC容器底层原理
这篇文章深入探讨了Spring5框架中的IOC容器,包括IOC的概念、底层原理、以及BeanFactory接口和ApplicationContext接口的介绍。文章通过图解和实例代码,解释了IOC如何通过工厂模式和反射机制实现对象的创建和管理,以及如何降低代码耦合度,提高开发效率。
Spring5入门到实战------2、IOC容器底层原理
|
5月前
|
Java Spring 容器
建模底层逻辑问题之以Spring IOC容器为例,使用因果法建模,如何操作
建模底层逻辑问题之以Spring IOC容器为例,使用因果法建模,如何操作
|
5月前
|
XML Java 数据格式
Spring5入门到实战------8、IOC容器-Bean管理注解方式
这篇文章详细介绍了Spring5框架中使用注解进行Bean管理的方法,包括创建Bean的注解、自动装配和属性注入的注解,以及如何用配置类替代XML配置文件实现完全注解开发。
Spring5入门到实战------8、IOC容器-Bean管理注解方式