Spring 4 + 使用 Java 8 的 MyBatis 集成问题

Spring 4 + MyBatis integration issue using Java 8

我在使用 Java 8 (1.8.0_60)、Spring 4.2.1 和 MyBatis 3.3.0

时遇到以下异常
Sep 29, 2015 11:02:58 AM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@246b179d: startup date [Tue Sep 29 11:02:58 EDT 2015]; root of context hierarchy
Sep 29, 2015 11:02:58 AM org.mybatis.spring.mapper.ClassPathMapperScanner checkCandidate
WARNING: Skipping MapperFactoryBean with name 'userMapper' and 'com.poc.test.spring4app.mapper.UserMapper' mapperInterface. Bean already defined with the same name!
Sep 29, 2015 11:02:58 AM org.mybatis.spring.mapper.ClassPathMapperScanner doScan
WARNING: No MyBatis mapper was found in '[com.poc.test.spring4app.mapper]' package. Please check your configuration.
Creating tables
Sep 29, 2015 11:02:59 AM org.springframework.context.annotation.AnnotationConfigApplicationContext refresh
WARNING: Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userMapper' defined in com.poc.test.spring4app.config.AppConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.poc.test.spring4app.mapper.UserMapper]: Factory method 'userMapper' threw exception; nested exception is org.apache.ibatis.binding.BindingException: Type interface com.poc.test.spring4app.mapper.UserMapper is not known to the MapperRegistry.
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1123)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1018)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getObject(AbstractBeanFactory.java:305)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:834)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537)
    at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84)
    at com.poc.test.spring4app.config.AppInitializer.main(AppInitializer.java:16)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.poc.test.spring4app.mapper.UserMapper]: Factory method 'userMapper' threw exception; nested exception is org.apache.ibatis.binding.BindingException: Type interface com.poc.test.spring4app.mapper.UserMapper is not known to the MapperRegistry.
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
    ... 13 more
