目录
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小时。
我们知道在创建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标注。
@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实例化过程中,会进入以下分支)。
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
创建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;
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后调用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
返回到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<>();
它的继承关系如下:
创建完成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
我们对本文来个一口气总结吧:
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的命令执行流程,敬请期待。
@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=******