【二】MyBatis-Spring最全源码详解之Mapper的自动注入

目录

1. MapperFactoryBean的实例化

1.1 Springboot自动配置与@Bean方法的扫描

1.2 sqlSessionFactorybean与SqlSessionTemplate的创建

2. Mapper的依赖注入

2.1 从MapperFactoryBean中获取对象

2.2 postProcessObjectFromFactoryBean

3. 总结

4. 附录:项目文档


上一篇文章重点介绍了@MapperScan("backage")将指定backage中XXXMapper类解析成beanDefinition,随后修改了beanDefinition中beanClass属性,将属性值从XXXMapper替换为了MapperFactoryBean类型。本节将深入分析MapperFactoryBean的实例化和Mapper的依赖注入过程。如果你对bean的实例化过程和依赖注入过程不太熟悉的话,我强烈建议你阅读之前的两篇文章:

《【六】Spring IoC 最全源码详解之bean实例化过程》和《【七】Spring IoC 最全源码详解之bean的依赖注入》。如果你已经比较熟悉Spring的IoC流程了,那么我们就开始吧。本文在有基础的情况下,建议阅读时间4小时。


1. MapperFactoryBean的实例化

我们知道在创建daoService这个bean的过程中,在populateBean进行自动装配这一步骤时,会对加上了@Autowired注解的属性PersonMapper personMapper进行自动装配时会调用bean工厂的getBean(beanName)尝试获取或者创建一个目标对象名是beanName的bean注入到personMapper属性上完成自动装配。因为PersonMapper对应的bd中,beanClass已经被替换为MapperFactoryBean,所以beanName为personMapper所对应的bean其实是MapperFactoryBean类型的对象。MapperFactoryBean对象通过带参的构造函数进行实例化,构造参数是Mapper类型PersonMapper.class。当MapperFactoryBean对象实例化之后,也需要对其执行自动装配。

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
	..省略..
	if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
	    MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
	    if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
	        autowireByName(beanName, mbd, bw, newPvs);
	    }
	    if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
              //按byType方式进行自动装配
	        autowireByType(beanName, mbd, bw, newPvs);
	    }
	    pvs = newPvs;
	}
	..省略..
}

因为beanDefinition已经被设置成按AUTOWIRE_BY_TYPE类型装配,所以在populateBean方法中会执行autowireByType方法

protected void autowireByType(
        String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

    // converter的类型是org.mybatis.spring.mapper.MapperFactoryBean
    TypeConverter converter = getCustomTypeConverter();
    if (converter == null) {
        converter = bw;
    }

    Set autowiredBeanNames = new LinkedHashSet<>(4);    
    // 最终得到的属性名是 "sqlSessionFactory" 和 "sqlSessionTemplate"。
    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    for (String propertyName : propertyNames) {
        try {
            // 获得属性的描述对象
            PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
            // 根据属性的描述,如果这个对象不是Object.class类才会进入if分支。因为没办法对Object.clss进行byType的自动装配。
            if (Object.class != pd.getPropertyType()) {
                MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
                // Do not allow eager init for type matching in case of a prioritized post-processor.
                boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance());
                DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);

                Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
                if (autowiredArgument != null) {
                    pvs.add(propertyName, autowiredArgument);
                }
                for (String autowiredBeanName : autowiredBeanNames) {
                    registerDependentBean(autowiredBeanName, beanName);
                }
                autowiredBeanNames.clear();
            }
        }
        catch (BeansException ex) {
            throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
        }
    }
}

重要的方法是在String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw)。返回不满足的非简单bean属性数组。什么是非简单bean属性呢?请见下图:

unsatisfiedNonSimpleProperties方法最终得到的属性名是 "sqlSessionFactory" 和 "sqlSessionTemplate"。也就是说要MapperFactoryBean依赖于sqlSessionFactory和sqlSessionTemplate这两个对象。这两个对象在哪里呢? 在org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration中通过@Bean标注。

1.1 Springboot自动配置与@Bean方法的扫描