Caused by: org.apache.ibatis.binding.BindingException: Type interface com.poc.test.spring4app.mapper.UserMapper is not known to the MapperRegistry.
    at org.apache.ibatis.binding.MapperRegistry.getMapper(MapperRegistry.java:47)
    at org.apache.ibatis.session.Configuration.getMapper(Configuration.java:675)
    at org.mybatis.spring.SqlSessionTemplate.getMapper(SqlSessionTemplate.java:293)
    at com.poc.test.spring4app.config.AppConfiguration.userMapper(AppConfiguration.java:51)
    at com.poc.test.spring4app.config.AppConfiguration$$EnhancerBySpringCGLIB$dd4b125.CGLIB$userMapper[=12=](<generated>)
    at com.poc.test.spring4app.config.AppConfiguration$$EnhancerBySpringCGLIB$dd4b125$$FastClassBySpringCGLIB$7c8295.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:318)
    at com.poc.test.spring4app.config.AppConfiguration$$EnhancerBySpringCGLIB$dd4b125.userMapper(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
    ... 14 more

该应用程序使用 JDK 1.7 和 Spring 3.2.2 运行 很好。但是由于 JDK 升级需要 Spring 4,我面临着这些兼容性问题。

下面是示例代码的详细信息。以下 POC 基于此 blog 中的示例。感谢迈克。如果我将 Spring 版本从 4.2.1 更改为 3.2.9,该应用程序可以正常工作。在 4.2.1 的情况下,我得到上述异常。

POM

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.poc.test</groupId>
<artifactId>spring4app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Spring4App</name>
<url>http://maven.apache.org</url>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring.version>4.2.1.RELEASE</spring.version>
    <spring-batch.version>3.0.5.RELEASE</spring-batch.version>
    <mybatisVersion>3.3.0</mybatisVersion>
    <mybatisSpringVersion>1.2.3</mybatisSpringVersion>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.batch</groupId>
        <artifactId>spring-batch-core</artifactId>
        <version>${spring-batch.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.batch</groupId>
        <artifactId>spring-batch-test</artifactId>
        <version>${spring-batch.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!-- DB RELATED -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>${mybatisVersion}</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>${mybatisSpringVersion}</version>
    </dependency>
    <!-- Binding for Log4J -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
    </dependency>
    <!-- H2Db related -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.3.167</version>
    </dependency>
</dependencies>
<build>
    <finalName>Spring4App</finalName>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.3</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.18.1</version>
            <configuration>
                <includes>
                    <include>**/${includeTestGroup}/**/*Test.java</include>
                </includes>
            </configuration>

        </plugin>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.5.3</version>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <finalName>Spring4App</finalName>
                <appendAssemblyId>false</appendAssemblyId>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>2.6</version>
            <executions>
                <execution>
                    <goals>
                        <goal>test-jar</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

AppInitializer

package com.poc.test.spring4app.config;

import java.util.List;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;

import com.poc.test.spring4app.domain.User;
import com.poc.test.spring4app.mapper.UserMapper;

public class AppInitializer {

    public static void main(String[] args) {
        AbstractApplicationContext context = null;
        try {
            context = new AnnotationConfigApplicationContext(
                    AppConfiguration.class);
            context.registerShutdownHook();
            UserMapper userMapper = context.getBean(UserMapper.class);
            List<User> users = userMapper.getAllUsers();
            System.out.println(users.toString());
            context.close();
        }
        catch (final Exception e) {
            if (context != null) {
                context.close();
            }
        }
    }
}

AppConfiguration

package com.poc.test.spring4app.config;

import javax.sql.DataSource;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;

import com.poc.test.spring4app.mapper.UserMapper;

@Configuration
@MapperScan("com.poc.test.spring4app.mapper")
public class AppConfiguration {

    @Bean
    public DataSource dataSource() {
        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
        dataSource.setDriverClass(org.h2.Driver.class);
        dataSource.setUsername("sa");
        dataSource.setUrl("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
        dataSource.setPassword("");
        // create a table and populate some data
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        System.out.println("Creating tables");
        jdbcTemplate.execute("drop table users if exists");
        jdbcTemplate.execute("create table users(id serial, firstName varchar(255), lastName varchar(255), email varchar(255))");
        jdbcTemplate.update("INSERT INTO users(firstName, lastName, email) values (?,?,?)", "FirstName", "LastName", "FirstName.LastName@xyz.abc");
        return dataSource;
     }

    @Bean
    public DataSourceTransactionManager transactionManager() {
         return new DataSourceTransactionManager(dataSource());
    }
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        sessionFactory.setTypeAliasesPackage("com.poc.test.spring4app.mapper");
        return sessionFactory;
     }

    @Bean
    public UserMapper userMapper() throws Exception {
        final SqlSessionTemplate sessionTemplate = new SqlSessionTemplate(sqlSessionFactory().getObject());
        return sessionTemplate.getMapper(UserMapper.class);
    }
}

用户映射器

package com.poc.test.spring4app.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import com.poc.test.spring4app.domain.User;

public interface UserMapper {

    @Select("select id, firstName, lastName, email from users")
    @Results(value={
            @Result(column="id", property="id"),
            @Result(column="firstName", property="firstName"),
            @Result(column="lastName", property="lastName"),
            @Result(column="email", property="email")
    })
    List<User> getAllUsers();

}

用户

package com.poc.test.spring4app.domain;

import java.io.Serializable;

public class User implements Serializable {

    private static final long serialVersionUID = 1L;
    private long id;
    private String firstName;
    private String lastName;
    private String email;
    /**
     * @return the id
     */
    public long getId() {
        return id;
    }
    /**
     * @param id the id to set
     */
    public void setId(long id) {
        this.id = id;
    }
    /**
     * @return the firstName
     */
    public String getFirstName() {
        return firstName;
    }
    /**
     * @param firstName the firstName to set
     */
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    /**
     * @return the lastName
     */
    public String getLastName() {
        return lastName;
    }
    /**
     * @param lastName the lastName to set
     */
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    /**
     * @return the email
     */
    public String getEmail() {
        return email;
    }
    /**
     * @param email the email to set
     */
    public void setEmail(String email) {
        this.email = email;
    }

    // getters and setters

 }

我相信这可能是您的问题:

 WARNING: Skipping MapperFactoryBean with name 'userMapper' and  'com.poc.test.spring4app.mapper.UserMapper' mapperInterface. Bean already defined with the same name!

尝试消除这种口是心非。

问题解决方法:

使用 Spring 4 和 MyBatis 3,如果您已经使用 @MapperScan 来扫描 MyBatis Mapper 实现,则不应显式定义 Mappers。 bean 的实例化是自动处理的。