首页 > 代码库 > spring+mybati java config配置引起的bean相互引用日志报警告问题

spring+mybati java config配置引起的bean相互引用日志报警告问题

摘要: Error creating bean with name ‘XXX‘: Requested bean is currently in creation: Is there an unresolvable circular reference?; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:

如果把MapperScan单独配置,就不会有警告

 

  • 报循环引用警告的配置


/** * 数据源配置 * 数据源配置个人觉得还是xml好些。用xml配置改动增加配置只需重启 *  * @author doctor * * @time 2015年3月3日 下午2:57:10 */@Configurationpublic class DataSourceConfig {    @Bean    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {        return new PropertySourcesPlaceholderConfigurer();    }    @Target(ElementType.TYPE)    @Retention(RetentionPolicy.RUNTIME)    @Documented    public @interface DbH2 {    }    /**     * H2 数据原配置     * 不用import ,原因文档见:{@code Configuration}文档中的With nested  Configuration  classes部分     * spring源码如何处理见:     * {@link org.springframework.context.annotation.ConfigurationClassParser#processMemberClasses(ConfigurationClass, SourceClass) }     *      * @author doctor     *     * @time 2015年3月3日 下午3:26:15     */    @Configuration    @MapperScan(basePackages = { "com.doctor.spring4.common.mapper" }, annotationClass = DbH2.class, sqlSessionFactoryRef = "dbH2SqlSessionFactory")    @PropertySource("classpath:/spring4_2015Pro/jdbc-H2.properties")    static class MybatisH2Config {        @Value("${jdbc.H2.url}")        private String url;        @Value("${jdbc.H2.user}")        private String user;        @Value("${jdbc.H2.password}")        private String password;        @Value("${jdbc.H2.driverClassName}")        private String driverClassName;        @Value("${jdbc.H2.initialSize}")        private int initialSize;        @Value("${jdbc.H2.minIdle}")        private int minIdle;        @Value("${jdbc.H2.maxActive}")        private int maxActive;        @Value("${jdbc.H2.maxWait}")        private long maxWait;        @Value("${jdbc.H2.minEvictableIdleTimeMillis}")        private long minEvictableIdleTimeMillis;        @Value("${jdbc.H2.timeBetweenEvictionRunsMillis}")        private long timeBetweenEvictionRunsMillis;        @Value("${jdbc.H2.validationQuery}")        private String validationQuery;        @Value("${jdbc.H2.testWhileIdle}")        private boolean testWhileIdle;        @Value("${jdbc.H2.testOnBorrow}")        private boolean testOnBorrow;        @Value("${jdbc.H2.testOnReturn}")        private boolean testOnReturn;        @Value("${jdbc.H2.poolPreparedStatements}")        private boolean poolPreparedStatements;        @Value("${jdbc.H2.maxPoolPreparedStatementPerConnectionSize}")        private int maxPoolPreparedStatementPerConnectionSize;        @Bean(name = "dbH2DataSource", initMethod = "init", destroyMethod = "close")        public DataSource dbH2DataSource() {            DruidDataSource druidDataSource = new DruidDataSource();            druidDataSource.setUrl(url);            druidDataSource.setUsername(user);            druidDataSource.setPassword(password);            druidDataSource.setDriverClassName(driverClassName);            druidDataSource.setInitialSize(initialSize);            druidDataSource.setMinIdle(minIdle);            druidDataSource.setMaxActive(maxActive);            druidDataSource.setMaxWait(maxWait);            druidDataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);            druidDataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);            druidDataSource.setValidationQuery(validationQuery);            druidDataSource.setTestWhileIdle(testWhileIdle);            druidDataSource.setTestOnBorrow(testOnBorrow);            druidDataSource.setTestOnReturn(testOnReturn);            druidDataSource.setPoolPreparedStatements(poolPreparedStatements);            druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);            return druidDataSource;        }        @Bean(name = "dbH2SqlSessionFactory")        @Resource(name = "dbH2DataSource")        public SqlSessionFactory DbH2SqlSessionFactory(DataSource dataSource) throws Exception {            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();            sqlSessionFactoryBean.setDataSource(dataSource);            sqlSessionFactoryBean.setTypeHandlersPackage("");            sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("/spring4_2015Config/mybatis-db-config.xml"));            return sqlSessionFactoryBean.getObject();        }    }}

dbH2DataSource,dbH2SqlSessionFactory,MapperScan有依赖关系.

  • 如果把MapperScan单独配置,就不会有警告,例如:

 