@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    factory.setVfs(SpringBootVFS.class);
    if (StringUtils.hasText(this.properties.getConfigLocation())) {
      factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
    }
    Configuration configuration = this.properties.getConfiguration();
    if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
      configuration = new Configuration();
    }
    if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
      for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
        customizer.customize(configuration);
      }
    }
    factory.setConfiguration(configuration);
    if (this.properties.getConfigurationProperties() != null) {
      factory.setConfigurationProperties(this.properties.getConfigurationProperties());
    }
    if (!ObjectUtils.isEmpty(this.interceptors)) {
      factory.setPlugins(this.interceptors);
    }
    if (this.databaseIdProvider != null) {
      factory.setDatabaseIdProvider(this.databaseIdProvider);
    }
    if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
      factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
    }
    if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
      factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
    }
    if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
      factory.setMapperLocations(this.properties.resolveMapperLocations());
    }
    return factory.getObject();
}
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    ExecutorType executorType = this.properties.getExecutorType();
    if (executorType != null) {
        return new SqlSessionTemplate(sqlSessionFactory, executorType);
    } else {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

Springboot项目的启动类上往往有@SpringBootApplication注解,点进去看会发现它含有一个@EnableAutoConfiguration注解。再点进去看发现有一个@Import(AutoConfigurationImportSelector.class)注解。

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration

进入AutoConfigurationImportSelector.class找到getCandidateConfigurations方法。该方法的作用是委托SpringFactoriesLoader.lodaFactoryNames方法从各个依赖包的META-INF/spring.factories路径下找到自动配置类的类名。找到类名后就可以通过反射实例化该对象。理解了该机制,就理解了Springboot自动配置的精髓。

protected List getCandidateConfigurations(AnnotationMetadata metadata,
        AnnotationAttributes attributes) {
    List configurations = SpringFactoriesLoader.loadFactoryNames(
            getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    Assert.notEmpty(configurations,
            "No auto configuration classes found in META-INF/spring.factories. If you "
                    + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

回到本项目中。在MapperFactoryBean实例化过程中,会首先根据mybatis-spring-boot-autoconfigure中META-INF/spring.factories中的内容获取到MybatisAutoConfiguration这个配置类类名。在后续的bean创建过程中,MybatisAutoConfiguration类的对象会被创建成一个继承了FactoryBean的代理对象放到bean工厂中。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

回到autowireByType方法。在for循环中resolveDependency方法依次创建sqlSessionFactory和sqlSessionTemplate两个bean。下面我们来跟进这两个bean的创建过程。

因为这两个类的的生成是@Bean注解来生成,所以在createBeanInstance的过程中,是通过instantiateUsingFactoryMethod方法来操刀的(在配置类被解析为bd的过程中,@Bean的方法会由ConfigurationClassParser#doProcessConfigurationClass中的retrieveBeanMethodMetadata方法进行元数据解析后设置到bd中。故在bean实例化过程中,会进入以下分支)

1.2 sqlSessionFactorybean与SqlSessionTemplate的创建

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
	...省略...
    if (mbd.getFactoryMethodName() != null) {
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }
    ...省略...
}

进入instantiateUsingFactoryMethod方法,以本项目为例

public BeanWrapper instantiateUsingFactoryMethod(
        String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {

    BeanWrapperImpl bw = new BeanWrapperImpl(); this.beanFactory.initBeanWrapper(bw);

    Object factoryBean;
    Class factoryClass;
    boolean isStatic;
    // MybatisAutoConfiguration
    String factoryBeanName = mbd.getFactoryBeanName();

    if (factoryBeanName != null) {
        // 根据factoryBeanName获取factoryBean对象。也就是MybatisAutoConfiguration的代理对象MybatisAutoConfiguration$$EnhancerBySpringCGLIB$$4eb91d84@5555
        factoryBean = this.beanFactory.getBean(factoryBeanName);
        if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
            throw new ImplicitlyAppearedSingletonException();
        }
        // 拿到FactoryBean的类型 MybatisAutoConfiguration
        factoryClass = factoryBean.getClass();
        isStatic = false;
    }
    else {        
        factoryBean = null;
        factoryClass = mbd.getBeanClass();
        isStatic = true;
    }

    Method factoryMethodToUse = null;
    ArgumentsHolder argsHolderToUse = null;
    Object[] argsToUse = null;

    if (explicitArgs != null) {
        argsToUse = explicitArgs;
    }
    else {
        Object[] argsToResolve = null;
        synchronized (mbd.constructorArgumentLock) {
            factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
            if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {
                argsToUse = mbd.resolvedConstructorArguments;
                if (argsToUse == null) {
                    argsToResolve = mbd.preparedConstructorArguments;
                }
            }
        }
        if (argsToResolve != null) {
            argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);
        }
    }

    // 因为factoryMethodToUse和argsToUse都能从mbd中获取到,故这个if分支不会进入
    if (factoryMethodToUse == null || argsToUse == null) {
    	... 省略 ...        
    }

    bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));
    return bw;
}

首先根据 mbd.getFactoryBeanName()获取到factoryBeanName=“MybatisAutoConfiguration”。然后又根据factoryBeanName从bean工厂中得到factoryBean = this.beanFactory.getBean(factoryBeanName),这个factoryBean是MybatisAutoConfiguration的代理对象。随后从mbd中获取到factoryMethodToUse

factoryMethodToUse = {Method@5565} "org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.sqlSessionFactory(javax.sql.DataSource) throws java.lang.Exception"
  clazz = {Class@4146} "class org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration"
  name = "sqlSessionFactory"
  returnType = {Class@5924} "interface org.apache.ibatis.session.SqlSessionFactory"
  parameterTypes = {Class@5415} "interface javax.sql.DataSource"

和argsToUse

argsToUse = {HikariDataSource@5557} "HikariDataSource"
  isShutdown = {AtomicBoolean@6338} "false"
  connectionTimeout = 30000
  validationTimeout = 5000
  idleTimeout = 600000
  username = "root"
  password = "123456"
  driverClassName = "com.mysql.cj.jdbc.Driver"
  jdbcUrl = "jdbc:mysql://localhost:3306/dev?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"

最后调用bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse))。由instantiate方法获取对象。

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
        @Nullable Object factoryBean, final Method factoryMethod, Object... args) {    
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction) () -> {
            ReflectionUtils.makeAccessible(factoryMethod);
            return null;
        });
    }
    else {
        ReflectionUtils.makeAccessible(factoryMethod);
    }

    Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
    try {
        currentlyInvokedFactoryMethod.set(factoryMethod);
        Object result = factoryMethod.invoke(factoryBean, args);
        if (result == null) {
            result = new NullBean();
        }
        return result;
    }
    finally {
        if (priorInvokedFactoryMethod != null) {
            currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
        }
        else {
            currentlyInvokedFactoryMethod.remove();
        }
    }    
} 
  

