spring整合mybatis的底层原理分析
目录
- spring整合myBATis的底层原理
- 一、手写一个spring集成mybatis
- 二、原理解析
- 总结
spring整合mybatis的底层原理
原理:
- FactoryBean的自定义对象
- jdk动态代理Mapper接口对象
一、手写一个spring集成mybatis
目录结构:
1.1 入口类
public class Test { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(AppConfig.class); context.refresh(); UserService userService = (UserService)context.getBean("userService"); userService.test(); } }
1.2 配置类
@CondorHeroMapperScan("com.athome.tulin.springmybatis.mapper") @ComponentScan("com.athome.tulin.springmybatis") public class AppConfig { @Bean public SqlSessionFactory sqlSessionFactory() throws IOException { System.out.println("4.依赖注入MemberMapper需要先创建对象………AppConfig…………SqlSessionFactory………"); InputStream inputStream = Resources.getResourceAsStream("mybatis.XML"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); return sqlSessionFactory; } }
1.3 业务类
@Component public class UserService { public UserService() { System.out.println("3.…………创建UserService…………"); } //如何把mybatis生成的UserMapper的代理对象赋值给UserMapper @Autowired private UserMapper userMapper; @Autowired private OrderMapper orderMapper; @Autowired private MemberMapper memberMapper; public void test(){ System.out.println("7.……UserService…test…"); System.out.println(userMapper.selectById()); System.out.println(orderMapper.selectById()); System.out.println(memberMapper.selectById()); } }
1.4 创建3个Mapper接口
public interface MemberMapper { @Select("select 'member' ") String selectById(); }
public interface OrderMapper { @Select("select 'order' ") String selectById(); }
public interface UserMapper { @Select("select 'user' ") String selectById(); }
1.5 自定义注解
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Import(CondorHeroImportBeanDefinitionRegistrar.class) public @interface CondorHeroMapperScan { String www.devze.comvalue(); }
1.6 自定义fanctoryBean
public class CondorHeroFactoryBean implements FactoryBean { private Class mapperInterface; private SqlSession sqlSession; public CondorHeroFactoryBean(Class mapperInterface) { this.mapperInterfajsce = mapperInterface; } /** * 从容器查找SqlSessionFactory 并获取sqlSession 赋值于sqlSession * 扫描的时候有 beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); * 那他就会自动找set方法 * @param sqlSessionFactory */ public void setSqlSession(SqlSessionFactory sqlSessionFactory) { System.out.println("5.……setSqlSession……"); sqlSessionFactory.getConfiguration().addMapper(mapperInterface); this.sqlSession = sqlSessionFactory.openSession(); } @Override public Object getObject() throws Exception { //动态代理获取UserMapper接口对象 System.out.println("6.……getObject……"); return sqlSession.getMapper(mapperInterface); } @Override public Class<?> getObjectType() { return mapperInterface; } }
1.7 自定义Bean注册类
public class CondorHeroImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { System.out.println("1.……registerBeanDefinitions……"); //1.获取注解上的指定路径 Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(CondorHeroMapperScan.class.getName()); String path = (String)annotationAttributes.get("value"); //2.扫描 CondorHeroBeanDefinitionScanner scanner = new CondorHeroBeanDefinitionScanner(registry); scanner.addIncludeFilter(new TypeFilter() { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { return true; } }); scanner.scan(path); } }
1.8 自定义扫描类
public class CondorHeroBeanDefinitionScanner extends ClassPathBeanDefinitionScanner { public CondorHeroBeanDefinitionScanner(BeanDefinitionRegistry registry) { super(registry); } @Override protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { //只关心接口(判断是否是接口) return beanDefinition.getMetadata().isInterface(); } /** * 扫描路径并得到beanDefinition * @param basePackages * @return */ @Override protected Set<BeanDefinitionHolder> DOScan(String... basePackages) { System.out.println("2.……doScan……"); Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages); for (BeanDefinitionHolder beanDefinitionHolder: beanDefinitionHolders) { GenericBeanDefinition beanDefinition = (GenericBeanDefinition)beanDefinitionHolder.getBeanDefinition(); //设置值 beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName()); //设置名称 beanDefinition.setBeanClassName(CondorHeroFactoryBean.class.getName()); //将MapperFactoryBean的注入模型设置为By-Type。也就是说,MapperFactoryBean中的setXxx中的属性会从容器中来进行查找 beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } return beanDefinitionHolders; } }
运行结果是:
1.……registerBeanDefinitions……2.……doScan……3.…………创建UserService…………4.依赖注入MemberMapper需要先创建对象………AppConfig…………SqlSessionFactory………5.……setSqlSession……6.……getObject……5.……setSqlSession……6.……getObject……5.……setSqlSession……6.……getObject……7.……UserService…test… user order member
二、原理解析
2.1 通过@MapperScan导入了MapperScannerRegistrar类
2.2 MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法
2.3 在registerBeanDefinitions方法中注册一个MapperScannerConfigurer
类型的BeanDefinition
2.4 而MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,所以Spring在启动过程中时会调用它的postProcessBeanDefinitionRegistry()方法
2.5 在postProcessBeanDefinitionRegistry方法中会生成一个ClassPathMapperScanner对象,然后进行扫描(scanner.scan)2.6 通过利用Spring的扫描后,会把接口扫描出来并且得到对应的BeanDefinition
2.7 接下来把扫描得到的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byType(在类ClassPathMapperScanner的方法processBeanDefinitions中)
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); String beanClassName = definition.getBeanClassName(); LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface"); // the mapper interface is the original class of the bean // but, the actual class of the bean is MapperFactoryBean //设置值 definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59 //设置名称 definition.setBeanClass(this.mapperFactoryBeanClass); definition.getPropertyValues().add("addToConfig", this.addToConfig); boolean explicitFactoryUsed = false; if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) { definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionFactory != null) { definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory); explicitFactoryUsed = true; } if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) { if (explicitFactoryUsed) { LOGGER.warn( () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionTemplate != null) { if (explicitFactoryUsed) { LOGGER.warn( () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed =www.devze.com true; php } if (!explicitFactoryUsed) { LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); //将MapperFactoryBean的注入模型设置为By-Type。也就是说,MapperFactoryBean中的setXxx中的属性会从容器中来进行查找 definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } definition.setLazyInit(lazyInitialization);javascript } }
2.8 扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个FactoryBean
2.9 在MapperFactoryBean中的getObject方法中,调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,这个代理对象就成为Spring容器中的Bean
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {}
@Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } @Override public Class<T> getObjectType() { return this.mapperInterface; }
注意:这里的getObject调用时机是,在创建的对象依赖了Mapper对象就会去创建该Mapper对象,此时通过MapperFactoryBean去获取
2.10 sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生
上面的getSqlSession()对应源码是:
2.11 MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean
2.12 如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性
这一步是程序员自己定义一个SqlSessionFactory,例如:
@CondorHeroMapperScan("com.athome.tulin.springmybatis.mapper") @ComponentScan("com.athome.tulin.springmybatis") public class AppConfig { @Bean public SqlSessionFactory sqlSessionFactory() throws IOException { System.out.println("4.依赖注入MemberMapper需要先创建对象………AppConfig…………SqlSessionFactory………"); InputStream inputStream = Resources.getResourceAsStream("mybatis.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); return sqlSessionFactory; } }
这里定义的SqlSessionFactory 会赋值于2.10的sqlSessionTemplate
2.13 而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就产生一个Mapper接口代理对象
2.14 当执行该代理对象的某个方法时,就会进入到Mybatis框架的底层执行流程
至此:业务类中的引入Mapper对象就复制成功。
@Autowired private OrderMapper orderMapper;
即:这时候的orderMapper就 是赋值了代理对象的对象是有值 的。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。
精彩评论