/** * 数据源配置 * 数据源配置个人觉得还是xml好些。用xml配置改动增加配置只需重启 *  * @author doctor * * @time 2015年3月3日 下午2:57:10 */@Configurationpublic class DataSourceConfig {    @Bean    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {        return new PropertySourcesPlaceholderConfigurer();    }    @Target(ElementType.TYPE)    @Retention(RetentionPolicy.RUNTIME)    @Documented    public @interface DbH2 {    }    /**     * H2 数据原配置     * 不用import ,原因文档见:{@code Configuration}文档中的With nested  Configuration  classes部分     * spring源码如何处理见:     * {@link org.springframework.context.annotation.ConfigurationClassParser#processMemberClasses(ConfigurationClass, SourceClass) }     *      * @author doctor     *     * @time 2015年3月3日 下午3:26:15     */    @Configuration    @PropertySource("classpath:/spring4_2015Pro/jdbc-H2.properties")    static class MybatisH2Config {        @Value("${jdbc.H2.url}")        private String url;        @Value("${jdbc.H2.user}")        private String user;        @Value("${jdbc.H2.password}")        private String password;        @Value("${jdbc.H2.driverClassName}")        private String driverClassName;        @Value("${jdbc.H2.initialSize}")        private int initialSize;        @Value("${jdbc.H2.minIdle}")        private int minIdle;        @Value("${jdbc.H2.maxActive}")        private int maxActive;        @Value("${jdbc.H2.maxWait}")        private long maxWait;        @Value("${jdbc.H2.minEvictableIdleTimeMillis}")        private long minEvictableIdleTimeMillis;        @Value("${jdbc.H2.timeBetweenEvictionRunsMillis}")        private long timeBetweenEvictionRunsMillis;        @Value("${jdbc.H2.validationQuery}")        private String validationQuery;        @Value("${jdbc.H2.testWhileIdle}")        private boolean testWhileIdle;        @Value("${jdbc.H2.testOnBorrow}")        private boolean testOnBorrow;        @Value("${jdbc.H2.testOnReturn}")        private boolean testOnReturn;        @Value("${jdbc.H2.poolPreparedStatements}")        private boolean poolPreparedStatements;        @Value("${jdbc.H2.maxPoolPreparedStatementPerConnectionSize}")        private int maxPoolPreparedStatementPerConnectionSize;        @Bean(name = "dbH2DataSource", initMethod = "init", destroyMethod = "close")        public DataSource dbH2DataSource() {            DruidDataSource druidDataSource = new DruidDataSource();            druidDataSource.setUrl(url);            druidDataSource.setUsername(user);            druidDataSource.setPassword(password);            druidDataSource.setDriverClassName(driverClassName);            druidDataSource.setInitialSize(initialSize);            druidDataSource.setMinIdle(minIdle);            druidDataSource.setMaxActive(maxActive);            druidDataSource.setMaxWait(maxWait);            druidDataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);            druidDataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);            druidDataSource.setValidationQuery(validationQuery);            druidDataSource.setTestWhileIdle(testWhileIdle);            druidDataSource.setTestOnBorrow(testOnBorrow);            druidDataSource.setTestOnReturn(testOnReturn);            druidDataSource.setPoolPreparedStatements(poolPreparedStatements);            druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);            return druidDataSource;        }        @Bean(name = "dbH2SqlSessionFactory")        @Resource(name = "dbH2DataSource")        public SqlSessionFactory DbH2SqlSessionFactory(DataSource dataSource) throws Exception {            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();            sqlSessionFactoryBean.setDataSource(dataSource);            sqlSessionFactoryBean.setTypeHandlersPackage("");            sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("/spring4_2015Config/mybatis-db-config.xml"));            return sqlSessionFactoryBean.getObject();        }    }    @Configuration    @MapperScan(basePackages = { "com.doctor.spring4.common.mapper" }, annotationClass = DbH2.class, sqlSessionFactoryRef = "dbH2SqlSessionFactory")    static class MyBatisMapperConfig {        // MapperScan注解不要和数据源定义的配置写在一起,(如MybatisH2Config配置上),        // 否此会导致循环引用初始化bean问题.        // 看来xml配置还是有优势的        // 03-11 17:01:50.010 main WARN o.s.b.f.s.DefaultListableBeanFactory - Bean creation        // exception on FactoryBean type check:        // org.springframework.beans.factory.BeanCreationException: Error creating bean with name        // ‘userMapper‘ defined in file        // [/home/cui/workspace/spring4-2015/target/classes/com/doctor/spring4/common/mapper/UserMapper.class]:        // Cannot resolve reference to bean ‘dbH2SqlSessionFactory‘ while setting bean property        // ‘sqlSessionFactory‘; nested exception is        // org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean        // with name ‘dbH2SqlSessionFactory‘: Requested bean is currently in creation: Is there an        // unresolvable circular reference?    }}
  • 但是最好的配置还是如下:

/** * 数据源配置 * 数据源配置个人觉得还是xml好些。用xml配置改动增加配置只需重启 *  * 注意:MybatisH2Config 类中两个有依赖关系的bean注入方法,不要用set方法形式注入, * 有可能导致注入循环引用问题.(@MapperScan 注解在MybatisH2Config,而且注解依赖里面的bean定义). * @author doctor * * @time 2015年3月3日 下午2:57:10 */@Configurationpublic class DataSourceConfig {    @Bean    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {        return new PropertySourcesPlaceholderConfigurer();    }    @Target(ElementType.TYPE)    @Retention(RetentionPolicy.RUNTIME)    @Documented    public @interface DbH2 {    }    /**     * H2 数据原配置     * 不用import ,原因文档见:{@code Configuration}文档中的With nested  Configuration  classes部分     * spring源码如何处理见:     * {@link org.springframework.context.annotation.ConfigurationClassParser#processMemberClasses(ConfigurationClass, SourceClass) }     *      * @author doctor     *     * @time 2015年3月3日 下午3:26:15     */    @Configuration    @MapperScan(basePackages = { "com.doctor.spring4.common.mapper" }, annotationClass = DbH2.class, sqlSessionFactoryRef = "dbH2SqlSessionFactory")    @PropertySource("classpath:/spring4_2015Pro/jdbc-H2.properties")    static class MybatisH2Config {        @Value("${jdbc.H2.url}")        private String url;        @Value("${jdbc.H2.user}")        private String user;        @Value("${jdbc.H2.password}")        private String password;        @Value("${jdbc.H2.driverClassName}")        private String driverClassName;        @Value("${jdbc.H2.initialSize}")        private int initialSize;        @Value("${jdbc.H2.minIdle}")        private int minIdle;        @Value("${jdbc.H2.maxActive}")        private int maxActive;        @Value("${jdbc.H2.maxWait}")        private long maxWait;        @Value("${jdbc.H2.minEvictableIdleTimeMillis}")        private long minEvictableIdleTimeMillis;        @Value("${jdbc.H2.timeBetweenEvictionRunsMillis}")        private long timeBetweenEvictionRunsMillis;        @Value("${jdbc.H2.validationQuery}")        private String validationQuery;        @Value("${jdbc.H2.testWhileIdle}")        private boolean testWhileIdle;        @Value("${jdbc.H2.testOnBorrow}")        private boolean testOnBorrow;        @Value("${jdbc.H2.testOnReturn}")        private boolean testOnReturn;        @Value("${jdbc.H2.poolPreparedStatements}")        private boolean poolPreparedStatements;        @Value("${jdbc.H2.maxPoolPreparedStatementPerConnectionSize}")        private int maxPoolPreparedStatementPerConnectionSize;        @Bean(name = "dbH2DataSource", initMethod = "init", destroyMethod = "close")        public DataSource dbH2DataSource() {            DruidDataSource druidDataSource = new DruidDataSource();            druidDataSource.setUrl(url);            druidDataSource.setUsername(user);            druidDataSource.setPassword(password);            druidDataSource.setDriverClassName(driverClassName);            druidDataSource.setInitialSize(initialSize);            druidDataSource.setMinIdle(minIdle);            druidDataSource.setMaxActive(maxActive);            druidDataSource.setMaxWait(maxWait);            druidDataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);            druidDataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);            druidDataSource.setValidationQuery(validationQuery);            druidDataSource.setTestWhileIdle(testWhileIdle);            druidDataSource.setTestOnBorrow(testOnBorrow);            druidDataSource.setTestOnReturn(testOnReturn);            druidDataSource.setPoolPreparedStatements(poolPreparedStatements);            druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);            return druidDataSource;        }        @Bean(name = "dbH2SqlSessionFactory")        public SqlSessionFactory DbH2SqlSessionFactory() throws Exception {            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();            sqlSessionFactoryBean.setDataSource(dbH2DataSource());            sqlSessionFactoryBean.setTypeHandlersPackage("");            sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("/spring4_2015Config/mybatis-db-config.xml"));            return sqlSessionFactoryBean.getObject();        }    }}