创建sqlSessionFactorybean的过程是:通过工厂方法(factoryMethod)factoryMethod.invoke(factoryBean, args)执行目标方法即sqlSessionFactory获取对象。最终建造者是org.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactory,它创建了一个DefaultSqlSessionFactory对象。最后在DefaultSqlSessionFactory创建完成后会注入到我们正在创建的MapperFactoryBean这个bean的对应属性和bean工厂的dependenciesForBeanMap中(希望你别忘记我们正在干啥了...)。

sqlSessionTemplate创建的流程也与之类似,它最终也会调用MybatisAutoConfiguration#sqlSessionTemplate中的new SqlSessionTemplate(sqlSessionFactory)方法进行sqlSessionTemplate的创建。

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
  PersistenceExceptionTranslator exceptionTranslator) {

    this.sqlSessionFactory = sqlSessionFactory;
    // 默认值SIMPLE
    this.executorType = executorType;
    // 新创建一个异常处理MybatisExceptionTranslator对象
    this.exceptionTranslator = exceptionTranslator;
    // DefaultSqlSessionFactory对象的代理对象
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
	    SqlSessionFactory.class.getClassLoader(),
	    new Class[] { SqlSession.class },
	    new SqlSessionInterceptor());
}

在创建SqlSessionTemplate的过程中,因为它依赖sqlSessionFactory,故会将刚刚创建的sqlSessionFactory对象注入到this.sqlSessionFactory属性上。另外sqlSessionProxy是sqlSessionFactory对象的。装配完MapperFactoryBean之后将其注入到容器中。对于sqlSessionProxy代理对象的生成用的是JDK的动态代理,我们主要关心它的InvocationHandler实现,即SqlSessionInterceptor(我们后续文章还会介绍)。当SqlSessionTemplate创建完成后的执行流程与sqlSessionFactorybean一致。

