MyBatis-Spring + @Configuration-无法自动连线映射器bean [英] MyBatis-Spring + @Configuration - Can't autowire mapper beans

查看:106
本文介绍了MyBatis-Spring + @Configuration-无法自动连线映射器bean的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have been trying to create a Spring project that uses MyBatis for the data access layer as a proof of concept for my team. I really want to avoid XML configuration if at all possible, so I'm attempting to wire everything together using annotated @Configuration classes.

一切似乎都正确连接了,但是我的mapper bean没有被自动连接到我的服务层中.

Everything seems to be wired correctly, but my mapper beans are not being AutoWired into my service layer.

在我的示例中,我试图将UserDao,User实体和UserService连接在一起.

In my example I'm trying to wire together a UserDao, User entity, and a UserService.

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;
    }
}

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

I'm wiring these together using two configuration classes.

@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.

You can read the comments above the last two methods in the above code snippet to gain more insight into how I'm creating the UserDao bean.

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

Once I got all the configuration setup, I created a unit test to try to test the UserService using the AnnotationConfigContextLoader, but was immediately hit with the following exception when trying to run the test:

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 实例.

After seeing that, I commented out the @Autowired in the UserService and went back to my unit test and injected the ApplicationContext so I could inspect it, and the bean named "userDao" is in fact a MapperProxy instance.

那么,我对 MapperFactoryBean 的工作方式是否了解还是只是与注释驱动的配置不太兼容?此外,如果有人对如何使 MapperScannerConfigurer 正常工作有任何想法,我将不胜感激!

So, is my understanding of how the MapperFactoryBean works off track or is it just not very compatible with annotation driven configuration? Additionally, if anyone has any idea how to make the MapperScannerConfigurer work correctly, I would appreciate it greatly!

推荐答案

过一段时间后,我就能弄清楚事情了,所以我会回答我自己的问题,以防其他人遇到类似的问题而不是整个问题.那里有很多信息,需要一些搜索.

After some time I was able to figure things out, so I'll answer my own question in case others run into something similar as there wasn't a whole lot of information available out there and it took some searching.

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

The problem comes down to the fact that MapperScannerConfigurer is a BeanDefinitionRegistryPostProcessor. As it turns out, this is the same mechanism used to process the @Configuration files and register the @Bean annotated methods. Unfortunately, one BeanDefinitionRegistryPostProcessor cannot make use of another, according to this Spring Jira ticket: https://jira.springsource.org/browse/SPR-7868

这里的建议是为处理器创建XML配置,然后在基于Java的配置中包含 @ImportResource 注释以将其引入.如果您仍打算通过 AnnotationConfigContextLoader 引导配置,则不能简单地使用该配置创建XML文件并将其拉入基于Java的配置.相反,您必须还原为首先通过XML加载配置,然后以旧式"方式为您的配置文件创建Bean.对我来说,这是微不足道的.

The suggestion here was to create an XML configuration for the processor and then include an @ImportResource annotation in the Java based configuration to pull it in. Well, that suggestion isn't fully accurate. You can't simply create an XML file with the configuration and pull it into the Java based configuration if you are still planning to have your configuration bootstrapped via an AnnotationConfigContextLoader. Instead, you have to revert back to loading your configuration via XML first and then creating a bean for your configuration file(s) the "old-fashion" way. For me this, was pretty trivial.

新应用上下文

<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 文件.

I then bootstrap the context container the traditional way, by providing a ContextConfigLocation. This works for me because the ApplicationContextConfig that I reference in the above XML handles everything else - including component scanning which will pick up all of my other @Configuration files.

一旦我这样做,我所有的问题都会消失.正如我所期望的,我能够 @Autowire UserDao,一切都很棒.

Once I did this, all of my problems went away. I was able to @Autowire the UserDao as I expected and all was wonderful.

注意:

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

When I tried manually defining UserDao by creating a MapperFactoryBean, like in my original question's code example, there was a UserDao bean created but it was of type MapperProxy and would not @Autowire. However, I could get it to load by name using @Repository("userDao"), for what that's worth. I believe that the MapperFactoryBean suffers from a similar problem as the MapperScannerConfigurer and is simply not compatible with @Configuration files, alas.

这篇关于MyBatis-Spring + @Configuration-无法自动连线映射器bean的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