前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >手写IOC容器

手写IOC容器

作者头像
CBeann
发布2023-12-25 17:25:41
1170
发布2023-12-25 17:25:41
举报
文章被收录于专栏:CBeann的博客

实践

项目目录结构

项目下载

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

pom

代码语言:javascript
复制
<!--解析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的定义信息
代码语言:javascript
复制
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
代码语言:javascript
复制
package myioc.entity;

/**
 * @author CBeann
 * @create 2019-12-18 10:47
 */
public class Book {
    public void speak(){
        System.out.println("------Book-------");
    }
}
Student
代码语言:javascript
复制
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
代码语言:javascript
复制
package myioc.entity;

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

    }

    public void speak() {
        System.out.println("-----StudentDao------");
    }
}
StudentService
代码语言:javascript
复制
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工厂类

工厂接口
代码语言:javascript
复制
package myioc.factory;

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

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


}
工厂实现类
代码语言:javascript
复制
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

代码语言:javascript
复制
<?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>

启动类

代码语言:javascript
复制
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)

代码语言:javascript
复制
 <property name="age" value="18"/>

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

代码语言:javascript
复制
@Component
public class IntegerDemo {

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

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

代码语言:javascript
复制
unsafe.putObject(var1, this.fieldOffset, var2);

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

(2)

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

(3)

可以看看Spring源码

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-03-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 实践
    • 项目目录结构
      • 项目下载
        • pom
          • 实体类
            • BeanDefinationn :Bean的定义信息
            • Book
            • Student
            • StudentDao
            • StudentService
          • IOC工厂类
            • 工厂接口
            • 工厂实现类
          • myioc.xml
            • 启动类
            • 总结
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档