手写spring ioc(一)

手写spring ioc(一)

首记

spring源码断断续续学了近半年,算是小成,不过也是只学了冰山一角,好在ioc基本理清了,于是想出一个手写ioc的系列博客,不知道要写多久,反正尽量写, 这一版本会完成注册bd,DI注入,单例池等简单功能,下图是版本一的图示
在这里插入图片描述

手写ioc

上图说了,一切始于bd,首先我们先定义一个BeanDefinition

public interface BeanDefinition {
    String SCOPE_SINGLETON = "singleton";//单例
    String SCOPE_PROTOTYPE = "prototype";//多例

    Class<?> getBeanClass();
    void setBeanClass(Class<?> beanClass);

    //单多例域
    String getScope();
    void setScope(String scope);


    boolean isSingleton();//是否单例
    boolean isPrototype();//是否多例

    //懒加载
    void setLazyInit(boolean lazyInit);
    boolean isLazyInit();

    //postConstructor方法
    String getInitMethodName();
    void setInitMethodName(String initMethodName);

    //DI属性
    List<PropertyValue> getPropertyValues();
    void setPropertyValues(List<PropertyValue> propertyValues);
}

PropertyValue是描述DI属性的,PropertyValue类要写什么呢, 由于在最终spring容器中需要通过属性名字拿到field,然后field.set(v,realValue), 所以PropertyValue需要存储属性名字,属性值

public class PropertyValue {
    private String name;//属性名字
   private Object value;//属性值
    public PropertyValue(String name, Object value){
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

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

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }
}

已经定义好了bd,我们写一个实现类

public class GenericBeanDefinition implements BeanDefinition {
    private Class<?> beanClass;
    private String scope = BeanDefinition.SCOPE_SINGLETON;
    private boolean lazyInit = false;
    private String initMethodName;
    private List<PropertyValue> propertyValues;
    @Override
    public Class<?> getBeanClass() {
        return beanClass;
    }

    @Override
    public void setBeanClass(Class<?> beanClass) {
        this.beanClass = beanClass;
    }

    @Override
    public String getScope() {
        return scope;
    }

    @Override
    public void setScope(String scope) {
        //单例不用改,scope是乱写的也不用改,只有用户传多例才改
        if(BeanDefinition.SCOPE_PROTOTYPE.equals(scope)){
            this.scope = scope;
        }
    }

    @Override
    public boolean isSingleton() {
        return BeanDefinition.SCOPE_SINGLETON.equals(scope);
    }

    @Override
    public boolean isPrototype() {
        return BeanDefinition.SCOPE_PROTOTYPE.equals(scope);
    }

    @Override
    public void setLazyInit(boolean lazyInit) {
        this.lazyInit = lazyInit;
    }

    @Override
    public boolean isLazyInit() {
        return lazyInit;
    }

    @Override
    public String getInitMethodName() {
        return initMethodName;
    }

    @Override
    public void setInitMethodName(String initMethodName) {
        this.initMethodName = initMethodName;
    }

    @Override
    public List<PropertyValue> getPropertyValues() {
        return propertyValues;
    }

    @Override
    public void setPropertyValues(List<PropertyValue> propertyValues) {
        this.propertyValues = propertyValues;
    }
}

现在bd部分就已经完成了,我们要定义bd注册中心和bean工厂

public interface BeanFactory {

    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String beanName) throws Exception;

    boolean containsBean(String beanName);
}
public interface BeanDefinitionRegistry {

    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);

    void removeBeanDefinition(String beanName);

    BeanDefinition getBeanDefinition(String beanName);

    boolean containsBeanDefinition(String beanName);

    String[] getBeanDefinitionNames();

    int getBeanDefinitionCount();

    boolean isBeanNameInUse(String beanName);
}

spring基于工厂设计模式实现ioc,我们需要提供一个bean工厂的实现类,来实现上面两个接口

