记一次Spring配置事故

在引入Spring的Validated时,需要声明如下bean:
 
@Bean
public
MethodValidationPostProcessor methodValidationPostProcessor() { return new MethodValidationPostProcessor(); }

 

出于偷懒,放在了如下的一个初始化中:
@Configuration
public class ConfigService implements WebMvcConfigurer {
    @Resource
    private LogService logService;
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}

 

配置好后,@Validated生效了,但是aop,事务等出现异常。启动日志如下:
2018-08-25 20:03:09 [INFO] [main] [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)
2018-08-25 20:03:09 [INFO] [main] [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker][328] Bean 'mybatis-org.mybatis.spring.boot.autoconfigure.MybatisProperties' of type [org.mybatis.spring.boot.autoconfigure.MybatisProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2018-08-25 20:03:09 [INFO] [main] [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker][328] Bean 'org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration' of type [org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration$$EnhancerBySpringCGLIB$$f57f05de] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2018-08-25 20:03:09 [INFO] [main] [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker][328] Bean 'org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari' of type [org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2018-08-25 20:03:09 [INFO] [main] [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)
2018-08-25 20:03:09 [INFO] [main] [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker][328] Bean 'dataSource' of type [com.zaxxer.hikari.HikariDataSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2018-08-25 20:03:10 [INFO] [main] [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker][328] Bean 'sqlSessionFactory' of type [org.apache.ibatis.session.defaults.DefaultSqlSessionFactory] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2018-08-25 20:03:10 [INFO] [main] [o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker][328] Bean 'sqlSessionTemplate' of type [org.mybatis.spring.SqlSessionTemplate] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

 

在如下代码块中
@Configuration
public class ConfigService implements WebMvcConfigurer {
    @Resource
    private LogService logService;
 
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}

 

我们注入了业务定义的bean,而这些bean是由最低优先级的BeanPostProcessor来加载并完成初始化的。但此时,为了加载其中的MethodValidationPostProcessor,导致不得不优先装载低优先级bean,此时,aop处理器,数据库处理器等都未完成装载,故由这部分业务bean牵扯到的相关逻辑的aop初始化,注解事务初始化,都事实上失败了。但spring就提示了一个INFO级别的提示,然后剩下的bean由最低优先级的BeanPostProcessor正常处理。
 
问题找到后,解决的方式很简单,由框架层初始化的bean,不要牵扯到业务层。不然即便初始化成功,也会导致一些模块因为顺序的缘故,未完成合适的处理流程,比如aop。
针对我这里的问题,如下解决:
@Configuration
public class MethodValidation {
 
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}

 重新创建一个类,单独初始化此类即可。 

 
 
那这个问题的根本原因是什么呢?
从Spring上下文初始化的过程可以看出来。
在注册BeanPostProcessor时,会区分出priorityOrderedPostProcessor,internalPostProcessor,orderedPostProcessor,noOrderedPostProcessor等4个级别的BeanPostProcessor。
按注册顺序为:priorityOrderedPostProcessor -> orderedPostProcessor -> noOrderedPostProcessor -> internalPostProcessor。
每个BeanPostProcessor会先实例化,然后再进行注册。在实例化完成后,会进行实例化后置处理。
在这个后置处理中,有一个关键的BeanPostProcessor是BeanPostProcessorChecker。在其后置处理逻辑中会检查并抛出上述问题的错误提示。
其处理代码如下:
        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;
        }

这部分代码逻辑如下:

当实例化的bean不为BeanPostProcessor,且不是归属框架类的bean定义,且当前已注册的BeanPostProcessor比初始化时登记的数量小(代表还未注册所有的BeanPostProcessor到beanFactory中,因为不同级别的BeanPostProcessor是分别处理的,故存在数量逐渐增加的这么一个过程)时,发起bean告警,提示该bean可能是不完全正确的,因为未经过所有BeanPostProcessor的前置后置处理。

 

上面的问题,是因为在orderedPostProcessor集合初始化过程中引入了业务bean的初始化,导致部分业务bean提前初始化没有被所有的BeanPostProcessor前后置处理,使得事务失效。

 

解决办法有很多种,上面列出来的单独一个类是一种方式,还可以在@Bean修饰的方法上增加static修饰。

因为@Configuration注解会对修饰的类进行CGLIB增强,会跳过static修饰的方法增强处理,不会导致业务bean的提前初始化;

在单独的类中,也可以移除@configuration注解,spring会将@Bean修饰的方法视为工厂方法,此时也不会导致注入的业务bean被提前初始化。

 

参考资料:

https://docs.spring.io/spring/docs/4.2.x/javadoc-api/org/springframework/context/annotation/Bean.html

 
posted @ 2018-08-25 23:32  飞昂之雪  阅读(22238)  评论(0编辑  收藏  举报