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


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

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

    spring core

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

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

    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);}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    当一个属性成员 serviceImpl 声明为 @Autowired 后,那么在创建 HelloWorldController 这个 Bean 时,会先使用构造器反射出实例,然后来装配各个标记为 @Autowired 的属性成员(装配方法参考 AbstractAutowireCapableBeanFactory#populateBean)。
    
    AutowiredAnnotationBeanPostProcessor
    它会通过 DefaultListableBeanFactory#findAutowireCandidates 寻找到依赖
    然后设置给对应的属性(即 serviceImpl 成员)
    AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject:
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    @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;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    原理,使用代理实现,获取函数返回类型,去容器中寻找,不调用原函数
    CglibSubclassingInstantiationStrategy.LookupOverrideMethodInterceptor
    LookupOverrideMethodInterceptor#intercept
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    @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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 其中有AutowiredAnnotationBeanPostProcessor处理器
    2. 寻找所有需要注入的字段与方法,获取这些字段的元信息(类型)
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    
    • 1
    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);
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    @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 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 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;
    }
    
    • 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

    @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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    结论: 如果一个类名是以两个大写字母开头的,则首字母不变,其它情况下默认首字母变成小写。
    5. 内部类的默认名称

    
    public static String getShortName(String className) {
       Assert.hasLength(className, "Class name must not be empty");
       int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
       int nameEndIndex = className.indexOf(CGLIB_CLASS_SEPARATOR);
       if (nameEndIndex == -1) {
          nameEndIndex = className.length();
       }
       String shortName = className.substring(lastDotIndex + 1, nameEndIndex);
       shortName = shortName.replace(INNER_CLASS_SEPARATOR, PACKAGE_SEPARATOR);
       return shortName;
    }
    
    com.spring.puzzle.class2.example3.StudentController$InnerClassDataService 
    ->
    StudentController$InnerClassDataService
    -> 
    studentController$InnerClassDataService
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    @ComponentScan 中nameGenerator 可以重写命名生成方案

    实现BeanNameGenerator接口generateBeanName方法,即可

    @Value 注入原理

    我们一般都会因为 @Value 常用于 String 类型的装配而误以为 @Value 不能用于非内置对象的装配,实际上这是一个常见的误区。

    @Value("#{student}")
    private Student student;
    
    @Value("#{student.name}") // 注入某个Bean的某个属性!!(之前没注意到可以这么用)
    private String name;
    
    @Bean
    public Student student(){
        Student student = createStudent(1, "xie");
        return student;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 解析依赖
    DefaultListableBeanFactory#doResolveDependency
    @Nullable
    public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
          @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
        //省略其他非关键代码
        Class type = descriptor.getDependencyType();
          //寻找@Value
          Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
          if (value != null) {
             if (value instanceof String) {
                //解析Value值
                String strVal = resolveEmbeddedValue((String) value);
                BeanDefinition bd = (beanName != null && containsBean(beanName) ?
                      getMergedBeanDefinition(beanName) : null);
                value = evaluateBeanDefinitionString(strVal, bd);
             }
             
             //转化Value解析的结果到装配的类型
             TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
             try {
                return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
             }
             catch (UnsupportedOperationException ex) {
                //异常处理
             }
          }
        //省略其他非关键代码
      }
    
    • 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
    1. 寻找value
    QualifierAnnotationAutowireCandidateResolver#findValue
    
    @Nullable
    protected Object findValue(Annotation[] annotationsToSearch) {
       if (annotationsToSearch.length > 0) {  
          AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(
                AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);
          //valueAnnotationType即为@Value
          if (attr != null) {
             return extractValue(attr);
          }
       }
       return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 解析 @Value 的字符串值,解析结果可能是对象、字符串
    2. 将解析结果转换为对应数据类型,实现接口 PropertyEditorSupport
    public class UUIDEditor extends PropertyEditorSupport {
    
       @Override
       public void setAsText(String text) throws IllegalArgumentException          {
          if (StringUtils.hasText(text)) {
             //转化操作
             setValue(UUID.fromString(text.trim()));
          }
          else {
             setValue(null);
          }
       }
       //省略其他非关代码
      
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    @Value 注入的值不对

    在这里插入图片描述
    数据来源有很多,包括一些运行环境变量,同时查找的逻辑很简单,找到第1个值就返回。

    (PropertySourcesPropertyResolver#getProperty)
    @Nullable
    protected  T getProperty(String key, Class targetValueType, boolean resolveNestedPlaceholders) {
       if (this.propertySources != null) {
          for (PropertySource propertySource : this.propertySources) {
             Object value = propertySource.getProperty(key);
             if (value != null) {
             //查到value即退出  
             return convertValueIfNecessary(value, targetValueType);
             }
          }
       }
       return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    结论:
    参数命名多级分类,保证@Value 注入的值唯一,特别是与环境变量或系统变量起冲突

    @Value 注入集合类型,当有该类型集合申明的Bean 与 散装Bean时,注入的是散装的Bean而非集合类型Bean

    结论: 两种执行按照优先集合类型收集 大于 指定申明名称bean的方式进行装配

    DefaultListableBeanFactory#doResolveDependency
    Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
    if (multipleBeans != null) {
       return multipleBeans;
    }
    Map matchingBeans = findAutowireCandidates(beanName, type, descriptor);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    @RestController
    @Slf4j
    public class StudentController {
    
        private List students;
    
        public StudentController(List students){
            this.students = students;
        }
    
        @RequestMapping(path = "students", method = RequestMethod.GET)
        public String listStudents(){
           return students.toString();
        };
    
    }
    
    @Bean
    public Student student1(){
        return createStudent(1, "xie");
    }
    
    @Bean
    public Student student2(){
        return createStudent(2, "fang");
    }
    
    @Bean
    public List students(){
        Student student3 = createStudent(3, "liu");
        Student student4 = createStudent(4, "fu");
        return Arrays.asList(student3, student4);
    } 
    
    • 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
    DefaultListableBeanFactory#resolveMultipleBeans
    
    private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,
          @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) {
       final Class type = descriptor.getDependencyType();
       if (descriptor instanceof StreamDependencyDescriptor) {
          //装配stream
          return stream;
       }
       else if (type.isArray()) {
          //装配数组
          return result;
       }
       else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
          //装配集合
          //获取集合的元素类型
          Class elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
          if (elementType == null) {
             return null;
          }
          //根据元素类型查找所有的bean
          Map matchingBeans = findAutowireCandidates(beanName, elementType,
                new MultiElementDescriptor(descriptor));
          if (matchingBeans.isEmpty()) {
             return null;
          }
          if (autowiredBeanNames != null) {
             autowiredBeanNames.addAll(matchingBeans.keySet());
          }
          //转化查到的所有bean放置到集合并返回
          TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
          Object result = converter.convertIfNecessary(matchingBeans.values(), type);
          //省略非关键代码
          return result;
       }
       else if (Map.class == type) {
          //解析map
          return matchingBeans;
       }
       else {
          return null;
       }
    }
    
    • 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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    1. 获取集合类型的元素类型 Class elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
    2. 根据元素类型,找出所有的 Bean Map matchingBeans = findAutowireCandidates(beanName, elementType, new MultiElementDescriptor(descriptor));
    3. 将匹配的所有的 Bean 按目标类型进行转化 Object result = converter.convertIfNecessary(matchingBeans.values(), type);

    spring 构造器中调用@Autowired 对象抛出空指针问题

    在这里插入图片描述
    第一部分,将一些必要的系统类,比如 Bean 的后置处理器类,注册到 Spring 容器,其中就包括我们这节课关注的 CommonAnnotationBeanPostProcessor 类;
    第二部分,将这些后置处理器实例化,并注册到 Spring 的容器中;
    第三部分,实例化所有用户定制类,调用后置处理器进行辅助装配、类初始化等等。

    结论,先通过构造器进行创建对象,再后进行各种BeanPostProcesser进行的填充对象,调用构造器时,自动注入对象实际为空。

    扩展信息:

    1. 很多必要的系统类,尤其是 Bean 后置处理器(比如 CommonAnnotationBeanPostProcessor、AutowiredAnnotationBeanPostProcessor 等),都是被 Spring 统一加载和管理的,并在 Spring 中扮演了非常重要的角色;
    2. 通过 Bean 后置处理器,Spring 能够非常灵活地在不同的场景调用不同的后置处理器,比如接下来我会讲到示例问题如何修正,修正方案中提到的 PostConstruct 注解,它的处理逻辑就需要用到 CommonAnnotationBeanPostProcessor(继承自 InitDestroyAnnotationBeanPostProcessor)这个后置处理器。

    Spring 初始化单例类的一般过程

    getBean()->doGetBean()->getSingleton(),如果发现 Bean 不存在,则调用 createBean()->doCreateBean() 进行实例化。

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
        throws BeanCreationException {
        //省略非关键代码
      if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
      }
      final Object bean = instanceWrapper.getWrappedInstance();
    
        //省略非关键代码
        Object exposedObject = bean;
        try {
           populateBean(beanName, mbd, instanceWrapper);
           exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
        //省略非关键代码
    }
    
    上述代码完整地展示了 Bean 初始化的三个关键步骤,按执行顺序分别是第 5 行的 createBeanInstance,第 12 行的 populateBean,以及第 13 行的 initializeBean,分别对应实例化 Bean,注入 Bean 依赖,以及初始化 Bean (例如执行 @PostConstruct 标记的方法 )这三个功能,这也和上述时序图的流程相符。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    AbstractAutowireCapableBeanFactory#createBeanInstance 方法
    
    public static  T instantiateClass(Constructor ctor, Object... args) throws BeanInstantiationException {
       Assert.notNull(ctor, "Constructor must not be null");
       try {
          ReflectionUtils.makeAccessible(ctor);
          return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
                KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
       }
       catch (InstantiationException ex) {
          throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
       }
       //省略非关键代码
    }
    
    方法通过依次调用 DefaultListableBeanFactory.instantiateBean() >SimpleInstantiationStrategy.instantiate(),最终执行到 BeanUtils.instantiateClass(),
    
    public static  T instantiateClass(Constructor ctor, Object... args) throws BeanInstantiationException {
       Assert.notNull(ctor, "Constructor must not be null");
       try {
          ReflectionUtils.makeAccessible(ctor);
          return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
                KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
       }
       catch (InstantiationException ex) {
          throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
       }
       //省略非关键代码
    }
    支持kotlin 委派创建对象,或者直接反射构造器初始化
    
    • 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

    推荐使用构造器参数来隐私注入是一种Spring的最佳实践

    规避在构造器中,调用@Autowire对象导致的空指针问题

    @PostConstruct 与 InitializingBean 接口

    实际上,Spring 在类属性完成注入之后,会回调用户定制的初始化方法。即在 populateBean 方法之后,会调用 initializeBean 方法,我们来看一下它的关键代码:

    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
       //省略非关键代码 
       if (mbd == null || !mbd.isSynthetic()) {
          wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
       }
       try {
          invokeInitMethods(beanName, wrappedBean, mbd);
       }
       //省略非关键代码 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    总结,优先调用@PostConstruct 方法,再调用InitializingBean接口方法,但是实际状况,如怕忘记,使用其1即可

    applyBeanPostProcessorsBeforeInitialization 方法最终执行到后置处理器 InitDestroyAnnotationBeanPostProcessor 的 buildLifecycleMetadata 方法(CommonAnnotationBeanPostProcessor 的父类):
    
    private LifecycleMetadata buildLifecycleMetadata(final Class clazz) {
       //省略非关键代码 
       do {
          //省略非关键代码
          final List currDestroyMethods = new ArrayList<>();
          ReflectionUtils.doWithLocalMethods(targetClass, method -> {
          //此处的 this.initAnnotationType 值,即为 PostConstruct.class
             if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
                LifecycleElement element = new LifecycleElement(method);
                currInitMethods.add(element);
      //非关键代码          
    }
    
    在这个方法里,Spring 将遍历查找被 PostConstruct.class 注解过的方法,返回到上层,并最终调用此方法。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    invokeInitMethods 与 InitializingBean 接口
    
    protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
          throws Throwable {
       boolean isInitializingBean = (bean instanceof InitializingBean);
       if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
          // 省略非关键代码 
          else {
             ((InitializingBean) bean).afterPropertiesSet();
          }
       }
       // 省略非关键代码 
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    @Bean 与 @Component及其衍生注解,触发shutdown close方法的区别

    结论:
    通过调试,我们发现只有通过使用 Bean 注解注册到 Spring 容器的对象,才会在 Spring 容器被关闭的时候自动调用 shutdown 方法,而使用 @Component(Service 也是一种 Component)将当前类自动注入到 Spring 容器时,shutdown 方法则不会被自动执行。

    使用 Bean 注解的方法所注册的 Bean 对象,如果用户不设置 destroyMethod 属性,则其属性值为 AbstractBeanDefinition.INFER_METHOD。此时 Spring 会检查当前 Bean 对象的原始类中是否有名为 shutdown 或者 close 的方法,如果有,此方法会被 Spring 记录下来,并在容器被销毁时自动执行;当然如若没有,那么自然什么都不会发生。

    private String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
       String destroyMethodName = beanDefinition.getDestroyMethodName();
       if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||(destroyMethodName == null && bean instanceof AutoCloseable)) {
          if (!(bean instanceof DisposableBean)) {
             try {
                //尝试查找 close 方法
                return bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
             }
             catch (NoSuchMethodException ex) {
                try {
                   //尝试查找 shutdown 方法
                   return bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
                }
                catch (NoSuchMethodException ex2) {
                   // no candidate destroy method found
                }
             }
          }
          return null;
       }
       return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
    }
    
    接着,继续逐级查找引用,最终得到的调用链从上到下为 doCreateBean->registerDisposableBeanIfNecessary->registerDisposableBean(new DisposableBeanAdapter)->inferDestroyMethodIfNecessary。
    
    
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
          throws BeanCreationException {
       //省略非关键代码 
       if (instanceWrapper == null) {
          instanceWrapper = createBeanInstance(beanName, mbd, args);
       }
       //省略非关键代码
       // Initialize the bean instance.
       Object exposedObject = bean;
       try {
          populateBean(beanName, mbd, instanceWrapper);
          exposedObject = initializeBean(beanName, exposedObject, mbd);
       }
       //省略非关键代码 
       // Register bean as disposable.
       try {
          registerDisposableBeanIfNecessary(beanName, bean, mbd);
       }
       catch (BeanDefinitionValidationException ex) {
          throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
       }
    
       return exposedObject;
    }
    
    到这,我们就可以对 doCreateBean 方法做一个小小的总结了。可以说 doCreateBean 管理了 Bean 的整个生命周期中几乎所有的关键节点,直接负责了 Bean 对象的生老病死,其主要功能包括:Bean 实例的创建;Bean 对象依赖的注入;定制类初始化方法的回调;Disposable 方法的注册。
    
    • 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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    registerDisposableBean
    
    public void registerDisposableBean(String beanName, DisposableBean bean) {
       //省略其他非关键代码
       synchronized (this.disposableBeans) {
          this.disposableBeans.put(beanName, bean);
       }
       //省略其他非关键代码
    }
    在 registerDisposableBean 方法内,DisposableBeanAdapter 类(其属性 destroyMethodName 记录了使用哪种 destory 方法)被实例化并添加到 DefaultSingletonBeanRegistry#disposableBeans 属性内,disposableBeans 将暂存这些 DisposableBeanAdapter 实例,直到 AnnotationConfigApplicationContext 的 close 方法被调用。
    而当 AnnotationConfigApplicationContext 的 close 方法被调用时,即当 Spring 容器被销毁时,最终会调用到 DefaultSingletonBeanRegistry#destroySingleton。此方法将遍历 disposableBeans 属性逐一获取 DisposableBean,依次调用其中的 close 或者 shutdown  方法:
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    为什么 @Service 的 shutdown不会被调用

    简述: @Bean 方法存在默认销毁方法AbstractBeanDefinition.INFER_METHOD
    但是@Setvice 标记的类,在不实现AutoCloseable、DisposableBean两个接口的情况下,不存在销毁方法,当日不会被调用

    public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
       if (bean instanceof DisposableBean || bean instanceof AutoCloseable) {
          return true;
       }
       String destroyMethodName = beanDefinition.getDestroyMethodName();
       if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName)) {
          return (ClassUtils.hasMethod(bean.getClass(), CLOSE_METHOD_NAME) ||
                ClassUtils.hasMethod(bean.getClass(), SHUTDOWN_METHOD_NAME));
       }
       return StringUtils.hasLength(destroyMethodName);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    spring-aop

    1. Spring AOP 则利用 CGlib 和 JDK 动态代理等方式来实现运行期动态方法增强
    2. 追根溯源,我们之所以能无感知地在容器对象方法前后任意添加代码片段,那是由于 Spring 在运行期帮我们把切面中的代码逻辑动态“织入”到了容器对象方法内,所以说 AOP 本质上就是一个代理模式

    spring-aop 使用

    spring-boot项目引入spring-boot-starter-aop后自动开启,普通spring项目需要@EnableAspectJAutoProxy进行开启 (老项目也有使用的xml 去开启)

    实际是引入了@Import({AspectJAutoProxyRegistrar.class})这个类,注入到spring容器后来完成APO相关Bean准备工作

    Spring AOP 的实现

    1. Spring AOP 的底层是动态代理。而创建代理的方式有两种,JDK 的方式和 CGLIB 的方式。JDK 动态代理只能对实现了接口的类生成代理,而不能针对普通类。而 CGLIB 是可以针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,来实现代理对象。具体区别可参考下图:
      在这里插入图片描述
      创建代理对象的过程堆栈
      在这里插入图片描述
      创建代理对象的时机就是创建一个 Bean 的时候,而创建的的关键工作其实是由 AnnotationAwareAspectJAutoProxyCreator 完成的,其本质就是BeanPostProcessor(同时也是实现了该接口)
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
       if (bean != null) {
          Object cacheKey = getCacheKey(bean.getClass(), beanName);
          if (this.earlyProxyReferences.remove(cacheKey) != bean) {
             return wrapIfNecessary(bean, beanName, cacheKey);
          }
       }
       return bean;
    }
    将初始化完成的对象变更为代理对象进行返回
    
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
       // 省略非关键代码
       Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
       if (specificInterceptors != DO_NOT_PROXY) {
          this.advisedBeans.put(cacheKey, Boolean.TRUE);
          Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
          this.proxyTypes.put(cacheKey, proxy.getClass());
          return proxy;
       }
       // 省略非关键代码 
    }
    第 6 行的 createProxy 调用是创建代理对象的关键。具体到执行过程,它首先会创建一个代理工厂,然后将通知器(advisors)、被代理对象等信息加入到代理工厂,最后通过这个代理工厂来获取代理对象。一些关键过程参考下面的方法:
    
    protected Object createProxy(Class beanClass, @Nullable String beanName,
          @Nullable Object[] specificInterceptors, TargetSource targetSource) {
      // 省略非关键代码
      ProxyFactory proxyFactory = new ProxyFactory();
      if (!proxyFactory.isProxyTargetClass()) {
       if (shouldProxyTargetClass(beanClass, beanName)) {
          proxyFactory.setProxyTargetClass(true);
       }
       else {
          evaluateProxyInterfaces(beanClass, proxyFactory);
       }
      }
      Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
      proxyFactory.addAdvisors(advisors);
      proxyFactory.setTargetSource(targetSource);
      customizeProxyFactory(proxyFactory);
       // 省略非关键代码
      return proxyFactory.getProxy(getProxyClassLoader());
    }
    
    • 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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    解决类内部方法调用导致的接口无法被增强问题

    原理: 只有引用的是被动态代理创建出来的对象,才会被 Spring 增强,具备 AOP 该有的功能
    解决方法1: 通过注入增加Bean进行使用

    @Service
    public class ElectricService {
        @Autowired
        ElectricService electricService;
        public void charge() throws Exception {
            System.out.println("Electric charging ...");
            //this.pay();
            electricService.pay();
        }
        public void pay() throws Exception {
            System.out.println("Pay with alipay ...");
            Thread.sleep(1000);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    解决方法2,使用AopContext,前提需要开启exposeProxy

    
    @SpringBootApplication
    @EnableAspectJAutoProxy(exposeProxy = true)
    public class Application {
        // 省略非关键代码
    }
    
    import org.springframework.aop.framework.AopContext;
    import org.springframework.stereotype.Service;
    @Service
    public class ElectricService {
        public void charge() throws Exception {
            System.out.println("Electric charging ...");
            ElectricService electric = ((ElectricService) AopContext.currentProxy());
            electric.pay();
        }
        public void pay() throws Exception {
            System.out.println("Pay with alipay ...");
            Thread.sleep(1000);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    Srping Proxy 代理过程

    正常情况下,AdminUserService 只是一个普通的对象,而 AOP 增强过的则是一个 AdminUserService E n h a n c e r B y S p r i n g C G L I B EnhancerBySpringCGLIB EnhancerBySpringCGLIBxxxx。
    这个类实际上是 AdminUserService 的一个子类。它会 overwrite 所有 public 和 protected 方法,并在内部将调用委托给原始的 AdminUserService 实例。
    从具体实现角度看,CGLIB 中 AOP 的实现是基于 org.springframework.cglib.proxy 包中 Enhancer 和 MethodInterceptor 两个接口来实现的。

    1. 定义自定义的 MethodInterceptor 负责委托方法执行;
    2. 创建 Enhance 并设置 Callback 为上述 MethodInterceptor;
    3. enhancer.create() 创建代理。
    
    public Object getProxy(@Nullable ClassLoader classLoader) {
        // 省略非关键代码
        // 创建及配置 Enhancer
        Enhancer enhancer = createEnhancer();
        // 省略非关键代码
        // 获取Callback:包含DynamicAdvisedInterceptor,亦是MethodInterceptor
        Callback[] callbacks = getCallbacks(rootClass);
        // 省略非关键代码
        // 生成代理对象并创建代理(设置 enhancer 的 callback 值)
        return createProxyClassAndInstance(enhancer, callbacks);
        // 省略非关键代码
    }
    
    
    protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
       //创建代理类Class
       Class proxyClass = enhancer.createClass();
       Object proxyInstance = null;
       //spring.objenesis.ignore默认为false
       //所以objenesis.isWorthTrying()一般为true
       if (objenesis.isWorthTrying()) {
          try {
             // 创建实例
             proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
          }
          catch (Throwable ex) {
              // 省略非关键代码
          }
       }
           
        if (proxyInstance == null) {
           // 尝试普通反射方式创建实例
           try {
              Constructor ctor = (this.constructorArgs != null ?
                    proxyClass.getDeclaredConstructor(this.constructorArgTypes) :
                    proxyClass.getDeclaredConstructor());
              ReflectionUtils.makeAccessible(ctor);
              proxyInstance = (this.constructorArgs != null ?
                    ctor.newInstance(this.constructorArgs) : ctor.newInstance());
          //省略非关键代码
           }
        }
       // 省略非关键代码
       ((Factory) proxyInstance).setCallbacks(callbacks);
       return proxyInstance;
    }
    
    • 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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    参照上述截图所示调用栈,objenesis 方式最后使用了 JDK 的 ReflectionFactory.newConstructorForSerialization() 完成了代理对象的实例化。
    重点: ReflectionFactory.newConstructorForSerialization() 方法不会初始化对象的成员变量

    Aop 增强对象的成员变量为null的问题

    ReflectionFactory.newConstructorForSerialization() 完成了代理对象的实例化。
    重点: ReflectionFactory.newConstructorForSerialization() 不含构造器的方法不会初始化对象的成员变量

    java.lang.Class.newInsance()
    java.lang.reflect.Constructor.newInstance()
    sun.reflect.ReflectionFactory.newConstructorForSerialization().newInstance()
    重点: ReflectionFactory.newConstructorForSerialization() 不含构造器的方法不会初始化对象的成员变量

    测试代码:
    ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
            Constructor constructor2 = reflectionFactory.newConstructorForSerialization(AdminUserService.class);
            AdminUserService adminUserService = (AdminUserService) constructor2.newInstance();
    
    • 1
    • 2
    • 3
    • 4

    Srping - AOP / Around、Before、After、AfterReturning、AfterThrowing 的执行顺序

    AbstractAutoProxyCreator#createProxy
    
    protected Object createProxy(Class beanClass, @Nullable String beanName,
          @Nullable Object[] specificInterceptors, TargetSource targetSource) {
       //省略非关键代码
       Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
       proxyFactory.addAdvisors(advisors);
       proxyFactory.setTargetSource(targetSource);
       //省略非关键代码
       return proxyFactory.getProxy(getProxyClassLoader());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 其中 advisors 就是增强方法对象,它的顺序决定了面临多个增强时,到底先执行谁。而这个集合对象本身是由 specificInterceptors 构建出来的,而 specificInterceptors 又是由 AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean 方法构建:
    @Override
    @Nullable
    protected Object[] getAdvicesAndAdvisorsForBean(
          Class beanClass, String beanName, @Nullable TargetSource targetSource) {
       List advisors = findEligibleAdvisors(beanClass, beanName);
       if (advisors.isEmpty()) {
          return DO_NOT_PROXY;
       }
       return advisors.toArray();
      }
    
    protected List findEligibleAdvisors(Class beanClass, String beanName) {
       //寻找候选的 Advisor
       List candidateAdvisors = findCandidateAdvisors();
       //根据候选的 Advisor 和当前 bean 算出匹配的 Advisor
       List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
       extendAdvisors(eligibleAdvisors);
       if (!eligibleAdvisors.isEmpty()) {
          //排序
          eligibleAdvisors = sortAdvisors(eligibleAdvisors);
       }
       return eligibleAdvisors;
    }
    最终 Advisors 的顺序是由两点决定:candidateAdvisors 的顺序;sortAdvisors 进行的排序。
    
    先按注解优先级排序
    new InstanceComparator<>(
          Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class)
    然后再按名称进行比较
    METHOD_COMPARATOR
    
    static {
       //第一个比较器,用来按照增强类型排序
       Comparator adviceKindComparator = new ConvertingComparator<>(
             new InstanceComparator<>(
                   Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
             (Converter) method -> {
                AspectJAnnotation annotation =
                   AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
                return (annotation != null ? annotation.getAnnotation() : null);
             })
       //第二个比较器,用来按照方法名排序
       Comparator methodNameComparator = new ConvertingComparator<>(Method::getName);
       METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);
     
    public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;
    
        int k = 0;
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }
    }
    
    • 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
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    结论: 比较两个方法的首个不相同字符的ASCII码,如均相同,取长度短的

    spring 事件

    1. 事件(Event):用来区分和定义不同的事件,在 Spring 中,常见的如 ApplicationEvent 和 AutoConfigurationImportEvent,它们都继承于 java.util.EventObject。
    2. 事件广播器(Multicaster):负责发布上述定义的事件。例如,负责发布 ApplicationEvent 的 ApplicationEventMulticaster 就是 Spring 中一种常见的广播器。
    3. 事件监听器(Listener):负责监听和处理广播器发出的事件,例如 ApplicationListener 就是用来处理 ApplicationEventMulticaster 发布的 ApplicationEvent,它继承于 JDK 的 EventListener,

    处理器的执行是顺序执行的,在执行过程中,如果一个监听器执行抛出了异常,则后续监听器就得不到被执行

    解决方案1: 处理器内部处理异常,不要抛出
    解决方案2: 设置全局异常处理
    SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = applicationContext.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, SimpleApplicationEventMulticaster.class); simpleApplicationEventMulticaster.setErrorHandler(TaskUtils.LOG_AND_SUPPRESS_ERROR_HANDLER);

    最终事件的执行是由同一个线程按顺序来完成的,任何一个报错,都会导致后续的监听器执行不了。
    
    SimpleApplicationEventMulticaster#multicastEvent(ApplicationEvent):
    @Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
       ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
       Executor executor = getTaskExecutor();
       for (ApplicationListener listener : getApplicationListeners(event, type)) {
          if (executor != null) {
             executor.execute(() -> invokeListener(listener, event));
          }
          else {
             invokeListener(listener, event);
          }
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    【心理学·人物】第二期(学术X综艺)
    顿号在键盘上怎么打?教你4个输入方法!
    Redis 支持哪些数据类型?以及使用场景?
    为什么重写Equals方法要重写HashCode方法
    python数据分析及可视化(六)Pandas的对齐运算、混合运算、统计函数、排序函数、处理缺失值及数据的存储与读取
    用Visual C++开发数据库应用程序
    微信小程序之微信授权登入及授权的流程讲解
    聊聊jedis连接池参数配置
    振弦式测缝(位移)计表面裂缝监测
    Java的String工具类源码
  • 原文地址:https://blog.csdn.net/hesiyuanlx/article/details/126599198