private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        // 原始方法的执行。
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
}

值得注意的是在MapperFactoryBean注入到bean工厂之前,会在initializeBean步骤中调用invokeInitMethods方法进行一些初始化方法。它会调用org.springframework.dao.support.DaoSupport#afterPropertiesSet的checkDaoConfig方法中干2件重要的事情。

protected void checkDaoConfig() {
    super.checkDaoConfig();
    Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
        configuration.addMapper(this.mapperInterface);
      } catch (Exception e) {        
        throw new IllegalArgumentException(e);
      } finally {
        ErrorContext.instance().reset();
      }
    }
}

public  void addMapper(Class type) {
   if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new xxx;
      }
      boolean loadCompleted = false;
      try {
      	//1. 在knownMappers中添加MapperProxyFactory缓存
        knownMappers.put(type, new MapperProxyFactory(type));
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        //2. 对SQL语句进行解析,解析的结果封装成MappedStatement对象存放在Configuration对象的mappedStatements map中。
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
   }
}

1.在knownMappers中添加MapperProxyFactory缓存

2. 对SQL语句进行解析,解析的结果封装成MappedStatement对象存放在Configuration对象的mappedStatements map中。

parser.parse()会解析比如@Results,@Select,@Updata,@Delete,@Insert,@ResultMap等等这些注解。MappedStatement的地位就如同Spring中BeanDefinition一样。以下是MappedStatement需要解析得到的属性值:

  private String resource;
  private Configuration configuration;
  private String id;
  private Integer fetchSize;
  private Integer timeout;
  private StatementType statementType;
  private ResultSetType resultSetType;
  private SqlSource sqlSource;
  private Cache cache;
  private ParameterMap parameterMap;
  private List resultMaps;
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;

2. Mapper的依赖注入

2.1 从MapperFactoryBean中获取对象

MapperFactoryBean在创建完成会后被放入bean工厂中管理。并且注入到DaoService的personMapper属性上使得正在执行的daoService创建过程得以完成。在对personMapper属性依赖注入时,会调用bean工厂的的getBean(java.lang.String)方法获得该bean。由于名为personMapper的bean已经是MapperFactoryBean,所以在doGetBean的getObjectForBeanInstance方法中,最终会调用getObjectFromFactoryBean,通过FactoryBean.getObject()方法来获取最终的bean。

if (mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, () -> {
        try {
            return createBean(beanName, mbd, args);
        }
        catch (BeansException ex) {
            destroySingleton(beanName);
            throw ex;
        }
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
protected Object getObjectForBeanInstance(
        Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

    if (BeanFactoryUtils.isFactoryDereference(name)) {
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
    }
    // 不是FactoryBean就直接返回
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }

    Object object = null;
    if (mbd == null) {
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        // beanInstance是mapperFactoryBean对象
        FactoryBean factory = (FactoryBean) beanInstance;
        // Caches object obtained from FactoryBean if it is a singleton.
        if (mbd == null && containsBeanDefinition(beanName)) {
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}

获得从FactoryBean中创造的对象,也就是从MapperFactoryBean中制造的对象。

protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) {
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                // 获得mapper的代理对象MapperProxy
                object = doGetObjectFromFactoryBean(factory, beanName);
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    if (shouldPostProcess) {
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            // Temporarily return non-post-processed object, not storing it yet..
                            return object;
                        }
                        beforeSingletonCreation(beanName);
                        try {
                            // 尝试获得object的代理对象
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw xxx;
                        }
                        finally {
                            afterSingletonCreation(beanName);
                        }
                    }
                    if (containsSingleton(beanName)) {
                        // 缓存beanFactory对象
                        this.factoryBeanObjectCache.put(beanName, object);
                    }
                }
            }
            return object;
        }
    }
    else {
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        if (shouldPostProcess) {
            try {
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
            }
        }
        return object;
    }
}

