原来依赖注入这么简单,小小Spring拿捏 手写Spring第五篇了

上次写完构造方法那篇后,有小伙伴在评论区疯狂催更:“这更新速度也太慢了吧,等你写完Spring,我怕是已经被优化成光了!”

啊这…我也是打工人啊,每天也在老板的鞭策下努力搬砖呢。不过既然大家这么期待,那今天就来点硬菜 - 属性注入!

image.png

相信各位卷王们肯定都一把梭过这样的代码:

@Autowired
private UserService userService;

看起来就两行代码,背后却暗藏玄机。今天我们来看看是怎么个事?扒一扒Spring是怎么偷偷帮我们注入属性的。

画外音:属性不是在创建实例的时候通过构造方法传进去了吗?

纸仓:是这样没错,但Spirng 管理的类要注入有点麻烦,简单来说就是:你动还是我动?

纸仓:而且下一步我们还有活要整,如果不这样做这系列就大结局了。求求给个机会吧!

image.png

设计一下子

要实现属性注入,首先是怎么存储的问题。也就是属性存储的数据结构怎么设计?总的来说,我们一共有两种属性:

  • Java 的属性对象,包括 int、String、Object这些朴实无华的家伙
  • Spring 的 Bean 对象,就是那些被Spring管家照顾的贵族对象

画外音:为什么 Spring 的 Bean 属性要单独处理呢?他也是 Java 对象属性的一种啊?

纸仓:聪明!Spring Bean 本质上就是一个Java 对象。但是呢,如果你要自己去 Spring 拿出来再注入,那还要用 Spring 干嘛?肯定是让她自己动起来啊。这样她觉得自己有价值,你也爽了。

image.png

那我们画个小图看看,就画亿点点

image.png

来看看我们的数据结构设计:

  1. BeanReference - 相当于给Bean打个小标签,标记【这是个Spring管理的对象哦】
  2. PropertyValue - 就是一个属性值,可能是普通Java对象,也可能是Spring Bean
  3. PropertyValues - PropertyValue的批发商(不是,是集合)
  4. BeanDefinition - 老朋友了,Bean的说明书,记录着Bean的各种配置信息

是不是就画了亿点点?如果不够就再来一个

image.png

要不要再来一个?

假设 给你一棵二叉树的根节点root 尝试找出这棵树的每一棵子树的平均值中的最大值。子树是树中的任意节点和它的所有后代构成的集合。树的平均值是树中节点值的总和除以节点数。(不是,重来)

假设 你已经把数据结构都写好了,我们才能开始捋步骤。(反正我已经写好了,有图为证)

image.png

开始捋步骤:

第一步:定义好属性以及属性值,并放置在 BeanDefinition

第二步:把 BeanDefinition 注册到工厂中

第三步:根据 BeanDefinition 初始化对象

第四步:根据 BeanDefinition 填充属性

第五步:存储单例对象,并返回

实现一下子

首当其冲的属性三剑客

// PropertyValue - 属性值的包装类
public class PropertyValue {  
    private final String name;  
    private final Object value;  
    // 构造方法和getter省略...
}

// PropertyValues - 属性值的批发商
public class PropertyValues {  
    private final List<PropertyValue> propertyValues;  
}

// BeanReference - Bean的小标签
public class BeanReference {
    private final String beanName;
    // 构造方法和getter省略...
}

image.png

然后是我们的老朋友BeanDefinition,现在它也要支持属性了:

public class BeanDefinition {  
    private final Class<?> beanClass;  
	// 新增属性配置
    private final PropertyValues propertyValues;  
    // 构造方法和getter省略... 
}

image.png

最后是重头戏 - 属性注入的核心逻辑:

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
    private final InstantiationStrategy strategy = new CglibInstantiationStrategyImpl();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object... args) {
        Object instance;
        try {
            instance = createInstance(beanDefinition, args);
        } catch (Exception e) {
            throw new BeanException("Failed to initialize:" + beanDefinition.getBeanClass().getName(), e);
        }
        applyPropertyValue(beanName, instance, beanDefinition);
        addSingleton(beanName, instance);
        return instance;
    }

    private Object createInstance(BeanDefinition beanDefinition, Object... args) {
        // ...
    }

    private void applyPropertyValue(String beanName, Object instance, BeanDefinition beanDefinition) {
        PropertyValues propertyValues = beanDefinition.getPropertyValues();
        try {
            for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                String name = propertyValue.getName();
                Object value = propertyValue.getValue();

                // 如果需要填充的属性是 BeanFactory 管理的对象,那么递归获取
                if (value instanceof BeanReference) {
                    value = getBean(((BeanReference) value).getBeanName());
                }

                // 这是hutool的一个工具类:cn.hutool.core.bean.BeanUtil
                BeanUtil.setFieldValue(instance, name, value);
            }
        } catch (Exception e) {
            throw new BeanException("Failed to set properties for " + beanName);
        }
    }
}

image.png

测试一下子

public class BeanFactoryTest {  
   @Test  
    public void test() {  
	    // 创建工厂
        DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();  
  
        // 注册第一个Bean
        PropertyValue propertyValue = new PropertyValue("name", "KARL");  
        PropertyValues propertyValues = new PropertyValues(propertyValue);  
        BeanDefinition beanDefinition = new BeanDefinition(TestService.class, propertyValues);  
        defaultListableBeanFactory.registerBeanDefinition("testService", beanDefinition);  
        
		// 注册第二个Bean(依赖第一个Bean)
        PropertyValue serviceProperty = new PropertyValue("testService", new BeanReference("testService"));  
        BeanDefinition test2Definition = new BeanDefinition(Test2Service.class, new PropertyValues(serviceProperty));  
        defaultListableBeanFactory.registerBeanDefinition("test2Service", test2Definition);  
  
        // 获取Bean测试
        TestService service = (TestService) defaultListableBeanFactory.getBean("testService", "karl");  
        service.test();  // 输出:test  -> KARL

		
        service = (TestService) defaultListableBeanFactory.getBean("testService", 18);  
        service.test(); // 输出:test  -> KARL 因为单例不会再次执行实例化方法  
  
        Test2Service service2 = (Test2Service) defaultListableBeanFactory.getBean("test2Service");  
        service2.test();  // 输出:test2  -> test  -> KARL
    }

总结

  1. 我们实现了两种属性的注入:普通Java对象和Spring Bean

  2. 核心思路是:先创建实例,再注入属性

  3. 通过BeanReference来标记 Spring Bean

  4. 使用反射来设置属性值

image.png

本文完 | 求赞求关注求转发 !

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值