8.Spring底层原理之BeanFactory后处理器,模拟@MapperScan

我们扫描Mapper的时候,一般是配置@MapperScan注解,然后在注解中配置要扫描的路径。然后,Spring就会为我们创建Mapper对应的实现。

在com.zhaojun.springsource.a05.mapper包下,有两个接口分别为

@Mapper
public interface Mapper1 {
}
@Mapper
public interface Mapper2 {
}

其实我们也可以通过@Bean的方式手动一个个的创建,创建方式如下

@Bean
public SqlSessionFactoryBean  sqlSessionFactoryBean(DataSource dataSource){
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(dataSource);
    return sqlSessionFactoryBean;
}

@Bean(initMethod = "init")
public DruidDataSource dataSource(){
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setUrl("jdbc:mysql://localhost:3306/genshin");
    dataSource.setUsername("root");
    dataSource.setPassword("Zjmysql%0193");
    return dataSource;
}

@Bean
public MapperFactoryBean<Mapper1> mapper1(SqlSessionFactory sqlSessionFactory){
    MapperFactoryBean<Mapper1> mapperFactoryBean = new MapperFactoryBean<>(Mapper1.class);
    mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory);
    return mapperFactoryBean;
}

@Bean
public MapperFactoryBean<Mapper2> mapper2(SqlSessionFactory sqlSessionFactory){
    MapperFactoryBean<Mapper2> mapperFactoryBean = new MapperFactoryBean<>(Mapper2.class);
    mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory);
    return mapperFactoryBean;
}

每个Mapper其实是一个MapperFactoryBean,它需要依赖SqlSessionFactory。但是这样进行创建只能一个个的写代码,效率肯定没有@MapperScan快,但是我们通过这个方法了解了,MapperFactoryBean是怎样注册的。

接下来,我们自己模拟一下@MapperScan的过程。

public class MapperPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        try {
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            // 这里相当于basePackage 配置的包扫描路径
            Resource[] resources = resolver.getResources("classpath:com/zhaojun/springsource/a05/mapper/*.class");

            CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
            AnnotationBeanNameGenerator generator = new AnnotationBeanNameGenerator();
            for (Resource resource : resources) {
                MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                ClassMetadata classMetadata = metadataReader.getClassMetadata();
                // 是否有@Mapper注解
                boolean hasAnnotation = metadataReader.getAnnotationMetadata().hasAnnotation(Mapper.class.getName());
                // 若为接口并且有@Mapper注解
                if (classMetadata.isInterface() && hasAnnotation){
                    // 和刚才例子创建一样,首先创建MapperFactoryBean
                    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class)
                            .addConstructorArgValue(classMetadata.getClassName())
                            .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                            .getBeanDefinition();
                    AbstractBeanDefinition mapperBeanDefinition = BeanDefinitionBuilder.genericBeanDefinition(classMetadata.getClassName()).getBeanDefinition();
                    if (beanFactory instanceof DefaultListableBeanFactory){
                        // 为每个Mapper生产名字
                        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
                        String beanName = generator.generateBeanName(mapperBeanDefinition, defaultListableBeanFactory);

                        defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinition);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

实现BeanFactoryPostProcessor接口,即自定义一个BeanFactory后处理器。

通过配置的路径,进行扫描。判断该路径下的class,是否为接口,以及是否被@Mapper注释。如果有的话,我们创建一个MapperFactoryBean的beanDefinition,并且用该接口生成名字,注册到beanFactory上。

其实和手动创建差不多,每个Mapper接口其实都是创建了一个MapperFactoryBean。

应用后处理器

将后处理器注册并使用

public class A05Application {
    public static final Logger log = LoggerFactory.getLogger(A05Application.class);

    public static void main(String[] args) throws IOException {
        GenericApplicationContext context = new GenericApplicationContext();

        context.registerBean("config", Config.class);

        context.registerBean(AtBeanPostProcessor.class);
		// context.registerBean(ConfigurationClassPostProcessor.class);
        // 使用后处理器
        context.registerBean(MapperPostProcessor.class);

        context.refresh();

        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }

        context.close();
    }
}

其中AtBeanPostProcessor是我模拟@Bean的后处理器,可以去看我的上一篇文章7.Spring底层原理之BeanFactory后处理器,模拟@Bean,这里也可以用Spring自己的ConfigurationClassPostProcessor代替。

查看输出日志

16:07:00.143 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Refreshing org.springframework.context.support.GenericApplicationContext@42607a4f
16:07:00.176 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'com.zhaojun.springsource.a05.AtBeanPostProcessor'
16:07:00.200 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'com.zhaojun.springsource.a05.MapperPostProcessor'
16:07:00.282 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Overriding bean definition for bean 'mapper1' with a different definition: replacing [Generic bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=config; factoryMethodName=mapper1; initMethodName=null; destroyMethodName=null] with [Generic bean: class [org.mybatis.spring.mapper.MapperFactoryBean]; scope=; abstract=false; lazyInit=null; autowireMode=2; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null]
16:07:00.303 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'config'
16:07:00.303 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'bean1'
16:07:00.304 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'sqlSessionFactoryBean'
16:07:00.332 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'dataSource'
16:07:00.646 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} inited
16:07:00.646 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'sqlSessionFactoryBean' via factory method to bean named 'dataSource'
16:07:00.652 [main] DEBUG org.apache.ibatis.logging.LogFactory - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
16:07:00.656 [main] DEBUG org.mybatis.spring.SqlSessionFactoryBean - Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration
16:07:00.704 [main] DEBUG org.mybatis.spring.SqlSessionFactoryBean - Property 'mapperLocations' was not specified.
16:07:00.706 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'mapper1'
16:07:00.756 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'mapper2'
config
com.zhaojun.springsource.a05.AtBeanPostProcessor
com.zhaojun.springsource.a05.MapperPostProcessor
bean1
sqlSessionFactoryBean
dataSource
mapper1
mapper2
16:07:00.775 [main] DEBUG org.springframework.context.support.GenericApplicationContext - Closing org.springframework.context.support.GenericApplicationContext@42607a4f, started on Fri May 13 16:07:00 CST 2022
16:07:00.776 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} closing ...
16:07:00.778 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} closed

可以看到mapper1,mapper2已经扫描注册成功了。


8.Spring底层原理之BeanFactory后处理器,模拟@MapperScan
https://www.zhaojun.inkhttps://www.zhaojun.ink/archives/spring-mapper-scan
作者
卑微幻想家
发布于
2022-05-13
许可协议