Spring 编程常见问题之一(专栏学习笔记)_parameter 0 of constructor in-CSDN博客

Spring 编程常见问题之一(专栏学习笔记)

看见一个专栏,Spring 编程常见错误 50 例 ,进行spring知识点的相关巩固

非常赞同评论区的一句话:
之前忙业务需求疲于奔命,偶尔看些源码却只是窥豹一斑。
最近想把基础知识重新梳理一遍,搭建自己的知识体系。

spring core

spring boot 默认扫描包路径 - 扫描不到启动类同级包

ComponentScanAnnotationParser#parse
结论:使用启动类所在包作为基础包路径

ComponentScanAnnotationParser#parse

bean存在构造方法 报Parameter 0 of constructor in xxx required a bean of type xxxx that could not be found.

创建bean调用的方法
AbstractAutowireCapableBeanFactory#createBeanInstance
首先获取可能的构造器,然后获取其构造参数进行创建(仅1个有参构造器时)
如果存在多个有参构造器,spring 会使用无参构造器,当不存在无参构造器时会抛出异常。

Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args);}

@Autowired 装载原型bean prototype时,为每个类分别分配一个,每个类固定

当一个属性成员 serviceImpl 声明为 @Autowired 后,那么在创建 HelloWorldController 这个 Bean 时,会先使用构造器反射出实例,然后来装配各个标记为 @Autowired 的属性成员(装配方法参考 AbstractAutowireCapableBeanFactory#populateBean)。

AutowiredAnnotationBeanPostProcessor
它会通过 DefaultListableBeanFactory#findAutowireCandidates 寻找到依赖
然后设置给对应的属性(即 serviceImpl 成员)
AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject:

@Lookup 主动的依赖查找

    @RestController
    public class HelloWorldController {
    
        @RequestMapping(path = "hi", method = RequestMethod.GET)
        public String hi() {
            return "helloworld, service is : " + getServiceImpl();
        };

        @Lookup
        public ServiceImpl getServiceImpl() {
            return null;
        }
    }
原理,使用代理实现,获取函数返回类型,去容器中寻找,不调用原函数
CglibSubclassingInstantiationStrategy.LookupOverrideMethodInterceptor
LookupOverrideMethodInterceptor#intercept

在这里插入图片描述

@Autowired 发生位置与核心

  1. 执行 AbstractAutowireCapableBeanFactory#createBeanInstance 通过构造器反射构造出Bean
  2. 执行 AbstractAutowireCapableBeanFactory#populate 方法
  3. populate 方法遍历所有的BeanPostProcessor处理器
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
      //省略非关键代码
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
          //省略非关键代码
         }
      }
   }   
}
  1. 其中有AutowiredAnnotationBeanPostProcessor处理器
  2. 寻找所有需要注入的字段与方法,获取这些字段的元信息(类型)
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
  1. AutowiredFieldElement#inject 从容器中找到依赖并使用反射完成注入

@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
   Field field = (Field) this.member;
   Object value;
   //省略非关键代码
      try {
          DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
         //寻找“依赖”,desc为"dataService"的DependencyDescriptor
         value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
      }
      
   }
   //省略非关键代码
   if (value != null) {
      ReflectionUtils.makeAccessible(field);
      //装配“依赖”
      field.set(bean, value);
   }
}

@Autowire required a single bean, but 2 were found 原因

  1. 上述 value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
  2. DefaultListableBeanFactory#doResolveDependency
  3. 通过类型查找,找到大于1个以上该类型,触发优先级候选,如果没有优先级,判断是否是该对象是否必须的同时不是List Map 集合这种可以接受对个对象的DefaultListableBeanFactory#indicatesMultipleBeans,则要求该对象必须唯一,否则异常
    在这里插入图片描述
候补选取流程: 
是否有@Primary 如果有的话直接使用
否则使用@Priority 优先级高的 
否则根据matchesBeanName,通过指定名称去匹配

DefaultListableBeanFactory#determineAutowireCandidate

protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
   Class<?> requiredType = descriptor.getDependencyType();
   String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
   if (primaryCandidate != null) {
      return primaryCandidate;
   }
   String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
   if (priorityCandidate != null) {
      return priorityCandidate;
   }
   // Fallback
   for (Map.Entry<String, Object> entry : candidates.entrySet()) {
      String candidateName = entry.getKey();
      Object beanInstance = entry.getValue();
      if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
            matchesBeanName(candidateName, descriptor.getDependencyName())) {
         return candidateName;
      }
   }
   return null;
}

@Priority(value=${number}),数字越小,优先级越高

默认bean名称的来源

  1. 先找到要生成bean的类(扫描),ClassPathBeanDefinitionScanner#doScan
    在这里插入图片描述
  2. BeanNameGenerator#generateBeanName 调用该方法生成名称
  3. 走注解标记的bean,使用AnnotationBeanNameGenerator#generateBeanName 生成bean名称
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
   if (definition instanceof AnnotatedBeanDefinition) {
      String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
      if (StringUtils.hasText(beanName)) {
         // Explicit bean name found.
         return beanName;
      }
   }
   // Fallback: generate a unique default bean name.
   return buildDefaultBeanName(definition, registry);
}
  1. 注解显示指定名称则使用显示名称,否则调用AnnotationBeanNameGenerator#buildDefaultBeanName
protected String buildDefaultBeanName(BeanDefinition definition) {
   String beanClassName = definition.getBeanClassName();
   Assert.state(beanClassName != null, "No bean class name set");
   String shortClassName = ClassUtils.getShortName(beanClassName);
   return Introspector.decapitalize(shortClassName);
}

public static String decapitalize(String name) {
    if (name == null || name.length() == 0) {
        return name;
    }
    if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                    Character.isUpperCase(name.charAt(0))){
        return name;
    }
    char chars[] = name.toCharArray();
    chars[0] = Character.toLowerCase(chars[0]);
    return new String(chars);
}

结论

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值