各位小伙伴们,上回我们聊了手写Spring第三篇,原来Spring容器是使用反射来初始化对象的, 结果没想到居然钓到了一条大鱼!有个眼尖的朋友跳出来说:兄弟,你的代码有bug啊,自定义一个有参构造方法就报错了,你这是要误人子弟啊
我一看真有鱼上钩就赶紧收杆:你真是我的知音啊,这个钩子我已经留了好久没人发现。如果再没有人发现我打算请出我的一个朋友了,真的太谢谢你了。
还记得上次我们那个奋斗B模板哥吗?就是那个带着一个团队,只拿一个人工资的狠角色。这次这个活我也打算交给他。
模板哥听说有大活,想都没想就说要把我们工厂的生产线彻底改造一番。
我一听,心想这小子怕不是又要整什么幺蛾子。但是转念一想,上次他带来的那个团队确实给我们工厂带来了不少改变。于是我决定再给他一次机会,看看他这次能玩出什么花样。
模板哥一进门就开始滔滔不绝:"老板,你看啊,我们上次只是用了最简单的方法来生产豆子。但是你有没有想过,如果有些豆子需要特殊的生产方法怎么办?
比如说,有些豆子需要加水,有些需要加肥料,还有些可能需要阳光照射才能长得好。"
我一听,顿时来了兴趣:"哦?那你说说,该怎么解决这个问题?"
模板哥嘿嘿一笑:"这就要用到我们的'多策略反射调用自定义构造方法'了!"
我一听这名字就头大,这不是又要整些高大上的东西吗?但是为了不让自己显得太外行,我还是强装镇定:"哦?说来听听。"
模板哥一边说一边掏出一张图纸:上次我用了 instance = beanDefinition.getBeanClass().newInstance(),这招【无参构造大法】,只能对付没有防备的类。如果遇到带有"有参构造护盾"的类,我们该如何突破呢?
再进一步,如果一个类有多个构造方法,我们能不能做到"点穴式"调用,想用哪个就用哪个?为了解决这个问题,我们可以这样设计...
先在工厂窗口 BeanFactory 加个可选的增值服务,客户要豆子的时候如果需要增值服务就提前说好加什么料。
public interface BeanFactory {
// name:豆子名,args:增值服务
Object getBean(String name, Object... args);
}
我们生产车间拿到客户订单的时候就知道这个用户是要增值服务的。这个createBeanInstance方法就像是我们工厂的多功能生产机器。生产车间根据增值服务的项目数量和类型决定用什么方法来生产豆子。
我点点头,心想这倒是挺有道理的。
//这里的@SuppressWarnings是在告诉编译器:"别唠叨了,我知道我在干啥!"
@SuppressWarnings("rawtypes")
private Object createInstance(BeanDefinition beanDefinition, Object... args) {
Constructor[] constructors = beanDefinition.getBeanClass().getDeclaredConstructors();
Optional<Constructor> matchingConstructor = Arrays.stream(constructors)
.filter(constructor -> {
// 通过构造方法参数列表长度和类型选择对应的构造方法
Class<?>[] parameterTypes = constructor.getParameterTypes();
if (parameterTypes.length != args.length) {
return false;
}
for (int i = 0; i < parameterTypes.length; i++) {
if (args[i] != null && !parameterTypes[i].isAssignableFrom(args[i].getClass())) {
return false;
}
}
return true;
}).findFirst();
try {
return strategy.createBean(beanDefinition, matchingConstructor.orElse(null), args);
} catch (Exception e) {
throw new BeanException("Failed to initialize " + beanDefinition.getBeanClass().getName(), e);
}
}
我听得云里雾里,但又不好意思说不懂。只好问道:那这个...呃... InstantiationStrategy 是什么东西?
public interface InstantiationStrategy {
// 这里的@SuppressWarnings是在告诉编译器:"别唠叨了,我知道我在干啥!"
@SuppressWarnings("rawtypes")
Object createBean(BeanDefinition beanDefinition, Constructor constructor, Object[] args) throws Exception;
}
模板哥眼睛一亮:这个就是我们的核心武器了!它就像是我们工厂里的不同生产线。我们可以根据需要,随时切换不同的生产线。
这个 JDKInstantiationStrategyImpl 就是我们的普通生产线。它可以处理大部分的豆子生产。但是如果我们遇到一些特殊的豆子,需要更复杂的生产方法,我们还可以再加一条高级生产线。
JDK 的方式实例化有个局限:如果需要实例化的类没有对应的接口那就不行,不过这是后话了。
public class JDKInstantiationStrategyImpl implements InstantiationStrategy {
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public Object createBean(BeanDefinition beanDefinition, Constructor constructor, Object[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class beanClass = beanDefinition.getBeanClass();
if (constructor == null) {
return beanClass.getConstructor().newInstance();
}
return beanClass.getConstructor(constructor.getParameterTypes()).newInstance(args);
}
}
public class CglibInstantiationStrategyImpl implements InstantiationStrategy {
@SuppressWarnings("rawtypes")
@Override
public Object createBean(BeanDefinition beanDefinition, Constructor constructor, Object[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(beanDefinition.getBeanClass());
enhancer.setCallback(new NoOp() {
@Override
public int hashCode() {
return super.hashCode();
}
});
if (Objects.isNull(constructor)) {
return enhancer.create();
}
return enhancer.create(constructor.getParameterTypes(), args);
}
}
模板哥滔滔不绝地讲着,我却已经听得头晕脑胀。不过有一点我倒是听明白了,这小子这次是真的给我们工厂带来了一场工业大革命。
以后不管来了什么奇奇怪怪的豆子,我们都能生产出来了。这下子很快就能给我换辆大G开了。
等模板哥讲完,我故作深沉地点点头:不错,不错。这次你干得不错。
模板哥听了,顿时喜笑颜开:那老板,我们团队的工资,您看?
我立马打断他:咳咳,你先去把这套系统实施了,等系统跑起来了我们再谈工资的事。
不过话说回来,这次模板哥带来的这套"多策略反射调用自定义构造方法"确实给我们的Spring工厂带来了质的飞跃。以后再来什么奇奇怪怪的豆子,我们也不怕了!