https://my.oschina.net/doctor2014/blog/386431

技术分享

 

Requested bean is currently in creation: Is there an unresolvable circular reference?

技术分享
 getBean的时候由于bean之间存在循环依赖出现类似的错误,先做一个简单实验模拟一下这个异常出现的原因:
bean.xml配置如下:
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
       default-autowire="byName">
<bean id="a" class="BeanA" scope="prototype"/>
<bean id="b" class="BeanB" scope="prototype"/>
</beans>
 
BeanA类如下:
public class BeanA 
{
private BeanB b;
public BeanB getB() {
return b;
}
public void setB(BeanB b) {
this.b = b;
}
}
 
BeanB类如下:
public class BeanB 
{
private BeanA a;
public BeanA getA() {
return a;
}
public void setA(BeanA a) {
this.a = a;
}
}
程序执行的堆栈信息:
技术分享
 
bean在初始化的同时会初始化相应的属性(byName),a在初始化的时候会创建a.b,a.b在初始化的时候会创建a.b.a,形成了一个环,所以在AbstractBeanFactory.doGetBean(这里会出现递归调用) 时候 ,走到:
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
会抛出一个异常:Requested bean is currently in creation: Is there an unresolvable circular reference,然后scope是singlton或者其他的时候bean的创建过程不一样,不会有一个这样子的判断,因而不会抛出这样子的错误。
因为scope=prototype,所以每次请求对应不同的bean,所以在创建的时候有一个依赖的先后顺序,而如果bean配置成singleton,则属性间初始化的顺序可以无所谓了,所有的bean都只有一份,所以在bean是singleton时候不会报unresolvable circular reference的错误。

 

spring+mybati java config配置引起的bean相互引用日志报警告问题