上次写完构造方法那篇后,有小伙伴在评论区疯狂催更:“这更新速度也太慢了吧,等你写完Spring,我怕是已经被优化成光了!”
啊这…我也是打工人啊,每天也在老板的鞭策下努力搬砖呢。不过既然大家这么期待,那今天就来点硬菜 - 属性注入!
相信各位卷王们肯定都一把梭过这样的代码:
@Autowired
private UserService userService;
看起来就两行代码,背后却暗藏玄机。今天我们来看看是怎么个事?扒一扒Spring是怎么偷偷帮我们注入属性的。
画外音:属性不是在创建实例的时候通过构造方法传进去了吗?
纸仓:是这样没错,但Spirng 管理的类要注入有点麻烦,简单来说就是:你动还是我动?
纸仓:而且下一步我们还有活要整,如果不这样做这系列就大结局了。求求给个机会吧!
设计一下子
要实现属性注入,首先是怎么存储的问题。也就是属性存储的数据结构怎么设计?总的来说,我们一共有两种属性:
- Java 的属性对象,包括 int、String、Object这些朴实无华的家伙
- Spring 的 Bean 对象,就是那些被Spring管家照顾的贵族对象
画外音:为什么 Spring 的 Bean 属性要单独处理呢?他也是 Java 对象属性的一种啊?
纸仓:聪明!Spring Bean 本质上就是一个Java 对象。但是呢,如果你要自己去 Spring 拿出来再注入,那还要用 Spring 干嘛?肯定是让她自己动起来啊。这样她觉得自己有价值,你也爽了。
那我们画个小图看看,就画亿点点
来看看我们的数据结构设计:
BeanReference
- 相当于给Bean打个小标签,标记【这是个Spring管理的对象哦】PropertyValue
- 就是一个属性值,可能是普通Java对象,也可能是Spring BeanPropertyValues
- PropertyValue的批发商(不是,是集合)BeanDefinition
- 老朋友了,Bean的说明书,记录着Bean的各种配置信息
是不是就画了亿点点?如果不够就再来一个
要不要再来一个?
假设 给你一棵二叉树的根节点root 尝试找出这棵树的每一棵子树的平均值中的最大值。子树是树中的任意节点和它的所有后代构成的集合。树的平均值是树中节点值的总和除以节点数。(不是,重来)
假设 你已经把数据结构都写好了,我们才能开始捋步骤。(反正我已经写好了,有图为证)
开始捋步骤:
第一步:定义好属性以及属性值,并放置在 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省略...
}
然后是我们的老朋友BeanDefinition
,现在它也要支持属性了:
public class BeanDefinition {
private final Class<?> beanClass;
// 新增属性配置
private final PropertyValues propertyValues;
// 构造方法和getter省略...
}
最后是重头戏 - 属性注入的核心逻辑:
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);
}
}
}
测试一下子
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
}
总结
-
我们实现了两种属性的注入:普通Java对象和Spring Bean
-
核心思路是:先创建实例,再注入属性
-
通过
BeanReference
来标记 Spring Bean -
使用反射来设置属性值
本文完 | 求赞求关注求转发 !