文章目录
Spring boot 2.0 升级到 3.3.1 的相关问题 (六)
spring-data-redis
和 Spring AOP
警告的问题
问题描述
启动的时候会出现类似于这种内容的警告信息
2024-07-22T17:48:33.448+08:00 WARN 40892 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'com.alibaba.druid.spring.boot3.autoconfigure.stat.DruidSpringAopConfiguration' of type [com.alibaba.druid.spring.boot3.autoconfigure.stat.DruidSpringAopConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [projectingArgumentResolverBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-07-22T17:48:33.560+08:00 WARN 40892 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'spring.datasource.druid-com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties' of type [com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [projectingArgumentResolverBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
2024-07-22T17:48:33.565+08:00 WARN 40892 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'advisor' of type [org.springframework.aop.support.RegexpMethodPointcutAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [projectingArgumentResolverBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
问题调研
在2.0 版本中,也有类似的日志,但是info
级别的。
[2024-07-24 13:33:00.107] [] [main] INFO : [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker:328] - Bean 'com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure' of type [com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure$$EnhancerBySpringCGLIB$$2505a1af] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
[2024-07-24 13:33:00.166] [] [main] INFO : [c.a.d.s.b.a.DruidDataSourceAutoConfigure:56] - Init DruidDataSource
[2024-07-24 13:33:01.989] [] [main] INFO : [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker:328] - Bean 'org.springframework.boot.context.properties.ConversionServiceDeducer$Factory' of type [org.springframework.boot.context.properties.ConversionServiceDeducer$Factory] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
[2024-07-24 13:33:12.885] [] [main] INFO : [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker:328] - Bean 'spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties' of type [org.springframework.boot.autoconfigure.jdbc.DataSourceProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
相关代码
org.springframework.context.support.PostProcessorRegistrationDelegate.BeanPostProcessorChecker#postProcessAfterInitialization
2.0 版本
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) &&
this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) {
if (logger.isInfoEnabled()) {
logger.info("Bean '" + beanName + "' of type [" + bean.getClass().getName() +
"] is not eligible for getting processed by all BeanPostProcessors " +
"(for example: not eligible for auto-proxying)");
}
}
return bean;
}
3.0 版本代码
org.springframework.context.support.PostProcessorRegistrationDelegate.BeanPostProcessorChecker#postProcessAfterInitialization
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) &&
this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) {
if (logger.isWarnEnabled()) {
Set<String> bppsInCreation = new LinkedHashSet<>(2);
for (String bppName : this.postProcessorNames) {
if (this.beanFactory.isCurrentlyInCreation(bppName)) {
bppsInCreation.add(bppName);
}
}
if (bppsInCreation.size() == 1) {
String bppName = bppsInCreation.iterator().next();
if (this.beanFactory.containsBeanDefinition(bppName) &&
beanName.equals(this.beanFactory.getBeanDefinition(bppName).getFactoryBeanName())) {
logger.warn("Bean '" + beanName + "' of type [" + bean.getClass().getName() +
"] is not eligible for getting processed by all BeanPostProcessors " +
"(for example: not eligible for auto-proxying). The currently created " +
"BeanPostProcessor " + bppsInCreation + " is declared through a non-static " +
"factory method on that class; consider declaring it as static instead.");
return bean;
}
}
logger.warn("Bean '" + beanName + "' of type [" + bean.getClass().getName() +
"] is not eligible for getting processed by all BeanPostProcessors " +
"(for example: not eligible for auto-proxying). Is this bean getting eagerly " +
"injected into a currently created BeanPostProcessor " + bppsInCreation + "? " +
"Check the corresponding BeanPostProcessor declaration and its dependencies.");
}
}
return bean;
}
private boolean isInfrastructureBean(@Nullable String beanName) {
if (beanName != null && this.beanFactory.containsBeanDefinition(beanName)) {
BeanDefinition bd = this.beanFactory.getBeanDefinition(beanName);
return (bd.getRole() == BeanDefinition.ROLE_INFRASTRUCTURE);
}
return false;
}
}
问题是由引入的spring-data-commons
包引起的。
结论
核心是因为org.springframework.aop.config.internalAutoProxyCreator
Bean (通常对应的实现类是AnnotationAwareAspectJAutoProxyCreator
) 会对所有的Bean进行增强,在这个过程中会出现要增强的Bean是BeanPostProcessor
比如org.springframework.data.web.config.ProjectingArgumentResolverRegistrar.ProjectingArgumentResolverBeanPostProcessor
,但他本身又在这个AnnotationAwareAspectJAutoProxyCreator
之后构建,导致在构建依赖的Bean 检测BeanPostProcessor
时,发现存在还没有完全完成构建的BeanPostProcessor
。
如果是自定义的BeanPostProcessor
,只要实现了Ordered
或者PriorityOrdered
接口就可以搞定,这个是因为
在Spring处理BeanPostProcessor
初始化时,会根据是否实现了PriorityOrdered
或者Ordered
接口来按照不同分组进行构建,其中org.springframework.aop.config.internalAutoProxyCreator
Bean 就是归类在Ordered
组。核心代码如下:
org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, org.springframework.context.support.AbstractApplicationContext)
方法(3.0版本)
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
// WARNING: Although it may appear that the body of this method can be easily
// refactored to avoid the use of multiple loops and multiple lists, the use
// of multiple lists and multiple passes over the names of processors is
// intentional. We must ensure that we honor the contracts for PriorityOrdered
// and Ordered processors. Specifically, we must NOT cause processors to be
// instantiated (via getBean() invocations) or registered in the ApplicationContext
// in the wrong order.
//
// Before submitting a pull request (PR) to change this method, please review the
// list of all declined PRs involving changes to PostProcessorRegistrationDelegate
// to ensure that your proposal does not result in a breaking change:
// https://github.com/spring-projects/spring-framework/issues?q=PostProcessorRegistrationDelegate+is%3Aclosed+label%3A%22status%3A+declined%22
// 获取待处理构建的BeanPostProcessor的名称列表
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
// Register BeanPostProcessorChecker that logs an info message when
// a bean is created during BeanPostProcessor instantiation, i.e. when
// a bean is not eligible for getting processed by all BeanPostProcessors.
//计算BeanPostProcessor的最终目标数量
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
//增加一个BeanPostProcessorChecker的检测器,就是这个检查器输出警告日志
beanFactory.addBeanPostProcessor(
new BeanPostProcessorChecker(beanFactory, postProcessorNames, beanProcessorTargetCount));
// Separate between BeanPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
// 按照PriorityOrdered、Ordered 和noneOrdered 3种情况分组,顺序完成初始化和注册。
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
//完成PriorityOrdered 的 BeanPostProcessor 的初始化
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// First, register the BeanPostProcessors that implement PriorityOrdered.
// 按照Ordered排序顺序,进行排序
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
//追加到beanFactory的BeanPostProcessors (前面的BeanPostProcessorChecker检查的就是这个)
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
// Next, register the BeanPostProcessors that implement Ordered.
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String ppName : orderedPostProcessorNames) {
//完成Ordered 的 BeanPostProcessor 的初始化
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(orderedPostProcessors, beanFactory);
//追加到beanFactory的BeanPostProcessors ,如果自定义的BeanPostProcessors 在这里还没完成初始化,而是要到后面才能开始初始化,这种情况就会出现BeanPostProcessorChecker的警告。
registerBeanPostProcessors(beanFactory, orderedPostProcessors);
// Now, register all regular BeanPostProcessors.
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String ppName : nonOrderedPostProcessorNames) {
//完成NonOrdered 的 BeanPostProcessor 的初始化
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
// Finally, re-register all internal BeanPostProcessors.
sortPostProcessors(internalPostProcessors, beanFactory);
//追加到beanFactory的BeanPostProcessors,完成所有的BeanPostProcessors构建和配置
registerBeanPostProcessors(beanFactory, internalPostProcessors);
// Re-register post-processor for detecting inner beans as ApplicationListeners,
// moving it to the end of the processor chain (for picking up proxies etc).
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
解决方案
方案1-将冲突的Bean 提升为InfrastructureBean
参考BeanPostProcessorChecker
的检测代码
if (!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) && this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount)
让触发警告的Bean
的!isInfrastructureBean(beanName)
不满足即可。
可以使用 @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Bean("txInterceptor")
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(){
return new TransactionInterceptor(tx , transactionAttributeSource()) ;
}
也可以使用实现BeanFactoryPostProcessor
接口的postProcessBeanFactory
方法来动态修改BeanDefinition
的Role
@Configuration
public class ConfigBeanPostProcessor implements BeanFactoryPostProcessor, PriorityOrdered {
static final Set<String> beanNames = Set.of(
// "projectingArgumentResolverBeanPostProcessor"
"com.alibaba.druid.spring.boot3.autoconfigure.stat.DruidSpringAopConfiguration",
"spring.datasource.druid-com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties",
"advisor"
);
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for(String defaultBeanName:beanNames){
if (beanFactory.containsBeanDefinition(defaultBeanName)) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(defaultBeanName);
//修改bean的角色为spring框架级别的bean
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
}
}
}
/**
* Get the order value of this object.
* <p>Higher values are interpreted as lower priority. As a consequence,
* the object with the lowest value has the highest priority (somewhat
* analogous to Servlet {@code load-on-startup} values).
* <p>Same order values will result in arbitrary sort positions for the
* affected objects.
*
* @return the order value
* @see #HIGHEST_PRECEDENCE
* @see #LOWEST_PRECEDENCE
*/
@Override public int getOrder() {
return HIGHEST_PRECEDENCE;
}
}
当然也可以二者结合使用。需要注意的是记得把相关的依赖Bean都要标记上,否则一旦有一个没标记上就仍会出现警告。
自定义的
BeanPostProcessor
最好还要实现Ordered
或者PriorityOrdered
接口,否则这个自定义的BeanPostProcessor
也会触发这个警告。
方案2
暂时没找到具体可行方案,思路大概就这几个方向
1、让AnnotationAwareAspectJAutoProxyCreator
不增强指定的BeanPostProcessor
2、提前完成指定BeanPostProcessor
的构建
3、将没有实现Ordered
或者PriorityOrdered
接口的BeanPostProcessor
通过特殊手段追加上。
(通过调试验证是可行,但代码实现目前没找到突破口)
其他相关资料
相关demo: spring-boot-upgrade-from2to3-demo
这个Demo可以直接直接展示这个警告日志的输出。
相关资料:
https://github.com/spring-projects/spring-boot/issues/38558