mybatis-spring:SqlSessionFactoryBean £神魔★判官ぃ 2024-04-17 05:37 59阅读 0赞 # 引言 # 在[demo: springboot+mybatis][demo_ springboot_mybatis]的MybatisConfig中有这样两个bean配置(事务此时不看): // 数据源配置 @Bean public DataSource dataSource() { // mybatis自带的一个简易数据库连接池,只是为了debug代码,这个就不关心了 PooledDataSource pooledDataSource = new PooledDataSource(); pooledDataSource.setDriver(environment.getProperty("mysql.driver")); pooledDataSource.setUsername(environment.getProperty("mysql.username")); pooledDataSource.setPassword(environment.getProperty("mysql.passwd")); pooledDataSource.setUrl(environment.getProperty("mysql.url")); return pooledDataSource; } @Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws IOException { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); sqlSessionFactoryBean.setMapperLocations(resourcePatternResolver.getResources("classpath:/mapper/*.xml")); return sqlSessionFactoryBean; } dataSource数据源配置的bean,是通过依赖注入传递给SqlSessionFactoryBean这个bean了。 问题点在于SqlSessionFactoryBean在整个容器初始化过程中起了什么作用。 # @MappserScan # [mybatis-spring:@MapperScan注解][mybatis-spring_MapperScan]中解释使用这个注解,完成两件事: 1. 扫描指定接口 2. 注册这些接口的bean定义到spring容器(实际是FactoryBean定义) 然后spring容器在对Dao层接口注入的时候,实际注入的是一个Mybatis创建的代理对象:MapperProxy, public class MapperProxy<T> implements InvocationHandler, Serializable { private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } } 看构造方法和属性字段,mapperInterface是被代理的接口,methodCache是接口的方法,另外一个关键属性是sqlSession。 SqlSession是MyBatis 的一个主要 Java 接口。通过它的实现可以执行SQL命令、执行事务等。所以它是执行SQL的关键。现在需要确认的是SqlSession与SqlSessionFactoryBean的关系。 # SqlSessionFactoryBean # 见名知义,SqlSessionFactoryBean是创建SqlSessionFactory的: public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {} 它也实现了FactoryBean,并且实现了InitializingBean接口的afterPropertiesSet()方法。这样在spring容器实例化这个bean的时候,便会回调afterPropertiesSet()方法: @Override public void afterPropertiesSet() throws Exception { notNull(dataSource, "Property 'dataSource' is required"); notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); this.sqlSessionFactory = buildSqlSessionFactory(); } 来创建SqlSessionFactory实例,然后看下它对FactoryBean的接口方法实现: @Override public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { afterPropertiesSet(); } return this.sqlSessionFactory; } /** * {@inheritDoc} */ @Override public Class<? extends SqlSessionFactory> getObjectType() { return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass(); } spring容器中某个bean的字段需要依赖注入SqlSessionFactory实例的时候,会调用getObject方法,返回SqlSessionFactory实例。 那么哪个bean的属性需要注入SqlSessionFactory?答案是:MapperFactoryBean。 上文说扫描到的每个接口注册的工厂bean定义的就是MapperFactoryBean这个bean定义。 它的用处就是注入service的dao层接口属性(如在ServiceImpl的属性UserDao)的代理对象的时候,会返回这个MapperProxy代理对象。[mybatis-spring:@MapperScan注解][mybatis-spring_MapperScan]文中有提到解释。 问题来了,这里注入的是SqlSessionFactory对象,MapperProxy需要的是SqlSession实现类。答案看下面: MapperFactoryBean继承自SqlSessionDaoSupport: public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {} 在父类SqlSessionDaoSupport里有方法: public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (!this.externalSqlSession) { this.sqlSession = new SqlSessionTemplate(sqlSessionFactory); } } 而SqlSessionTemplate是SqlSession的实现: public class SqlSessionTemplate implements SqlSession, DisposableBean { private final SqlSessionFactory sqlSessionFactory; private final ExecutorType executorType; private final SqlSession sqlSessionProxy; private final PersistenceExceptionTranslator exceptionTranslator; } 而它的sqlSessionProxy实例是从它的构造方法里构造的: public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required"); notNull(executorType, "Property 'executorType' is required"); this.sqlSessionFactory = sqlSessionFactory; this.executorType = executorType; this.exceptionTranslator = exceptionTranslator; this.sqlSessionProxy = (SqlSession) newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor()); } 所以MapperProxy的SqlSession实例,实际是SqlSessionTemplate实例,下层再委托给sqlSessionProxy代理对象进行实际调用。 因此,注入ServiceImpl的UserDao属性字段时,发生以下过程: 1. spring容器注入UserDao属性的MapperProxy实例时,依赖MapperProxy实例 2. 此时实例MapperFactoryBean来创建MapperProxy实例 3. 创建MapperFactoryBean需要依赖注入SqlSessionFacotry实例 4. 使用SqlSessionFactoryBean来创建SqlSessionFactory实例 最终完成整个过程,注入userDao属性的值 MapperProxy实例。 [demo_ springboot_mybatis]: https://blog.csdn.net/x763795151/article/details/99778304 [mybatis-spring_MapperScan]: https://blog.csdn.net/x763795151/article/details/99899471
还没有评论,来说两句吧...