当前位置: 首页 > news >正文

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);
}

结论: 如果一个类名是以两个大写字母开头的,则首字母不变,其它情况下默认首字母变成小写。
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

@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. 解析依赖
DefaultListableBeanFactory#doResolveDependency
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
      @Nullable Set<String> 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. 寻找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. 解析 @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);
      }
   }
   //省略其他非关代码
  
}

@Value 注入的值不对

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

(PropertySourcesPropertyResolver#getProperty)
@Nullable
protected <T> T getProperty(String key, Class<T> 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;
}

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

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

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

DefaultListableBeanFactory#doResolveDependency
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
   return multipleBeans;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
@RestController
@Slf4j
public class StudentController {

    private List<Student> students;

    public StudentController(List<Student> 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<Student> students(){
    Student student3 = createStudent(3, "liu");
    Student student4 = createStudent(4, "fu");
    return Arrays.asList(student3, student4);
} 
DefaultListableBeanFactory#resolveMultipleBeans

private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,
      @Nullable Set<String> 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<String, Object> 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. 获取集合类型的元素类型 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 标记的方法 )这三个功能,这也和上述时序图的流程相符。
AbstractAutowireCapableBeanFactory#createBeanInstance 方法

public static <T> T instantiateClass(Constructor<T> 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> T instantiateClass(Constructor<T> 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 委派创建对象,或者直接反射构造器初始化

推荐使用构造器参数来隐私注入是一种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);
   }
   //省略非关键代码 
}

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

applyBeanPostProcessorsBeforeInitialization 方法最终执行到后置处理器 InitDestroyAnnotationBeanPostProcessor 的 buildLifecycleMetadata 方法(CommonAnnotationBeanPostProcessor 的父类):

private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
   //省略非关键代码 
   do {
      //省略非关键代码
      final List<LifecycleElement> 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 注解过的方法,返回到上层,并最终调用此方法。
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();
      }
   }
   // 省略非关键代码 
 }

@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 方法的注册。
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  方法:

为什么 @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);
}

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());
}

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

原理: 只有引用的是被动态代理创建出来的对象,才会被 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);
    }
}

解决方法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);
    }
}

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

参照上述截图所示调用栈,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();

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. 其中 advisors 就是增强方法对象,它的顺序决定了面临多个增强时,到底先执行谁。而这个集合对象本身是由 specificInterceptors 构建出来的,而 specificInterceptors 又是由 AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean 方法构建:
@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
      Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
   List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
   if (advisors.isEmpty()) {
      return DO_NOT_PROXY;
   }
   return advisors.toArray();
  }

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
   //寻找候选的 Advisor
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
   //根据候选的 Advisor 和当前 bean 算出匹配的 Advisor
   List<Advisor> 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<Method> adviceKindComparator = new ConvertingComparator<>(
         new InstanceComparator<>(
               Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
         (Converter<Method, Annotation>) method -> {
            AspectJAnnotation<?> annotation =
               AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
            return (annotation != null ? annotation.getAnnotation() : null);
         })
   //第二个比较器,用来按照方法名排序
   Comparator<Method> 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;
}
}

结论: 比较两个方法的首个不相同字符的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);
      }
   }
}

相关文章:

  • python基于django的考研报名交流平台
  • 基于VC++的心脏传导系统模拟器设计(2D虚拟心脏)
  • 遇见和光同尘
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • IMX6ULL学习笔记(4)——安装并使用交叉编译工具链
  • (一)使用Mybatis实现在student数据库中插入一个学生信息
  • 9月2日目标检测学习笔记——自然场景下文本检测
  • 易基因技术推介|植物内生菌宏基因组研究
  • 基于springboot+vue的游戏交流论坛系统 elementui
  • 【目标检测算法】YOLO-V1~V3原理梳理
  • 【Kafka】Docker安装kafka、搭建kafka集群
  • 4.【Linux虚拟机】创建自己的docker image并提供后端服务
  • 基于STFT和卷积神经网络的时序数据分类 代码+数据 可直接运行
  • 神经网络建模的适用范围,神经网络建模流程详解
  • 实名核验类API推荐
  • [ JavaScript ] 数据结构与算法 —— 链表
  • 【391天】每日项目总结系列128(2018.03.03)
  • 【腾讯Bugly干货分享】从0到1打造直播 App
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • 2019年如何成为全栈工程师?
  • Akka系列(七):Actor持久化之Akka persistence
  • ECS应用管理最佳实践
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • js对象的深浅拷贝
  • orm2 中文文档 3.1 模型属性
  • 电商搜索引擎的架构设计和性能优化
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 将 Measurements 和 Units 应用到物理学
  • 开源中国专访:Chameleon原理首发,其它跨多端统一框架都是假的?
  • 力扣(LeetCode)21
  • 用Visual Studio开发以太坊智能合约
  • 微龛半导体获数千万Pre-A轮融资,投资方为国中创投 ...
  • #NOIP 2014#Day.2 T3 解方程
  • #宝哥教你#查看jquery绑定的事件函数
  • #微信小程序:微信小程序常见的配置传旨
  • (9)YOLO-Pose:使用对象关键点相似性损失增强多人姿态估计的增强版YOLO
  • (PHP)设置修改 Apache 文件根目录 (Document Root)(转帖)
  • (八)Flask之app.route装饰器函数的参数
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (差分)胡桃爱原石
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (力扣题库)跳跃游戏II(c++)
  • (三)uboot源码分析
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • (转)Linq学习笔记
  • ./configure、make、make install 命令
  • .net core 6 redis操作类
  • .Net Framework 4.x 程序到底运行在哪个 CLR 版本之上
  • .NET HttpWebRequest、WebClient、HttpClient
  • .Net 中的反射(动态创建类型实例) - Part.4(转自http://www.tracefact.net/CLR-and-Framework/Reflection-Part4.aspx)...
  • .net(C#)中String.Format如何使用
  • .net6解除文件上传限制。Multipart body length limit 16384 exceeded
  • .NET的微型Web框架 Nancy
  • :“Failed to access IIS metabase”解决方法