在dubbo系列中我写了一篇探究Dubbo服务注册发现的原理,为了在spring中动态注入payservice接口,我利用了BeanDefinitionRegistryPostProcessor接口,中间利用factorybean来实现了一个自定义bean的创建过程.

但是待我运行之后报了这样的错误Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameter,

不得以跟着refresh方法debug,一点点端详,查出了猫腻...

下面是整个代码链路跟踪图,看看这个PayService自定义接口是怎么一步一步注入到spring中的

  • refresh : 这就不说了,spring的核心方法都在这里

  • finishBeanFactoryInitialization : 完成此上下文的bean工厂的初始化,初始化所有剩余的单例bean

  • preInstantiateSingletons : 确保所有非延迟初始单例都实例化,同时考虑到FactoryBeans 。 如果需要,通常在工厂设置结束时调用。

  • getBean/doGetBean : 返回一个实例可以是指定bean的共享或独立的。

  • getSingleton : 再次检查单例缓存中是否有手动注册的单例

  • AH8q3dGK2f2vLZVgbRfLTjQPySe2yRaJHs.doCreateBean : 实际创建指定的bean。 此时已经进行了预创建处理,例如,检查postProcessBeforeInstantiation回调。区分默认的bean实例化,使用工厂方法和自动装配构造函数。

  • createBeanInstance : 使用适当的实例化策略为指定的bean创建一个新实例:工厂方法,构造函数自动装配或简单实例化

  • autowireConstructor : “自动装配构造函数”(按类型带有构造函数参数)的行为。 如果指定了显式构造函数参数值,则将所有剩余参数与Bean工厂中的Bean进行匹配时也适用。
    这对应于构造函数注入:在这种模式下,Spring Bean工厂能够托管需要基于构造函数的依赖关系解析的组件

贴一下代码中FactoryBean的实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* @author 小五
*/
public class ProxyFactory<T> implements FactoryBean<T> {

@Setter
private AsyncTaskExecutor executor;

@Setter
private RedisDiscoveryCenter redisDiscoveryCenter;

private Class<T> interfaceClass;
// 省略执行逻辑
@Override
public T getObject() throws Exception {
return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
......
}
});
}
// 起初我没写这里的构造函数
public ProxyFactory(Class<T> interfaceClass) {
this.interfaceClass = interfaceClass;
}

@Override
public Class<?> getObjectType() {
return interfaceClass;
}
// 默认就是单例
@Override
public boolean isSingleton() {
return true;
}
}

我把断点直接打在ConstructorResolver.autowireConstructor方法上,

这个candidates是当前factorybean所有类型的构造函数,下面的逻辑是遍历且对给定已解析的构造函数参数值,创建一个参数数组以调用构造函数或工厂方法。也就是这个ArgumentsHolder

我写错的地方就是因为没有构造函数,导致constructorToUse == null成立,抛出BeanCreationException异常.
这里是要注入PayService,那假如我弄了个其他的构造函数呢,比如无参构造函数.或者不是interfaceClass,而是executor呢

在上面获取ArgumentsHolder对象时,会走到TypeConverterDelegate.convertIfNecessary方法中进行类型校验

  • 对于指定的属性,将值转换为所需的类型(如果需要从字符串转换)。
  • @param propertyName属性名
  • @param oldValue前一个值,如果可用(可能是{@code null})
  • @param newValue建议的新值
  • @param requiredType 必须转换为的类型(如果不知道,例如集合元素,则转换为{@code null})
  • @param typeDescriptor 目标属性或字段的描述符
  • @return 新值,可能是类型转换的结果
  • @throws 类型转换失败时抛出IllegalArgumentException异常

判断我当前注入的PayService是不是instance of这个构造函数传的参数,如果是则返回类型转换的结果,都不是,则导致constructorToUse == null成立,抛出BeanCreationException异常.如果中间类型转换失败,抛出IllegalArgumentException异常

再来看下factorybean中redisDiscoveryCenterexecutor是怎么注入到PayService中的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 用于Spring动态注入自定义接口
*/
public class ServiceBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor {

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
Set<Class<?>> typesAnnotatedWith......
for (Class beanClazz : typesAnnotatedWith) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz);
GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
new redisDiscoveryCenter......
new executor......
definition.getPropertyValues().addPropertyValue("redisDiscoveryCenter", redisDiscoveryCenter);
definition.getPropertyValues().addPropertyValue("executor", executor);
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClazz);
setBeanClass......
//这里采用的是byType方式注入,类似的还有byName等
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
registerBeanDefinition......
}
}
}

待PayService初始化完成,需要填充属性时执行populateBean方法,然后判断注入是bytype还是byname

我这里是bytype,会走到BeanWrapperImpl.getLocalPropertyHandler方法

然后从propertyDescriptors.get(name)获取到当前属性的PropertyDescriptor;
如果找不到则抛出异常,找到了,则进行反射赋值
**PropertyDescriptor描述了一个Java Bean的属性,通过一对访问器方法导出。所以必须对其属性提供setter方法**