深入源码理解Spring整合MyBatis原理 (5)

MapperFactoryBean的继承树


可以看到这个MapperFactoryBean同样实现了FactoryBean接口,那么按照惯例我们看看它的getObject()做了什么。

// Mapper接口类型 private Class<T> mapperInterface; /** * {@inheritDoc} */ @Override public T getObject() throws Exception { // 与MyBatis单独使用类似,都是通过sqlSession调用getMapper()方法获取对应的Mapper。 // 需要注意的是入参是一个接口类型,而出参是MyBatis生成的代理对象 return getSqlSession().getMapper(this.mapperInterface); } /** * {@inheritDoc} */ @Override public Class<T> getObjectType() { return this.mapperInterface; // Object的类型 }

可以看出Spring将Mapper包装成了MapperFactoryBean,其中的mapperInterface字段就是Mapper的类型,在交给Spring管理的时候依旧是通过sqlSession.getMapper(Class type)返回Mapper的代理对象的。

那么Mapper对应的模型有了,是不是还缺点什么?是的,我们需要扫描所有的Mapper,将他们包装成MapperFactoryBean(如UserMapper,就需要有一个MapperFactoryBean,其中mapperInterface字段是UserMapper.class)。这个重要的任务,Spring交给了我们接下来要聊的MapperScannerConfigurer了,通过类名就能感知到关键字:[Mapper、扫描、配置]

Mapper的扫描与配置-MapperScannerConfigurer

MapperScannerConfigurer

老规矩,看看几个核心接口的方法都做了什么。

afterPropertiesSet() /** * {@inheritDoc} */ @Override public void afterPropertiesSet() throws Exception { // 几乎啥也没干,就断言了个扫描包路径不为空。 下一个! notNull(this.basePackage, "Property 'basePackage' is required"); } 扫描Mapper并注册BeanDefinition

可以看到MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,文章开头的Spring技术回顾聊到该接口的postProcessBeanDefinitionRegistry()方法会在Spring容器启动的时候在较早的时机被回调。

private String basePackage; private boolean addToConfig = true; private SqlSessionFactory sqlSessionFactory; private SqlSessionTemplate sqlSessionTemplate; private String sqlSessionFactoryBeanName; private String sqlSessionTemplateBeanName; private Class<? extends Annotation> annotationClass; private Class<?> markerInterface; private ApplicationContext applicationContext; private String beanName; private boolean processPropertyPlaceHolders; private BeanNameGenerator nameGenerator; /** * {@inheritDoc} * * @since 1.0.2 */ @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) { // 解析并更新Spring配置文件中MapperScannerConfigurer相关的配置 processPropertyPlaceHolders(); } //创建类路径Mapper扫描器,并配置基本信息如扫描的注解(过滤条件)等。 ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); scanner.setAddToConfig(this.addToConfig); scanner.setAnnotationClass(this.annotationClass); scanner.setMarkerInterface(this.markerInterface); scanner.setSqlSessionFactory(this.sqlSessionFactory); scanner.setSqlSessionTemplate(this.sqlSessionTemplate); scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName); scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName); scanner.setResourceLoader(this.applicationContext); scanner.setBeanNameGenerator(this.nameGenerator); scanner.registerFilters(); //根据配置好的信息去扫描basePackage字段中指定的包及其子包 scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); }

我们来看看processPropertyPlaceHolders()做了什么。[可以跳过,不重要]

/* * BeanDefinitionRegistries are called early in application startup, before * BeanFactoryPostProcessors. This means that PropertyResourceConfigurers will not have been * loaded and any property substitution of this class' properties will fail. To avoid this, find * any PropertyResourceConfigurers defined in the context and run them on this class' bean * definition. Then update the values. */ // 上面Spring官方的注释的意思如下:BeanDefinitionRegistriy在Spring启动的时候回调地太早了,在BeanFactoryPostProcessors之后(PropertyResourceConfigurer实现了BeanFactoryProcessor) // 方法调用到此处的时候,相关的配置信息还没被载入进来,都是空,会有问题。所以我们要提前主动触发(getBeanOfType与getBean逻辑一致,都是先拿,拿不到就实例化再存入三级缓存)PropertyResourceConfigurer的实例化,这样相关的配置就能够被载入进来了。 private void processPropertyPlaceHolders() { // 先主动触发该类型的Bean的实例化。 Map<String, PropertyResourceConfigurer> prcs = applicationContext.getBeansOfType(PropertyResourceConfigurer.class); if (!prcs.isEmpty() && applicationContext instanceof ConfigurableApplicationContext) { BeanDefinition mapperScannerBean = ((ConfigurableApplicationContext) applicationContext) .getBeanFactory().getBeanDefinition(beanName); // PropertyResourceConfigurer does not expose any methods to explicitly perform // property placeholder substitution. Instead, create a BeanFactory that just // contains this mapper scanner and post process the factory. DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); factory.registerBeanDefinition(beanName, mapperScannerBean); for (PropertyResourceConfigurer prc : prcs.values()) { prc.postProcessBeanFactory(factory); } PropertyValues values = mapperScannerBean.getPropertyValues(); // 更新相关重要字段信息 this.basePackage = updatePropertyValue("basePackage", values); this.sqlSessionFactoryBeanName = updatePropertyValue("sqlSessionFactoryBeanName", values); this.sqlSessionTemplateBeanName = updatePropertyValue("sqlSessionTemplateBeanName", values); } } 根据条件扫描Mapper并整合成BeanDefintions注册进Spring

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzsjsw.html