MyBatis-Spring + @Configuration - 无法自动装配映射器bean

时间:2012-01-25 08:17:29

标签: spring mybatis

我一直在尝试创建一个使用MyBatis作为数据访问层的Spring项目,作为我团队的概念验证。我真的希望尽可能避免使用XML配置,所以我尝试使用带注释的 @Configuration 类将所有内容连接在一起。

所有似乎正确连接,但我的映射器bean没有自动连接到我的服务层。

在我的示例中,我正在尝试将UserDao,User实体和UserService连接在一起。

userDAO的

public interface UserDao {
    @Select("SELECT * FROM users WHERE id = #{userId}")
    User get(@Param("userId") Integer userId);
}

用户

@Component("User")
public class User implements Entity {
    public Integer userId;
    public String username;

    /** ... getters/setters ommitted **/
}

UserServiceImpl

@Service("UserService")
public class UserServiceImpl {
    private UserDao userDao = null;  

    public User getUserDetails(Integer userId) {
        return userDao.get(userId);        
    }

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

我使用两个配置类将它们连接在一起。

ApplicationContextConfig

@Configuration
@EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.ENABLED)
@Import(DefaultDataAccessConfig.class) // I'm importing this because I thought ordering might be important, otherwise I was hoping to just let the component scanning pull in additional configuration files
@ComponentScan(basePackages="com.example.gwtspringpoc.server",
               excludeFilters=@Filter(type=FilterType.ANNOTATION,
                                      value=Controller.class))
public class ApplicationContextConfig {
    /** No bean definitions needed here **/
}

DefaultDataAccessConfig

