1、问题描述

org.springframework.context.annotation.ConfigurationClassPostProcessor enhanceConfigurationClasses
Cannot enhance @Configuration bean definition 'mybatisConfiguration' since its singleton instance has been created too early. 
The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'.

大致意思就是不能对加了@Configuration注解的MybatisConfiguration进行增强代理,因为这个类过早实例化了。

使用 JavaConfig 方式配置 Mybatis,启动时奇怪提示解决_mybatis-spring


2、解决方法

检查一下在@Configuraion注解的类中加了@Bean注解的方法,看是否有类是BeanDefinitionRegistryPostProcessor的子类,将该方法改为static修饰的静态方法即可。作者这边是用了MapperScannerConfigurer,后面将这个方法去掉,改为@MapperScan注解也能解决。


3、简单分析

@Bean注解的非静态方法返回的类为BeanDefinitionRegistryPostProcessor的子类,则会导致其所在的类过早实例化。如下mapperScannerConfigurer()方法。

package com.example.demo.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import javax.sql.DataSource;

//@MapperScan(value = {"com.example.demo.mapper"})
@Configuration
public class MybatisConfiguration {

	@Bean
	public DataSource dataSource() {
		DruidDataSource dataSource = new DruidDataSource();
		dataSource.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC");
		dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
		dataSource.setUsername("root");
		dataSource.setPassword("123456");
		return dataSource;
	}

	@Bean
	public SqlSessionFactoryBean sqlSessionFactoryBean() {
		SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
		sqlSessionFactoryBean.setDataSource(dataSource());
		sqlSessionFactoryBean.setMapperLocations(new ClassPathResource("mapper/UserMapper.xml"));
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setLogImpl(Jdk14LoggingImpl.class);
        sqlSessionFactoryBean.setConfiguration(configuration);
		return sqlSessionFactoryBean;
	}

	// MapperScannerConfigurer 类是 BeanDefinitionRegistryPostProcessor 的子类
	// 在对后置处理器 MapperScannerConfigurer 进行实例化时,会导致 MybatisConfiguration 也实例化
	// 如下图
	@Bean
	public MapperScannerConfigurer mapperScannerConfigurer() {
		MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
		mapperScannerConfigurer.setBasePackage("com.example.demo.mapper");
		return mapperScannerConfigurer;
	}

}

org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors中实例化后置处理器MapperScannerConfigurer

使用 JavaConfig 方式配置 Mybatis,启动时奇怪提示解决_mybatis-spring_02

实例化后置处理器MapperScannerConfigurer时,会先创建MybatisConfiguration。但时在这个时候创建,它的Class还未被代理。

使用 JavaConfig 方式配置 Mybatis,启动时奇怪提示解决_mybatis-spring_03

打印日志时,此时Spring的生命周期还未到Bean实例化的阶段,但容器中singletonObjects已经存在MybatisConfiguration

使用 JavaConfig 方式配置 Mybatis,启动时奇怪提示解决_mybatis-spring_04

如下图,MybatisConfigurationClass已经被代理了,那么从这里开始,MybatisConfiguration只要等到实例化时就可创建为代理类,但是它是单例的,前面实例化一次,不会再次实例化了,也就不能被增强代理了。

所以才会提示"Cannot enhance @Configuration bean definition 'mybatisConfiguration' since its singleton instance has been created too early."。

使用 JavaConfig 方式配置 Mybatis,启动时奇怪提示解决_mybatis-spring_05