为什么我的单例bean会多次初始化?

背景

代码如下,两个aop切入的类,都各自依赖的第三方提供的bean(必须是未初始化的,如果你没有redisson,你可以任意依赖其他的未初始化的bean)

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
65
66
67
68
69
70
71
72
73
74
75
76
@Slf4j
@Component
public class RecordException extends AbstractPointcutAdvisor {

@Resource
Redisson redisson;

@Override
public Pointcut getPointcut() {
return new Pointcut() {
@Override
public ClassFilter getClassFilter() {
// 只切入本项目
return clazz -> clazz.getName().startsWith("com....");
}

@Override
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE;
}
};
}

@Override
public Advice getAdvice() {
return (MethodInterceptor) invocation -> {
try {
return invocation.proceed();
} catch (Throwable t) {
log.error(invocation.getMethod().getName() + "方法异常", t);
throw t;
}
};
}
}


@Slf4j
@Component
public class RecordTime extends AbstractPointcutAdvisor {

static AtomicInteger count = new AtomicInteger();

public RecordTime() {
System.out.println("我是RecordTime第" + count.getAndIncrement() + "次,初始化");
}

@Resource
Redisson redisson;

@Override
public Pointcut getPointcut() {
return new Pointcut() {
@Override
public ClassFilter getClassFilter() {
// 只切入本项目
return clazz -> clazz.getName().startsWith("com....");
}

@Override
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE;
}
};
}

@Override
public Advice getAdvice() {
return (MethodInterceptor) invocation -> {
long start = System.currentTimeMillis();
Object result = invocation.proceed();
log.info("调用{}方法,耗时:{}", invocation.getMethod().getName(), System.currentTimeMillis() - start);
return result;
};
}
}

先解释代码含义
这两个类都继承了AbstractPointcutAdvisor,顶层接口为Advisor,它的作用就是aop的时候执行用户自定义的逻辑。
主要就是返回Advice,用户自定义的逻辑,可以在before,after,around以及用户自定义的逻辑等

recordException记录异常
recordTime记录耗时

问题

当你在spring运行以上代码时,会频繁出现我是RecordTime第。。。。次,初始化,直至项目成功启动,我本机复现,这个类大概初始化了有30多次

原因

经过仔细排查,分析spring源码,由几个重要的spring的bean的后置处理器引起的

  1. ioc
    • @Autowired
      AutowiredAnnotationBeanPostProcessor
    • @Resource
      CommonAnnotationBeanPostProcessor
  2. aop: AbstractAdvisorAutoProxyCreator

当bean初始化完之后要进行ioc,要进行aop
aop时要寻找Advisor实现类,进行初始化,ioc
以上两个Advisor实现类中都依赖的Redisson,所以要初始化Redisson,初始化完后要对Redisson进行AOP,aop时要寻找Advisor实现类,然后又找到了recordException,和recordTime。
此时又要对recordException,和recordTime进行IOC,又要注入Redisson,这时候发现Redisson已经正在初始化,所以不能再次初始化了,所以就报错

1
2
3
4
5
6
7
8
9
10
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
。。。
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
// 由于redisson已经初始化了一次,第二次到这时就会报错,导致redisson初始化失败
throw new BeanCurrentlyInCreationException(beanName);
}
}
。。。
}
  • 但是为什么spring还能正常运行?
    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
    public class BeanFactoryAdvisorRetrievalHelper {
    public List<Advisor> findAdvisorBeans() {
    // Determine list of advisor bean names, if not cached already.
    String[] advisorNames = this.cachedAdvisorBeanNames;
    if (advisorNames == null) {
    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let the auto-proxy creator apply to them!
    advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
    this.beanFactory, Advisor.class, true, false);
    this.cachedAdvisorBeanNames = advisorNames;
    }
    if (advisorNames.length == 0) {
    return new ArrayList<>();
    }

    List<Advisor> advisors = new ArrayList<>();
    for (String name : advisorNames) {
    if (isEligibleBean(name)) {
    if (this.beanFactory.isCurrentlyInCreation(name)) {
    if (logger.isTraceEnabled()) {
    logger.trace("Skipping currently created advisor '" + name + "'");
    }
    }
    else {
    try {
    advisors.add(this.beanFactory.getBean(name, Advisor.class));
    }
    catch (BeanCreationException ex) {
    Throwable rootCause = ex.getMostSpecificCause();
    if (rootCause instanceof BeanCurrentlyInCreationException) {
    BeanCreationException bce = (BeanCreationException) rootCause;
    String bceBeanName = bce.getBeanName();
    if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
    if (logger.isTraceEnabled()) {
    logger.trace("Skipping advisor '" + name +
    "' with dependency on currently created bean: " + ex.getMessage());
    }
    // Ignore: indicates a reference back to the bean we're trying to advise.
    // We want to find advisors other than the currently created bean itself.
    continue;
    }
    }
    throw ex;
    }
    }
    }
    }
    return advisors;
    }
    }
    阅读源码,原来他会判断如果有异常,且是BeanCurrentlyInCreationException类型的异常,他会continue啊
    rootCause instanceof BeanCurrentlyInCreationException

解决方案

让我们自定义的类在AbstractAdvisorAutoProxyCreator之前初始化就可以了(不让我们aop的类被aop,本身aop的类不允许被aop)。
通过实现BeanPostProcessor以及Ordered,然后实现OrderedgetOrder,大于AbstractAdvisorAutoProxyCreator的Order就可以

总结

获取aop的实现时,他会进行字段注入,如果要注入的对象还未初始化,要进行初始化,对注入的对象aop,此时的逻辑又到了寻找aop的实现了。造成了循环,不过还好spring有判断
看起来spring对这样的逻辑是有预判的,不过对于一个单例类,确实会存在多次初始化的情况,但是他都会销毁(因为ioc失败,所以这个实例不会缓存),最后会再次重新创建