public class DefaultListableBeanFactory implements BeanFactory,BeanDefinitionRegistry {
	//bd集合
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    //单例池
    private final Map<String,Object> singletonObjects = new ConcurrentHashMap<>(256);
    //早期单例池
    private final Map<String,Object> earlySingletonObjects = new HashMap<>(16);
    //正在创建的单例spring bean的 beanName的集合
    private final Set<String> singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap<>(16));
    //bd name 集合
    private final List<String> beanDefinitionNames = new ArrayList<>();

    /**
     * 单例池取
     * 取不出,用bd new一个
     * 决策构造器,newInstance()
     * 空对象==暂无属性
     * 加入早期单例池
     * 属性注入
     * 循环依赖问题发现(定义一个正在创建的bean集合)
     * 循环依赖问题(二级缓存解决)
     * 调用init方法
     * 加入单例池,移除早期单例池
     * ioc完毕
     * @param beanName
     * @return
     */
    @Override
    public Object getBean(String beanName) throws Exception {
        Objects.requireNonNull(beanName,"spring bean name must not null at getBean");
        return this.doGetBean(beanName);
    }

    protected Object doGetBean(String beanName) throws Exception {
        BeanDefinition bd = beanDefinitionMap.get(beanName);
        if(bd==null||bd.getBeanClass()==null){
            return null;
        }
        Object instance = null;
        //单例直接从单例池拿
        if(bd.isSingleton()){
            instance = singletonObjects.get(beanName);
            if(instance!=null){
                return instance;
            }
        }

        //决策构造器
        instance = createBeanInstance(bd);

        //DI
        populateBean(bd,instance);

        //AOP代理 生命周期回调
        instance = initializeBean(bd,instance);

        //加入单例池
        if(bd.isSingleton()) {
            singletonObjects.put(beanName, instance);
        }
        return instance;
    }

    private Object initializeBean(BeanDefinition bd, Object instance) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        if(StringUtils.isNotBlank(bd.getInitMethodName())){
            Method method = instance.getClass().getMethod(bd.getInitMethodName(),null);
            method.invoke(instance,null);
        }
        //aop省略
        return instance;
    }

    private void populateBean(BeanDefinition bd, Object instance) throws Exception {
        if(null==instance||null==bd.getPropertyValues()||bd.getPropertyValues().isEmpty())return;
        for(PropertyValue pv:bd.getPropertyValues()){
            if(StringUtils.isBlank(pv.getName())){
                continue;
            }
            Class clazz = instance.getClass();
            Field field = clazz.getDeclaredField(pv.getName());
            field.setAccessible(true);
            Object realVal = getBaseValue(field.getType(),getRealValue(pv.getValue()));
            field.set(instance,realVal);
        }
    }

    private Object getBaseValue(Class<?>type,Object realVal){
        if(type==int.class||type==Integer.class){
            realVal = Integer.parseInt(realVal.toString());
        }else if(type==double.class||type==Double.class){
            realVal = Double.parseDouble(realVal.toString());
        }else if(type==float.class||type==Float.class){
            realVal = Float.parseFloat(realVal.toString());
        }else if(type==boolean.class||type==Boolean.class){
            realVal = Boolean.parseBoolean(realVal.toString());
        }else if(type==long.class||type==Long.class){
            realVal = Long.parseLong(realVal.toString());
        }else if(type==byte.class||type==Byte.class){
            realVal = Byte.parseByte(realVal.toString());
        }else if(type==short.class||type==Short.class){
            realVal = Short.parseShort(realVal.toString());
        }else if(type==char.class||type==Character.class){
            realVal = 'A';
        }
        return realVal;
    }

    //集合处理太复杂,省略
    public Object getRealValue(Object value) throws Exception {
        Object realValue = null;
        if(value==null){
            realValue = null;
        }else if (value instanceof BeanReference) {
            realValue = this.doGetBean(((BeanReference) value).getBeanName());
        } else if (value instanceof Object[]) {
            Object[] objs = (Object[]) value;
            for (int i=0;i<objs.length;i++) {
                objs[i] = getRealValue(objs[i]);
            }
            realValue = objs;
        } else if (value instanceof Collection) {
            Collection collection = (Collection) value;
            Object[] objs = collection.toArray();
            for (int i=0;i<objs.length;i++) {
                objs[i] = getRealValue(objs[i]);
            }
            //todo
            if(value instanceof List){
                List list= Arrays.asList(objs);
                if(value instanceof ArrayList){
                    realValue = Arrays.asList(objs);
                }else if(value instanceof LinkedList){
                    LinkedList ll = new LinkedList();
                    ll.addAll(list);
                    realValue = ll;
                }else if(value instanceof Vector){
                    Vector v = new Vector();
                    v.addAll(list);
                    realValue = v;
                }
            }else if(value instanceof Set){
                if(value instanceof HashSet){

                }else if(value instanceof LinkedHashSet){

                }else if(value instanceof SortedSet){

                }else if(value instanceof TreeSet){

                }
            }

        } else if (value instanceof Properties) {
            // TODO 处理properties中的bean引用
        } else if (value instanceof Map) {
            // TODO 处理Map中的bean引用
        } else{
            realValue = value;
        }
        return realValue;
    }

    private Object  createBeanInstance(BeanDefinition bd) throws Exception {
        return  bd.getBeanClass().newInstance();
    }

    @Override
    public boolean containsBean(String beanName) {
        return false;
    }

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition bd) {
        Objects.requireNonNull(beanName,"spring bean name must not null at registerBeanDefinition");
        Objects.requireNonNull(bd,"spring bd must not null at registerBeanDefinition");
        Objects.requireNonNull(bd.getBeanClass(),"spring bd class must not null at registerBeanDefinition");
        if(beanDefinitionMap.containsKey(beanName)){
            throw new RuntimeException("beanDefinitions must not contain same beanName:"+beanName);
        }
        beanDefinitionMap.put(beanName,bd);
        beanDefinitionNames.add(beanName);
    }

    @Override
    public void removeBeanDefinition(String beanName) {
        beanDefinitionMap.remove(beanName);
        beanDefinitionNames.remove(beanName);
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        return beanDefinitionMap.get(beanName);
    }

    @Override
    public boolean containsBeanDefinition(String beanName) {
        return beanDefinitionMap.containsKey(beanName);
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return (String[]) beanDefinitionNames.toArray();
    }

    @Override
    public int getBeanDefinitionCount() {
        return beanDefinitionNames.size();
    }

    @Override
    public boolean isBeanNameInUse(String beanName) {
        return singletonsCurrentlyInCreation.contains(beanName);
    }
    
    //预加载所有的单例非懒加载的bean
    public void preInstantiateSingletons() throws Exception{
        synchronized (beanDefinitionNames){
            for (String name : beanDefinitionNames){
                BeanDefinition beanDefinition = this.getBeanDefinition(name);
                if (beanDefinition.isSingleton()&&!beanDefinition.isLazyInit()){
                    this.doGetBean(name);
                }
            }
        }
    }
}

