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