在getObjectFromFactoryBean方法中首先从doGetObjectFromFactoryBean方法中获得mapper的代理对象,然后在postProcessObjectFromFactoryBean方法中对获得的代理对象再做一层代理。我们先来看看获得的mapper代理对象是什么。进入到doGetObjectFromFactoryBean方法看看它的核心代码。

private Object doGetObjectFromFactoryBean(final FactoryBean factory, final String beanName)
            throws BeanCreationException {
    Object object;
    // 调用MapperFactoryBean.getObject()方法获取mapperProxyFactory对PersonMapper.class接口生成的代理对象mapperProxy
    object = factory.getObject();      
    if (object == null) {        
        object = new NullBean();
    }
    return object;
}

可以看到对象是通过factory.getObject()方法拿到的。如果拿出的对象为null,则返回一个NullBean的对象。否则就是返回的factory制造的对象了。下面我们来看看getObject方法。

public T getObject() throws Exception {
    // mapperInterface是interface com.Hodey.analysemvc.dao.mapper.PersonMapper
    return getSqlSession().getMapper(this.mapperInterface);
}

// org.mybatis.spring.SqlSessionTemplate#getMapper
public  T getMapper(Class type) {
    return getConfiguration().getMapper(type, this);
}

// org.apache.ibatis.session.Configuration#getMapper
public  T getMapper(Class type, SqlSession sqlSession) {
    // mapperRegistry中已经缓存了之前就创建好的MapperProxyFactory对象 (MapperProxyFactory@7919)

    // MapperFactory代理对象放入到mapperRegistry的时机是在MapperFactoryBean对象被实例化出来之后,在initializeBean步骤的invokeInitMethods中,
    // 调用DaoSupport#afterPropertiesSet完成的。这一步仅仅是将(type)>这个map元素放进MapperRegistry管理的knownMappers中,type是PersonMapper。
    // 也就是说mapperInterface是PersonMapper。其实这个MapperProxyFactory最主要的作用就是存放mapperInterface,为后续不同mapper的代理对象生成提供便利。
    return mapperRegistry.getMapper(type, sqlSession);
}

因为factoryBean是MapperFactoryBean,那么自然调用MapperFactoryBean的getObject 方法。该放在最终会调用到configuration对象的getMapper方法。入参type是interface com.Hodey.analysemvc.dao.mapper.PersonMapper,sqlSession是之前被注入进去的sqlSessionTemplate对象。进入getMapper方法

public  T getMapper(Class type, SqlSession sqlSession) {
    // 根据type类型拿到之前缓存在knownMappers中的mapperProxyFactory,这个mapperProxyFactory中mapperInterface的属性值一般来说就是type的值
    final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
    return mapperProxyFactory.newInstance(sqlSession);    
}

public T newInstance(SqlSession sqlSession) {
    final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
    // Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
    // 也就是对mapperInterface接口类型创建动态代理对象,传入mapperProxy的原因是因为class MapperProxy implements InvocationHandler。     
    return newInstance(mapperProxy);
}

可以看到mapperProxyFactory,它是根据type类型拿到之前缓存在knownMappers中的mapperProxyFactory。Map, MapperProxyFactory> knownMappers中存放的是,比如本项目中存放的是

拿到mapperProxyFactory后调用newInstance(SqlSession sqlSession)方法,新建一个MapperProxy对象mapperProxy,构造函数如下:

public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
    // SqlSessionTemplate对象
    this.sqlSession = sqlSession;
    // com.Hodey.analysemvc.dao.mapper.PersonMapper
    this.mapperInterface = mapperInterface;
    // 暂时没有值
    this.methodCache = methodCache;
}

随后将刚刚创建的mapperProxy对象进行JDK动态代理,类型是正在被依赖注入的Mapper类型。InvocationHandler是MapperProxy,因为MapperProxy实现了InvocationHandler接口。

protected T newInstance(MapperProxy mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

MapperProxy实现InvocationHandler的invoke方法如下,以后我们还会遇到。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else if (isDefaultMethod(method)) {
            return invokeDefaultMethod(proxy, method, args);
        }
    } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
}

最终创建的MapperProxy代理对象会一路返回到getObjectFromFactoryBean中赋值给变量object。最终生成的Mapper的代理对象MapperProxy的属性值大致如下:

