Spring integration with Mybtais does the following configuration (all roads lead to Rome, not only).
private static final String ONE_MAPPER_BASE_PACKAGE = "com.XXX.dao.mapper.one";
@Bean
public MapperScannerConfigurer oneMapperScannerConfigurer() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage(ONE_MAPPER_BASE_PACKAGE);
mapperScannerConfigurer.
setSqlSessionFactoryBeanName("oneSqlSessionFactoryBean");
return mapperScannerConfigurer;
}
@Primary
@Bean(name="oneSqlSessionFactoryBean")
public SqlSessionFactoryBean oneSqlSessionFactoryBean( @Qualifier("oneDataSource") DruidDataSource oneDataSource) {
return getSqlSessionFactoryBeanDruid(oneDataSource,ONE_MAPPER_XML);
}
Copy the code
Spring integration with Mybatis was completed in less than 20 lines of code.
Backpacks!!!!!! What’s going on behind the scenes?
You should also start with MapperScannerConfigurer and SqlSessionFactoryBean.
MapperScannerConfigurer
Class notes
-
BeanDefinitionRegistryPostProcessor from base package recursive search interface, will they registered as MapperFactoryBean. Note that the interface must contain at least one method whose implementation class is ignored.
-
Used to be, extending the spring BeanFactoryPostProcessor 1.0.1 1.0.2 is to extend BeanDefinitionRegistryPostProcessor later, Specific reasons, please refer to https://jira.springsource.org/browse/SPR-8269
-
BasePackage can be multiple, separated by commas or semicolons.
-
AnnotationClass or markerInterface allows you to specify an interface to scan. By default these two properties are null and all interfaces under basePackage will be scanned.
-
MapperScannerConfigurer automatically injects SqlSessionFactory or SqlSessionTemplate into the beans it creates. If more than one SqlSessionFactory exists, Need to set up sqlSessionFactoryBeanName or sqlSessionTemplateBeanName sqlSessionFactory or sqlSessionTemplate to specify specific injection.
-
You cannot pass in objects with placeholders (for example, objects that contain username and password placeholders for a database). You can use beanName to defer the actual object creation until all placeholder replacements are complete. Note that MapperScannerConfigurer supports its own properties using placeholders, using the ${property} format.
Class diagrams find key methods
Look from the class diagram MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor InitializingBean, ApplicationContextAware, BeanNameAware interface. The meanings of each interface are as follows:
- ApplicationContextAware: When the Spring container is initialized, the ApplicationContext is automatically injected
- BeanNameAware: Sets the name of the current Bean in Spring
- The InitializingBean interface includes only the afterPropertiesSet method, which is executed when the bean is initialized
- BeanDefinitionRegistryPostProcessor: for the expansion of the spring BeanFactoryPostProcessor, allowed to register multiple before spring BeanFactoryPostProcessor implementation bean definition. For postProcessBeanDefinitionRegistry needs to extend the method.
The afterPropertiesSet method of MapperScannerConfigurer is as follows without any extension information.
@Override public void afterPropertiesSet() throws Exception {
notNull(this.basePackage, "Property 'basePackage' is required");
}
Copy the code
MapperScannerConfigurer comments with class diagram analysis, to determine its core method is: postProcessBeanDefinitionRegistry
PostProcessBeanDefinitionRegistry analysis
@Override
public void postProcessBeanDefinitionRegistry(
BeanDefinitionRegistry registry) {
if(this.processPropertyPlaceHolders) { //1. The placeholder attribute processing processPropertyPlaceHolders (); } 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); //2. Set scanner.registerfilters (); / / 3. Scan the Java file scanner. Scan (StringUtils. TokenizeToStringArray (enclosing basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); }Copy the code
See from the source besides processPropertyPlaceHolders, other work, commissioned ClassPathMapperScanner
ProcessPropertyPlaceHolders processing placeholder
Said before BeanDefinitionRegistryPostProcessor before spring BeanFactoryPostProcessor implementation calls,
This means that the Spring handle placeholder class PropertyResourceConfigurer haven’t execute!
So how does MapperScannerConfigurer support its properties using placeholders? The answer to all this is there
ProcessPropertyPlaceHolders this method.
private void processPropertyPlaceHolders() {
Map<String, PropertyResourceConfigurer> prcs =
applicationContext.getBeansOfType(PropertyResourceConfigurer.class);
if(! prcs.isEmpty() && applicationContext instanceof GenericApplicationContext) { BeanDefinition mapperScannerBean = ((GenericApplicationContext) applicationContext) .getBeanFactory().getBeanDefinition(beanName); / / PropertyResourceConfigurer not directly replace placeholders exposure method, / / create a contains the BeanFactory MapperScannerConfigurer / / and then execute the BeanFactory post-processing can 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); }}Copy the code
Finish see processPropertyPlaceHolders, can be summarized MapperScannerConfigurer support its own attributes using a placeholder
-
Find all registered PropertyResourceConfigurer types of beans
-
Use the new DefaultListableBeanFactory () to simulate the Spring environment, MapperScannerConfigurer registered to this the BeanFactory, perform the BeanFactory post-processing, to replace the placeholder.
ClassPathMapperScanner’s registerFilters method
The class annotation for MapperScannerConfigurer contains the following information:
AnnotationClass or markerInterface allows you to specify an interface to scan, which is null by default, and all interfaces under basePackage will be scanned. Scanner.registerfilters (), which is set to annotationClass and markerInterface.
public void registerFilters() {
boolean acceptAllInterfaces = true; // If annotationClass is specified,if(this.annotationClass ! = null) { addIncludeFilter(new AnnotationTypeFilter(this.annotationClass)); acceptAllInterfaces =false; } // Rewrite AssignableTypeFilter to ignore matches on the actual tagged interfaceif(this.markerInterface ! = null) { addIncludeFilter(new AssignableTypeFilter(this.markerInterface) { @Override protected boolean matchClassName(String className) {return false; }}); acceptAllInterfaces =false;
}
if(acceptAllInterfaces) {// Process all interfaces by default addIncludeFilter(new)TypeFilter() {
@Override
public boolean match(
MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
return true; }}); } // Does not contain Java files ending in package-info // package-info.java package-level documentation and package-level comments addExcludeFilter(new)TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
String className = metadataReader.getClassMetadata().getClassName();
return className.endsWith("package-info"); }}); }Copy the code
Although the filters are set up, it’s up to the scanner.scan method to see how they work in a scan.
ClassPathMapperScanner Scan method
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
doScan(basePackages); // Register the annotation configuration handlerif (this.includeAnnotationConfig) {
AnnotationConfigUtils
.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
Copy the code
The doScan method is as follows:
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("No MyBatis mapper was found in '"
+ Arrays.toString(basePackages)
+ "' package. Please check your configuration.");
} else {
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
Copy the code
Located in the superclass ClassPathBeanDefinitionScanner ClassPathMapperScanner doScan method, it is
Scans the package under all Java files into BeanDefinition (actual ScannedGenericBeanDefinition).
ProcessBeanDefinitions is a BeanDefinition that converts the previous BeanDefinition into a MapperFactoryBean.
How does the filter work (i.e. AnnotationClass or markerInterface)? I traced the source code all the way
Finally in ClassPathScanningCandidateComponentProvider isCandidateComponent found the handling of the filter
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return false; }}for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
returnisConditionMatch(metadataReader); }}return false;
}
Copy the code
Summarize the function of MapperScannerConfigurer
MapperScannerConfigurer realized beanDefinitionRegistryPostProcessor postProcessBeanDefinitionRegistry method
Recursively searches interfaces from the specified basePackage directory, registering them as MapPerFactoryBeans
SqlSessionFactoryBean
Class notes
-
Create Mybatis SqiSessionFactory for sharing in the Spring context.
-
The SqiSessionFactory can be dependency injected into the DAOS with Mybatis.
-
Datasourcetransactionmanager, jtatransactionmanager combined with sqlsessionfactory want to realize the transaction.
Class diagrams find key methods
SqlSessionFactoryBean implements the ApplicationListener, InitializingBean, and FactoryBean interfaces.
- ApplicationListener listens for Spring events
- The InitializingBean interface includes only the afterPropertiesSet method, which is executed when the bean is initialized
- FactoryBean: The object returned is not an instance of the specified class, but the object returned by the FactoryBean’s getObject method
You should focus on the afterPropertiesSet and getObject methods.
Critical method analysis
The afterPropertiesSet method
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder,
"Property 'sqlSessionFactoryBuilder' is required"); state((configuration == null && configLocation == null) || ! (configuration ! = null && configLocation ! = null),"Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = buildSqlSessionFactory();
}
Copy the code
BuildSqlSessionFactory: SqlSessionFactory: SqlSessionFactory: SqlSessionFactory
GetObject method
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
Copy the code
Conclusion SqlSessionFactoryBean
An afterPropertiesSet of InitializingBean was implemented in which Mybatis SqlSessionFactory was created
GetObject that implements FactoryBean returns the created sqlSessionFactory.
doubt
After watching SqlSessionFactoryBean and MapperScannerConfigurer, you may have a question! The general way to use Mybatis in Spring is as follows:
ApplicationContext context=new AnnotationConfigApplicationContext();
UsrMapper usrMapper=context.getBean("usrMapper"); Sqlsession.getmapper (usRMapper.class);Copy the code
SqlSessionFactoryBean creates the SqlSessionFactory of Mybatis. MapperScannerConfigurer converts the interface to MapperFactoryBean. Sqlsession.getmapper (usRMapper.class)
MapperFactoryBean is the answer to all of this.
MapperFactoryBean instructions
Class notes
BeanFactory that can inject MyBatis mapping interface. It can set the SqlSessionFactory or a pre-configured SqlSessionTemplate. Note that this factory only injects interfaces, not implementation classes
Class diagrams find key methods
Look at the class diagram and see InitializingBean and FactoryBean again!!
- The InitializingBean interface includes only the afterPropertiesSet method, which is executed when the bean is initialized
- FactoryBean: The object returned is not an instance of the specified class, but the object returned by the FactoryBean’s getObject method
Focus again on the implementation of afterPropertiesSet and getObject!
Critical method analysis
DaoSupport class has the following implementation of afterPropertiesSet:
public final void afterPropertiesSet()
throws IllegalArgumentException, BeanInitializationException {
this.checkDaoConfig();
try {
this.initDao();
} catch (Exception var2) {
throw
new BeanInitializationException(
"Initialization of DAO failed", var2); }}Copy the code
InitDao is an empty implementation, checkDaoConfig is implemented in MapperFactoryBean as follows:
protected void checkDaoConfig() {
super.checkDaoConfig();
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = getSqlSession().getConfiguration();
if(this.addToConfig && ! configuration.hasMapper(this.mapperInterface)) { try { configuration.addMapper(this.mapperInterface); } catch (Exception e) { logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e); throw new IllegalArgumentException(e); } finally { ErrorContext.instance().reset(); }}}Copy the code
The key statement is configuration.addmapper (this.mapperInterface), which adds the interface to the configuration of Mybatis.
The getObject method is super simple, calling sqlsession.getmapper (usRMapper.class);
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
Copy the code
Conclusion MapperFactoryBean
The afterPropertiesSet method of InitializingBean is implemented in which the Mapper interface is set into the configuration of MyBatis.
The getObject method of FactoryBean is implemented, which calls SQLSession. getMapper and returns a Mapper object.
conclusion
Spring integration Mybatis core 3 classes:
MapperScannerConfigurer
Implements the beanDefinitionRegistryPostProcessor postProcessBeanDefinitionRegistry method, in which from the specified directory of basePackage recursive search interface, Register them as BeanDefinitions of type MapperFactoryBean
SqlSessionFactoryBean
An afterPropertiesSet of InitializingBean was implemented in which Mybatis SqlSessionFactory was created.
GetObject that implements FactoryBean returns the created sqlSessionFactory.
MapperFactoryBean
The afterPropertiesSet method of InitializingBean is implemented to set the Mapper interface into the configuration of MyBatis.
The getObject method of FactoryBean is implemented, which calls SQLSession. getMapper and returns a Mapper object.