一. 测试代码
//实体类 public class User { private Integer id; private String name; private Integer age; private String email; private SexEnum sex; //getter / setter } public enum SexEnum { Male("Male", "男"), Female("Female", "女"); private String code; private String desc; SexEnum(String code, String desc){ this.code = code; this.desc = desc; } } //Repository @Repository public interface UserMapper { public User getById(Integer id); public List<User> getByAge(Integer age); public int insert(User user); } @SpringBootApplication @MapperScan("com.study.demo.mybatis.mapper") public class MybatisApplication { public static void main(String[] args) { SpringApplication.run(MybatisApplication.class, args); } } @Test public void getById(){ System.out.println("getById 开始执行..."); User user = userMapper.getById(1); System.out.println(user); System.out.println("getById 结束执行..."); }
mapper.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.study.demo.mybatis.mapper.UserMapper"> <insert id="insert"> insert into user (name,age,email,sex) values(#{name}, #{age}, #{email}, #{sex}) </insert> <select id="getById" resultType="com.study.demo.mybatis.vo.User"> select * from user where id = #{id} </select> <select id="getByAge" resultType="com.study.demo.mybatis.vo.User"> select * from user where age = #{age} </select> </mapper>
配置文件:
spring: datasource: #type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/deco?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false username: root password: 123456 mybatis: mapper-locations: classpath:mapper/**Mapper.xml
@MapperScan("com.study.demo.mybatis.mapper") 主要做了两件事情:
1. 根据 "com.study.demo.mybatis.mapper" 配置进行mapper.java文件扫描.
此处扫描到的就是 SchoolMapper.java 和 UserMapper.java 两个文件
2. 为扫描到的文件进行 BeanDefinition 注册
//关键代码:
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); if (logger.isDebugEnabled()) { logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface"); } // the mapper interface is the original class of the bean // but, the actual class of the bean is MapperFactoryBean definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59 definition.setBeanClass(this.mapperFactoryBean.getClass()); ...... }
在进入此方法之前, beanDefinitions 已经通过扫描得到,
beanName | beanClass |
schoolMapper | com.study.demo.mybatis.mapper.SchoolMapper |
userMapper | com.study.demo.mybatis.mapper.UserMapper |
进入方法后, 会执行以下代码, 对 beanClass 进行重新赋值:
definition.setBeanClass(this.mapperFactoryBean.getClass());
这里的 mapperFactoryBean 是 ClassPathMapperScanner 的一个私有属性:
private MapperFactoryBean<?> mapperFactoryBean = new MapperFactoryBean<Object>();
也就是说, 后面对 beanName = userMapper 进行创建的时候, 会使用到 MapperFactoryBean
二. MapperFactoryBean
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { private Class<T> mapperInterface; private boolean addToConfig = true; public MapperFactoryBean() { //intentionally empty } public MapperFactoryBean(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } /** * {@inheritDoc} */ @Override protected void checkDaoConfig() { super.checkDaoConfig(); notNull(this.mapperInterface, "Property 'mapperInterface' is required"); Configuration configuration = getSqlSession().getConfiguration(); if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) { try { configuration.addMapper(this.mapperInterface); } catch (Exception e) { logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e); throw new IllegalArgumentException(e); } finally { ErrorContext.instance().reset(); } } } /** * {@inheritDoc} */ @Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } /** * {@inheritDoc} */ @Override public Class<T> getObjectType() { return this.mapperInterface; } /** * {@inheritDoc} */ @Override public boolean isSingleton() { return true; } //------------- mutators -------------- /** * Sets the mapper interface of the MyBatis mapper * * @param mapperInterface class of the interface */ public void setMapperInterface(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } /** * Return the mapper interface of the MyBatis mapper * * @return class of the interface */ public Class<T> getMapperInterface() { return mapperInterface; } /** * If addToConfig is false the mapper will not be added to MyBatis. This means * it must have been included in mybatis-config.xml. * <p/> * If it is true, the mapper will be added to MyBatis in the case it is not already * registered. * <p/> * By default addToCofig is true. * * @param addToConfig */ public void setAddToConfig(boolean addToConfig) { this.addToConfig = addToConfig; } /** * Return the flag for addition into MyBatis config. * * @return true if the mapper will be added to MyBatis in the case it is not already * registered. */ public boolean isAddToConfig() { return addToConfig; } }
从代码上看, 实现了 FactoryBean 接口, 他是一个 工厂bean.
在创建 userMapper 的时候, 就会调用 MapperFactoryBean 的 getObject() 方法.
md版本: https://files.cnblogs.com/files/elvinle/mybatis.zip