到此为止实现了ioc简单功能,我们可以写一个测试类测试一下

public class ABean {
    private String name;
    private int age;

    public void doSth(){
        System.out.println("do");
    }

    public void init(){
        System.out.println("init");
    }
}
public class Test {

    DefaultListableBeanFactory fac = new DefaultListableBeanFactory();

    @org.junit.jupiter.api.Test
    public void testIoc1() throws Exception {
        BeanDefinition bd = new GenericBeanDefinition();
        bd.setBeanClass(ABean.class);
        bd.setInitMethodName("init");
        List<PropertyValue> pvs = new ArrayList<>();
        pvs.add(new PropertyValue("name","lry"));
        pvs.add(new PropertyValue("age",20));
        bd.setPropertyValues(pvs);
        fac.registerBeanDefinition("aBean",bd);
        fac.preInstantiateSingletons();
        //上面都是系统做的,以后会支持注解扫描出类注册bd,提前实例化bean

        ABean aBean = (ABean) fac.getBean("aBean");
        aBean.doSth();
    }
}

输出
init
do

证明一切顺利,但实则不然,还有一个小问题,如果ABean中有个属性要注入spring容器中的CBean怎么办,
这个new PropertyValue(“name”,“lry”)要怎么写
写法一:new PropertyValue(“cBean”,“cBean”) 肯定不行,他会把字符串cBean赋值给cBean对象 ,肯定报错
写法一:new PropertyValue(“cBean”,newCBean()) 肯定不行,我们需要的是spring管理的bean,而不是手动new出来的,
所以我想要是spring看到cBean这个字符串就知道是个spring bean就好了,那么他就可以调用getBean(cBean)获取这个bean了,所以我们可以定义一个BeanReference只储存beanName即可

public class BeanReference {
    private String beanName;

    public BeanReference(String beanName){
        this.beanName = beanName;
    }

    public String getBeanName(){
        return beanName;
    }
}

DefaultListableBeanFactory 的getRealValue 加入以下代码

	else if (value instanceof BeanReference) {
            realValue = this.doGetBean(((BeanReference) value).getBeanName());
    }

再次修改测试用例

public class CBean {
}
public class Test {

    DefaultListableBeanFactory fac = new DefaultListableBeanFactory();

    @org.junit.jupiter.api.Test
    public void testIoc1() throws Exception {
        BeanDefinition bd1 = new GenericBeanDefinition();
        bd1.setBeanClass(CBean.class);
        fac.registerBeanDefinition("cBean",bd1);

        BeanDefinition bd = new GenericBeanDefinition();
        bd.setBeanClass(ABean.class);
        bd.setInitMethodName("init");
        List<PropertyValue> pvs = new ArrayList<>();
        pvs.add(new PropertyValue("name","lry"));
        pvs.add(new PropertyValue("age",20));
        pvs.add(new PropertyValue("cBean",new BeanReference("cBean")));
        bd.setPropertyValues(pvs);
        fac.registerBeanDefinition("aBean",bd);
        fac.preInstantiateSingletons();
        //上面都是系统做的,以后会支持注解扫描出类注册bd,提前实例化bean

        ABean aBean = (ABean) fac.getBean("aBean");
        aBean.doSth();
    }
}

总结

第一版的ioc就算完成了,不过还有很多不完善的地方,例如循环引用,推断构造器,提前暴露对象(有aop的情况是提前暴露对象工厂),其中最让我头疼的就是推断构造器,推断构造器实在是太难了,我又回头看了推断构造器的源码和ImportBeanDefinitionRegistrar这个接口,突然对mybatis的MapperScan注解有了更深层次的理解,spring推断构造器的通过构造器装配真的是把扩展发挥到极致了,它可以让mybatis不加Component注解却依然可以注入一个class对象,我很想给自己的spring ioc也提供这个功能,看看以后写不写得出来

推荐精读
ImportBeanDefinitionRegistrar
推断构造器

发布了127 篇原创文章 · 获赞 68 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/LiuRenyou/article/details/103494818