h = {MapperProxy@5773} 
 sqlSession = {SqlSessionTemplate@5487} 
 mapperInterface = {Class@4430} "interface com.Hodey.analysemvc.dao.mapper.PersonMapper"
 methodCache = {ConcurrentHashMap@5634}  size = 0

2.2 postProcessObjectFromFactoryBean

返回到getObjectFromFactoryBean后,接下会执行object = postProcessObjectFromFactoryBean(object, beanName)。它内部是调用applyBeanPostProcessorsAfterInitialization(object, beanName),其实是调用bean后置处理器的postProcessAfterInitialization方法。咦?我们是不是在哪里见过? 没错!这个正是Spring AOP的动态代理。如果你没见过,或者不知道流程,可以顺序参阅以下3篇:

《【九】Spring IoC 最全源码详解之initializeBean》

《【一】Spring AOP 最全源码详解之AOP元数据解析》

《【二】Spring AOP 最全源码详解之创建代理对象》

最终起作用的后置处理器是PersistenceExceptionTranslationPostProcessor,它的postProcessAfterInitialization方法如下

public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (this.advisor == null || bean instanceof AopInfrastructureBean) {
        return bean;
    }
    if (bean instanceof Advised) {
        Advised advised = (Advised) bean;
        if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
            if (this.beforeExistingAdvisors) {
                advised.addAdvisor(0, this.advisor);
            }
            else {
                advised.addAdvisor(this.advisor);
            }
            return bean;
        }
    }
    if (isEligible(bean, beanName)) {
        // 创建了一个proxyFactory对象,对象中含有DefaultAopProxyFactory
        ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
        if (!proxyFactory.isProxyTargetClass()) {
            evaluateProxyInterfaces(bean.getClass(), proxyFactory);
        }
        // 添加了一个PersistenceExceptionTranslationInterceptor类型的advisor
        proxyFactory.addAdvisor(this.advisor);
        customizeProxyFactory(proxyFactory);
        // 获得了一个代理类,这个代理类的advisor是PersistenceExceptionTranslationAdvisor对象,被代理的对象是MapperProxy。
        // 也就是说这里返回了代理类的代理类。
        return proxyFactory.getProxy(getProxyClassLoader());
    }
    return bean;
}

在ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName)方法中,首先创建了一个ProxyFactory对象,它大概有的属性和内容长这样:

ProxyFactory:
	ProxyCreatorSupport:
		this.aopProxyFactory = new DefaultAopProxyFactory();
		AdvisedSupport:
			TargetSource targetSource = MapperProxy@5773
			AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
			List advisors = new ArrayList<>();

它的继承关系如下:

【二】MyBatis-Spring最全源码详解之Mapper的自动注入_第1张图片

创建完成proxyFactory后,将PersistenceExceptionTranslatorAdvisor这个advisor添加到proxyFactory的advisors列表中。最后调用proxyFactory.getProxy(getProxyClassLoader())方法创建一个代理类。getProxy方法如下:

public Object getProxy(@Nullable ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
}

protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
        activate();
    }
    return getAopProxyFactory().createAopProxy(this);
}


public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        Class targetClass = config.getTargetClass();
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

最终创建的AopProxy是JdkDynamicAopProxy(config),入参config(也就是自身这个proxyFactory对象)最终赋值给了JdkDynamicAopProxy的AdvisedSupport advised属性上。最后创建代理类,接口还是原生对象属性的mapper——PersonMapper。

