MyBatis-Spring + @Configuration - 不能自动装配映射器 bean
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8999597/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
MyBatis-Spring + @Configuration - Can't autowire mapper beans
提问by Jason McClellan
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 @Configurationclasses.
我一直在尝试创建一个 Spring 项目,该项目使用 MyBatis 作为数据访问层,作为我团队的概念证明。如果可能的话,我真的很想避免使用 XML 配置,因此我尝试使用带注释的@Configuration类将所有内容连接在一起。
Everything seemsto be wired correctly, but my mapper beans are not being AutoWired into my service layer.
一切似乎都连接正确,但我的映射器 bean 没有自动连接到我的服务层。
In my example I'm trying to wire together a UserDao, User entity, and a UserService.
在我的示例中,我试图将 UserDao、User 实体和 UserService 连接在一起。
UserDao
用户道
public interface UserDao {
@Select("SELECT * FROM users WHERE id = #{userId}")
User get(@Param("userId") Integer userId);
}
User
用户
@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.
我使用两个配置类将它们连接在一起。
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;
}
}
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.
您可以阅读上述代码片段中最后两个方法上方的注释,以更深入地了解我是如何创建 UserDao bean 的。
Once I got all the configuration setup, I created a unit test to try to test the UserServiceusing the AnnotationConfigContextLoader, but was immediately hit with the following exception when trying to run the test:
完成所有配置设置后,我创建了一个单元测试以尝试使用AnnotationConfigContextLoader测试UserService,但在尝试运行测试时立即遇到以下异常:
Exception
例外
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: {}
After seeing that, I commented out the @Autowiredin the UserServiceand went back to my unit test and injected the ApplicationContextso I could inspect it, and the bean named "userDao" is in fact a MapperProxyinstance.
看到那之后,我注释掉@Autowired在UserService,又回到我的单元测试,并注入了ApplicationContext的,所以我可以检查它,并命名为“userDAO的”豆实际上是一个MapperProxy实例。
So, is my understanding of how the MapperFactoryBeanworks off track or is it just not very compatible with annotation driven configuration? Additionally, if anyone has any idea how to make the MapperScannerConfigurerwork correctly, I would appreciate it greatly!
那么,我对MapperFactoryBean如何偏离轨道的理解是否与注释驱动的配置不太兼容?此外,如果有人知道如何使MapperScannerConfigurer正常工作,我将不胜感激!
回答by Jason McClellan
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.
一段时间后,我能够解决问题,所以我会回答我自己的问题,以防其他人遇到类似的问题,因为那里没有大量可用信息并且需要进行一些搜索。
The problem comes down to the fact that MapperScannerConfigureris a BeanDefinitionRegistryPostProcessor. As it turns out, this is the same mechanism used to process the @Configurationfiles and register the @Beanannotated methods. Unfortunately, one BeanDefinitionRegistryPostProcessorcannot make use of another, according to this Spring Jira ticket: https://jira.springsource.org/browse/SPR-7868
问题归结为MapperScannerConfigurer是一个BeanDefinitionRegistryPostProcessor。事实证明,这与用于处理@Configuration文件和注册@Bean注释方法的机制相同。不幸的是,根据 Spring Jira 票证,一个BeanDefinitionRegistryPostProcessor不能使用另一个:https: //jira.springsource.org/browse/SPR-7868
The suggestion here was to create an XML configuration for the processor and then include an @ImportResourceannotation 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.
这里的建议是为处理器创建一个 XML 配置,然后在基于 Java 的配置中包含一个@ImportResource注释以将其引入。好吧,这个建议并不完全准确。如果您仍计划通过AnnotationConfigContextLoader引导您的配置,则不能简单地创建带有配置的 XML 文件并将其拉入基于 Java 的配置中。相反,您必须先恢复为通过 XML 加载配置,然后以“老式”方式为配置文件创建 bean。对我来说,这是非常微不足道的。
New Application Context
新的应用程序上下文
<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>
I then bootstrap the context container the traditional way, by providing a ContextConfigLocation. This works for me because the ApplicationContextConfigthat I reference in the above XML handles everything else - including component scanning which will pick up all of my other @Configurationfiles.
然后我通过提供ContextConfigLocation以传统方式引导上下文容器。这对我有用,因为我在上面的 XML 中引用的ApplicationContextConfig处理其他所有内容 - 包括组件扫描,它将获取我的所有其他@Configuration文件。
Once I did this, all of my problems went away. I was able to @Autowirethe UserDao as I expected and all was wonderful.
一旦我这样做了,我所有的问题都消失了。我能够按照我的预期@AutowireUserDao,一切都很棒。
Note:
笔记:
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 MapperProxyand would not @Autowire. However, I could get it to load by name using @Repository("userDao"), for what that's worth. I believe that the MapperFactoryBeansuffers from a similar problem as the MapperScannerConfigurerand is simply not compatible with @Configurationfiles, alas.
当我尝试通过创建MapperFactoryBean手动定义 UserDao 时,就像在我原始问题的代码示例中一样,创建了一个 UserDao bean,但它是MapperProxy类型,而不是@Autowire。但是,我可以使用@Repository("userDao")按名称加载它,这是值得的。我相信MapperFactoryBean遇到了与MapperScannerConfigurer类似的问题,并且与@Configuration文件不兼容,唉。
回答by pavanlapr
From mybatis.3.2.0 and mybatis-spring.1.2.0, instead of MapperFactoryBean, you can use MapperScan for this.
从 mybatis.3.2.0 和 mybatis-spring.1.2.0 开始,您可以使用 MapperScan 代替 MapperFactoryBean。
@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();
}
}
回答by Amit Goldstein
Another possible solution can be found in the jira ticked that Jason mentioned. Solved my problem and I did not have to use XML configuration which I try to avoid at any cost...
另一种可能的解决方案可以在 Jason 提到的 jira 中找到。解决了我的问题,我不必使用 XML 配置,我不惜一切代价试图避免...
https://jira.spring.io/browse/SPR-7868
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;
}
}

![spring 在Jenkins中出现Spring错误“名为'x'的Bean必须是[y]类型,但实际上是[$Proxy]类型”](/res/img/loading.gif)