@Configuration
@EnableTransactionManagement
public class DefaultDataAccessConfig implements TransactionManagementConfigurer {    
    @Bean
    public DataSource dataSource() {
        OracleDataSource ods = null;
        try {
            ods = new OracleDataSource();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

        ods.setURL("jdbc:oracle:thin:@//localhost:9601/sid");
        ods.setUser("user");
        ods.setPassword("pass");        

        return ods;       
    }

    @Override
    @Bean(name="transactionManager")
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory() {
        SqlSessionFactoryBean sf = new SqlSessionFactoryBean();        
        sf.setDataSource(dataSource());    
        try {
            return (SqlSessionFactory) sf.getObject();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Bean
    public SqlSession sqlSessionTemplate() {
        return new SqlSessionTemplate(sqlSessionFactory());
    }    

    /*
     * This did not work at all. It seems to be configured correctly, but the UserDao bean never
     * got created at any stage, which was very disappointing as I was hoping not to have to
     * create a bean definition for each DAO manually
     */
    /*@Bean
    public static MapperScannerConfigurer mapperScannerConfig() {
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.ca.spna.gwtspringpoc.server.model.dao");      
        msc.setAnnotationClass(Repository.class);      

        return msc;
    }*/

    /*
     * Because the above code did not work, I decided to create the mapping manually.
     * This is most likely my problem - something about this setup. My understanding
     * is that the MapperFactoryBean once instantiated by Spring, will create a proxy
     * object of type UserDao with the name "userDao" that can be injected elsewhere.
     */
    @Bean
    public MapperFactoryBean<UserDao> userDao() {
        MapperFactoryBean<UserDao> mfb = new MapperFactoryBean<UserDao>();        
        mfb.setMapperInterface(UserDao.class);
        return mfb;
    }
}

您可以阅读上面代码段中最后两种方法的注释,以深入了解我如何创建UserDao bean。

完成所有配置后,我创建了一个单元测试,尝试使用 AnnotationConfigContextLoader 测试 UserService ,但在尝试时会立即遇到以下异常运行测试:

异常

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void com.example.gwtspringpoc.server.service.UserServiceImpl.setUserDao(com.example.gwtspringpoc.server.model.dao.UserDao); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.example.gwtspringpoc.server.model.dao.UserDao] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

看到之后,我在 UserService 中注释掉 @Autowired 并返回我的单元测试并注入了 ApplicationContext ,所以我可以检查它,名为“userDao”的bean实际上是 MapperProxy 实例。

那么,我是否理解 MapperFactoryBean 的工作方式是偏离轨道还是与注释驱动配置不兼容?另外,如果有人知道如何使 MapperScannerConfigurer 正常工作,我将非常感激!

4 个答案:

答案 0 :(得分:14)

经过一段时间后,我能够解决问题,所以我会回答我自己的问题,以防其他人碰到类似的东西,因为那里没有很多可用的信息并且需要进行一些搜索。

问题归结为 MapperScannerConfigurer BeanDefinitionRegistryPostProcessor 。事实证明,这与处理 @Configuration 文件并注册 @Beans 带注释的方法的机制相同。不幸的是,根据Spring Jira的这张票,一个 BeanDefinitionRegistryPostProcessor 无法使用另一个:https://jira.springsource.org/browse/SPR-7868

这里的建议是为处理器创建XML配置,然后在基于Java的配置中包含 @ImportResource 注释以将其拉入。那么,该建议并不完全准确。如果仍计划通过 AnnotationConfigContextLoader 引导配置,则不能简单地使用配置创建XML文件并将其拉入基于Java的配置。相反,您必须首先通过XML恢复加载配置,然后以“旧式”方式为配置文件创建bean。对我而言,这非常简单。

新申请背景

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd         http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">

    <!--
        Because MapperScannerConfigurer is a BeanDefinitionRegistryPostProcessor, it cannot be 
        configured via @Configuration files with a @Bean annotaiton, because those files are
        themselves configured via a BeanDefinitionRegistryPostProcessor which cannot spawn
        another one.
    -->
    <bean id="myBatisMapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
       <property name="basePackage" value="com.example.gwtspringpoc.server.model.dao"/>
       <property name="annotationClass" value="org.springframework.stereotype.Repository"/>
    </bean>

    <!-- 
        Load the rest of our configuration via our base configuration class
     -->
     <bean class="com.example.gwtspringpoc.server.spring.config.ApplicationContextConfig" />
</beans>

然后,我通过提供 ContextConfigLocation 来以传统方式引导上下文容器。这对我有用,因为我在上面的XML中引用的 ApplicationContextConfig 处理其他所有内容 - 包括组件扫描,它将拾取我所有其他 @Configuration 文件。

一旦我这样做,我的所有问题就消失了。我能像我预期的那样 @Autowire UserDao,一切都很精彩。

注意:

当我尝试通过创建 MapperFactoryBean 来手动定义UserDao时,就像在我原来的问题的代码示例中一样,创建了一个UserDao bean,但它的类型为 MapperProxy ,并且不是 @Autowire 。但是,我可以使用 @Repository(“userDao”)按名称加载它,这是值得的。我相信 MapperFactoryBean 会遇到与 MapperScannerConfigurer 类似的问题,并且与 @Configuration 文件完全不兼容,唉。

答案 1 :(得分:5)

来自mybatis.3.2.0和mybatis-spring.1.2.0, 而不是MapperFactoryBean,你可以使用MapperScan。

@Configuration 
@MapperScan("org.mybatis.spring.sample.mapper") 
public class AppConfig 
{   
    @Bean   
    public DataSource dataSource() 
    {     
      return new EmbeddedDatabaseBuilder().addScript("schema.sql").build();   
    }   
   @Bean   
   public DataSourceTransactionManager transactionManager() 
   {     
        return new DataSourceTransactionManager(dataSource());   
   }   
   @Bean   
   public SqlSessionFactory sqlSessionFactory() throws Exception 
   {     
       SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
       sessionFactory.setDataSource(dataSource());    
       return sessionFactory.getObject();   
   } 
}

答案 2 :(得分:2)

另一种可能的解决方案可以在Jason提到的jira中找到。解决了我的问题,我不必使用XML配置,我试图不惜一切代价避免......

https://jira.spring.io/browse/SPR-7868

@Configuration 
public class A implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {

    @Override
    public void postProcessBeanDefinitionRegistry(...) {
         ...
    }

    @Override
    public void postProcessBeanFactory(...) {
        ...
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

答案 3 :(得分:-1)

您必须有一个上下文:弹簧配置中的组件扫描,以启用@Component的自动检测。查看reference