public Object getProxy(@Nullable ClassLoader classLoader) {
    //确定用于代理给定AOP配置的完整接口集。
    Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

最终通过JdkDynamicAopProxy的getProxy方法对刚才的proxyFactory方法再次进行了一次代理。本次代理的目的是增加一个PersistenceExceptionTranslatorAdvisor功能的代理对象,该对象可以拦截在操作数据库时的一些异常信息做相应的类型转换。其实废这么大个劲儿做这个事情对主流程意(mei)义(sha)不(luan)大(yong)!最后将获得的代理对象注入到personMapper上,完成了Mapper的自动注入。

bean = {daoService@5624}
    personMapper = org.apache.ibatis.binding.MapperProxy@646c0a67

到此,daoService这个bean的属性PersonMapper personMapper终于终于被完整的创建了出来!如果有人问你,你能说出注入到personMapper属性上的bean是什么玩意了吗?T . T

3. 总结

我们对本文来个一口气总结吧:

1. daoService实例化完成后,需要对属性PersonMapper personMapper进行依赖注入。根据personMapper的beanDefinition实例出一个MapperFactoryBean对象。不要补充的是一个Mapper一个MapperFactoryBean。

2.在依赖注入mapperFactoryBean对象的过程中,发现它依赖了SqlSessionFactory和SqlSessionTemplate这两个对象(其实远远不止,本文仅关注这个过程中最重要的这两个对象)。随后分别创建SqlSessionFactory和SqlSessionTemplate这两个对象并将他们注入到mapperFactoryBean中,完成mapperFactoryBean的创建。随后通过JDK动态代理,将mapperFactoryBean转换成代理对象mapperFactoryBeanProxy放入到bean工厂中管理,值得注意的是它关联的InvocationHandler是SqlSessionInterceptor。

3. daoService的创建流程依然处于依赖注入环节。依赖对象放入bean工厂后,它会通过getBean("personMapper")拿到这个mapperFactoryBeanProxy对象。由于该代理对象是一个FactoryBean,故它会通过factory.getObject()方法拿到需要的bean。

4. factory.getObject()过程中拿bean的过程有一些曲折。它会借助一个中介者mapperProxyFactory获取一个MapperProxy对象mapperProxy。随后再通过bean后置处理器的postProcessAfterInitialization方法,由PersistenceExceptionTranslationPostProcessor将mapperProxy再代理了一次,成为一个superMapperProxy。这一次代理仅仅是使得superMapperProxy可以拦截底层抛出的持久化异常能力,可以对其进行转换。

5.最后将这个superMapperProxy注入到daoService的属性personMapper上,完成整个过程。

6.试试看,你能否一口气念完上述5条总结。^_^


本文详细介绍了Mapper自动注入的过程,该过程非常复杂,前后看得见的地方就多达至少4次动态代理。整个过程从始至终贯穿了IoC实例化,依赖注入,动态代理等过程,把握起来有一定难度。

下一篇文章是MyBatis-Spring的收尾文章,会介绍MyBatis-Spring的命令执行流程,敬请期待。


4. 附录:项目文档

@MapperScan("com.Hodey.analysemvc.dao")
@SpringBootApplication
public class AnalyseMvcApplication {

	public static void main(String[] args) {
		SpringApplication.run(AnalyseMvcApplication.class, args);
	}
}
@Service
public class DaoService {

    @Autowired
    private PersonMapper personMapper;

    public String selectByName(String personName){
        PersonInfo personInfo = personMapper.selectByName(personName);
        return personInfo.toString();
    }

    public List selectAll(){
        List personInfoList = personMapper.selectAll();
        return personInfoList;
    }
}
@Repository
public interface PersonMapper {

    @Results(id = "personMap", value = {
            @Result(property = "id", column = "id"),
            @Result(property = "personName", column = "name"),
            @Result(property = "age", column = "age")
    })

    @Select("select * from person where name=#{personName}; ")
    public PersonInfo selectByName(String personName);

    @Select("select * from person;")
    @ResultMap(value = "personMap")
    public List selectAll();
}
public class PersonInfo {

    private int id;

    private String personName;

    private int age;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getPersonName() {
        return personName;
    }

    public void setPersonName(String personName) {
        this.personName = personName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "PersonInfo{" +
                "id=" + id +
                ", personName='" + personName + '\'' +
                ", age=" + age +
                '}';
    }
}
@RestController
@RequestMapping("/dao")
public class DaoContorller {

    @Autowired
    private DaoService daoService;

    @RequestMapping(value = "/getPersonName", method = RequestMethod.GET)
    public String getPersonName(@Param("personName") String personName){
        return daoService.selectByName(personName);
    }

    @RequestMapping(value = "/getAll", method = RequestMethod.GET)
    public List getAll(){
        return daoService.selectAll();
    }
}

application.properties

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/dev?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=******
spring.datasource.password=******

 

 

 

你可能感兴趣的:(【二】MyBatis-Spring最全源码详解之Mapper的自动注入)