新日撸java三百行` 新手小白java学习记录 `Day5

新日撸java三百行新手小白java学习记录

Day5 Spring之IOC篇(二)



前言

树欲静而风不止
                                       —— 古人


一 、对于有参构造的处理

上一天写到了把创建对象的权利交出去,当时的实现是很暴躁的直接类对象new, 忽略了有参的初始化情况,这里进行改进:
1、使用策略模式,构建不同的初始化器,JDK的类对象初始化与Cglib的初始化方案:

public interface InstantiationStrategy {
    Object instantiate(Class beanClass , Constructor constructor , Object... args);
}


public class CglibInstantiationStrategy implements InstantiationStrategy{
    @Override
    public Object instantiate(Class beanClass, Constructor constructor, Object... args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(beanClass);
        enhancer.setCallback(new NoOp() {
            @Override
            public int hashCode() {
                return super.hashCode();
            }
        });

        if(null == constructor)return enhancer.create();
        else return enhancer.create(constructor.getParameterTypes() , args);
    }
}


public class JDKInstantiationStrategy implements InstantiationStrategy{
    @Override
    public Object instantiate(Class beanClass, Constructor constructor, Object... args) {
        if(constructor == null) {
            try {
                return beanClass.newInstance();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        else {
            try {
                return constructor.newInstance(args);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}

解析:最上层表明了需要的基本参数有:需要被实例化的类对象 、对应的构造器 、对应的构造方法的参数 ,有这几个参数便可以构造对象,使用JDK的只需要直接使用构造器创建对象即可 , 还有一个使用Cglib来创建,对于这个我也不是很熟练,以后在学习。
至于这些参数怎么来的:

abstract public class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory{

    @Override
    public Object createBean(String name, BeanDefinition beanDefinition , Object[] args) {
        Object bean;
        try {
            bean = createBeanInstance(beanDefinition , args);
            addSingleton(name , bean);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return bean;
    }

    protected Object createBeanInstance(BeanDefinition beanDefinition, Object... args){
        Constructor constructor = null;
        Class<?> clazz = beanDefinition.getBeanClass();
        for (Constructor c: clazz.getConstructors()
             ) {
            if(args != null && args.length == c.getParameterTypes().length){
                constructor = c;
                break;
            }
        }

        return getInstantiationStrategy().instantiate(clazz ,constructor ,args);
    }

    protected InstantiationStrategy getInstantiationStrategy (){
        //return new JDKInstantiationStrategy();
        return new CglibInstantiationStrategy();
    }
}

class可以从bean定义里面得到,因为bean定义里面目前存的就是这个,剩下的唯一操作就是根据参数判断走哪个构造器,只需要用参数的长度比一下构造器的长度,哪个相等就用哪个。
然后就是改动的别的地方的代码:

abstract public class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {

    @Override
    public Object getBean(String name) {
        Object bean = getSingleton(name);
        if(bean != null) return bean;
        return doGetBean(name ,null);
    }

    @Override
    public Object getBean(String name , Object... args){
        Object bean = getSingleton(name);
        if(bean != null)return bean;
        return doGetBean(name , args);
    }

    public Object doGetBean(String name , Object... args){
        BeanDefinition beanDefinition = getBeanDefinition(name);
        return createBean(name , beanDefinition , args);
    }

    abstract public BeanDefinition getBeanDefinition(String name);
    abstract public Object createBean(String name  , BeanDefinition beanDefinition , Object[] args);

}


public interface BeanFactory {
     Object getBean(String name);

     Object getBean(String name , Object[] args);
}

这点内容是在太简单,主要还是学习他的设计模式,学习每个类如何甩锅。

二、实现依赖注入

1、像HashMap一样定义一个捆绑包 , 包含属性的名字与值。

public class PropertyValue {
    private final String name;
    private final Object value;

    public PropertyValue(String name, Object value) {
        this.name = name;
        this.value = value;
    }

    public String getName(){
        return name;
    }

    public Object getValue(){
        return value;
    }
}

2、由于一个类不只有一个属性,所以有需要一个捆绑包链:

public class PropertyValues {
    private final List<PropertyValue> propertyValueList = new ArrayList<>();

    public void addPropertyValue(PropertyValue propertyValue){
        propertyValueList.add(propertyValue);
    }

    public PropertyValue[] getPropertyValues(){
        return propertyValueList.toArray(new PropertyValue[0]);
    }

    public PropertyValue getPropertyValue(String name){
        for (PropertyValue pv:propertyValueList
             ) {
            if(pv.getName().equals(name))return pv;
        }

        return null;
    }
}

3、当bean对象被创建好了,就可以往里面填充属性:

    public Object createBean(String name, BeanDefinition beanDefinition , Object[] args) {
        Object bean;
        try {
            bean = createBeanInstance(beanDefinition , args);
            applyPropertyValues(bean, beanDefinition);
            addSingleton(name , bean);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return bean;
    }

    private void applyPropertyValues(Object bean, BeanDefinition beanDefinition) {
        PropertyValues propertyValues = beanDefinition.getPropertyValues();
        for (PropertyValue p : propertyValues.getPropertyValues()
                ) {
            String name = p.getName();
            Object value = p.getValue();
            if(value instanceof BeanReference){
                String beanName = ((BeanReference) value).getBeanName();
                value = getBean(beanName);
            }
            BeanUtils.setFieldValue(bean, name ,value);
            //BeanUtil.setFieldValue(bean , name ,value);  调用 cn.hutool.core.bean.BeanUtil包;
        }
    }

4、 这里的BeanReference是属性里面的对象类型,非基本类型,主要也就是记录一个名字,好方便getBean:

public class BeanReference {
 private final String beanName;

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

 public String getBeanName(){
     return beanName;
 }
}

5、最关键的地方就是如何填充bean对象的属性,有两种办法实现BeanUtil,一种就是掉包简单省事还稳妥 ,第二就是自己搓一个简单的BeanUtils,优点是锻炼自己,缺点是健壮性太差:

public class BeanUtils {

    public static void setFieldValue(Object bean , String name , Object value){
        Class clazz = bean.getClass().getName().contains("$$EnhancerByCGLIB$$") ? bean.getClass().getSuperclass() : bean.getClass();
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            Unsafe unsafe = (Unsafe) field.get(null);

            long offset = unsafe.objectFieldOffset(clazz.getDeclaredField(name));
            setFile(bean , offset  ,value ,unsafe);

        } catch (Exception e) {
            throw new RuntimeException("偏移量错误");
        }
    }

    public static void setFile(Object bean , long offset , Object value , Unsafe unsafe){
        if(value instanceof Integer)unsafe.putInt(bean ,offset , (int) value);
        else if(value instanceof String)unsafe.putObject(bean ,offset ,value);
        else unsafe.putObject(bean , offset , value);
    }
}

大概思路就是:使用Unsafe直接往对应的地址位置写值,需要注意的是,因为对象可能是Cglib生成的,所以需要单独判断确保拿到真正的Class对象,这种写法没什么营养,主要是帅。
6、贴一个运行结果:

public class Main {
    public static void main(String[] args) {

        BeanDefinition beanDefinitionDog = new BeanDefinition(Dog.class);

        DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();


        defaultListableBeanFactory.registerBeanDefinition("dog" , beanDefinitionDog);

        PropertyValues propertyValues = new PropertyValues();

        propertyValues.addPropertyValue(new PropertyValue("age" , 18));
        propertyValues.addPropertyValue(new PropertyValue("name" , "花花"));
        propertyValues.addPropertyValue(new PropertyValue("dog" , new BeanReference("dog")));

        BeanDefinition beanDefinitionCat = new BeanDefinition(Cat.class, propertyValues);
        defaultListableBeanFactory.registerBeanDefinition("Cat" , beanDefinitionCat);

        Cat cat = (Cat)defaultListableBeanFactory.getBean("Cat");
 //       Dog dog = (Dog)defaultListableBeanFactory.getBean("Dog");

        cat.name();
        cat.age();
        cat.useDog();
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值