Mybatis集成篇(一)
Spring 框架集成Mybatis
目前主流Spring框架体系中,可以集成很多第三方框架,方便开发者利用Spring框架机制使用第三方框架的功能。就例如本篇Spring集成Mybatis
简单集成案例:
Config配置:
@Configuration
@MapperScan(basePackages = {"com.czy.mapper"})
public class MybatisConfig {
@Bean
public SqlSessionFactory getSqlSessionFactory() throws Exception {
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
// 可以配置Mybatis相关配置
configuration.setLogImpl(StdOutImpl.class);
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setConfiguration(configuration);
return factoryBean.getObject();
}
private DataSource dataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUrl("jdbc:mysql://xxxx:3306/mybatis?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true");
dataSource.setPassword("xxx");
dataSource.setUser("xxx");
return dataSource;
}
}
Mapper文件:
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
List<User> selectUser(int id);
}
主函数:
public class Main {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MybatisConfig.class);
UserMapper userMapper = applicationContext.getBean(UserMapper.class);
List<User> users = userMapper.selectUser(1);
users.forEach(System.out::println);
}
}
上面的案例是通过注解的方式进行Mybatis相关信息配置和相关Mapper使用。
原理解析
@MapperScan
在上面案例的配置文件中有个@MapperScan注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends Annotation> annotationClass() default Annotation.class;
Class<?> markerInterface() default Class.class;
String sqlSessionTemplateRef() default "";
String sqlSessionFactoryRef() default "";
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
String lazyInitialization() default "";
String defaultScope() default "";
boolean processPropertyPlaceHolders() default true;
Filter[] excludeFilters() default {};
}
该注解中有个很重要的@Import注解;@Import是Spring框架中的一个重要特性,主要用于导入配置类或普通类,实现配置的模块化和动态配置的加载。通过使用@Import注解,可以将多个配置类组合在一起,便于管理和维护大型应用程序的配置。
也就是说MapperScan的相关处理是由MapperScannerRegistrar这个类进行逻辑处理
MapperScannerRegistrar 核心代码
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
var mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
}
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
// MapperScannerRegistrar相关核心处理是由MapperScannerConfigurer处理
var builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", annoAttrs.getBoolean("processPropertyPlaceHolders"));
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
}
var sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
if (StringUtils.hasText(sqlSessionTemplateRef)) {
builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
}
var sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
if (StringUtils.hasText(sqlSessionFactoryRef)) {
builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
}
List<String> basePackages = new ArrayList<>(Arrays.stream(annoAttrs.getStringArray("basePackages"))
.filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
.collect(Collectors.toList()));
if (basePackages.isEmpty()) {
basePackages.add(getDefaultBasePackage(annoMeta));
}
var excludeFilterArray = annoAttrs.getAnnotationArray("excludeFilters");
if (excludeFilterArray.length > 0) {
List<TypeFilter> typeFilters = new ArrayList<>();
List<Map<String, String>> rawTypeFilters = new ArrayList<>();
for (AnnotationAttributes excludeFilters : excludeFilterArray) {
if (excludeFilters.getStringArray("pattern").length > 0) {
// in oder to apply placeholder resolver
rawTypeFilters.addAll(parseFiltersHasPatterns(excludeFilters));
} else {
typeFilters.addAll(typeFiltersFor(excludeFilters));
}
}
builder.addPropertyValue("excludeFilters", typeFilters);
builder.addPropertyValue("rawExcludeFilters", rawTypeFilters);
}
var lazyInitialization = annoAttrs.getString("lazyInitialization");
if (StringUtils.hasText(lazyInitialization)) {
builder.addPropertyValue("lazyInitialization", lazyInitialization);
}
var defaultScope = annoAttrs.getString("defaultScope");
if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {
builder.addPropertyValue("defaultScope", defaultScope);
}
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
// for spring-native
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
MapperScannerConfigurer
核心方法
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
// MapperScannerConfigurer后置处理则由ClassPathMapperScanner进行处理
var scanner = new ClassPathMapperScanner(registry, getEnvironment());
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setExcludeFilters(this.excludeFilters = mergeExcludeFilters());
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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.parseBoolean(lazyInitialization));
}
if (StringUtils.hasText(defaultScope)) {
scanner.setDefaultScope(defaultScope);
}
scanner.registerFilters();
// 扫描basePackage相关的类型注入到Spring中
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
ClassPathMapperScanner
核心方法
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
this.doScan(basePackages);
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
}
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
var beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
if (printWarnLogIfNotFoundMappers) {
LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
+ "' package. Please check your configuration.");
}
} else {
// 处理Spring BeanDefinition
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
/***
** 这个方法主要是给Bean的属性赋能,会生成一个definition.setBeanClass(this.mapperFactoryBeanClass);
而private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
则得出一个结论:definition生成的Bean应该是MapperFactoryBean类
**
**/
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
AbstractBeanDefinition definition;
var registry = getRegistry();
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (AbstractBeanDefinition) holder.getBeanDefinition();
var scopedProxy = false;
if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
definition = (AbstractBeanDefinition) Optional
.ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition())
.map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException(
"The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]"));
scopedProxy = true;
}
var 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
try {
Class<?> beanClass = Resources.classForName(beanClassName);
// Attribute for MockitoPostProcessor
// https://github.com/mybatis/spring-boot-starter/issues/475
definition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, beanClass);
// for spring-native
definition.getPropertyValues().add("mapperInterface", beanClass);
} catch (ClassNotFoundException ignore) {
// ignore
}
//private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
// 定义了Bean的类型是MapperFactoryBean,也就相当xml配置class部分
//<bean id="xxx" class="org.mybatis.spring.mapper.MapperFactoryBean">
definition.setBeanClass(this.mapperFactoryBeanClass);
// 设置MapperFactoryBean的相关属性
definition.getPropertyValues().add("addToConfig", this.addToConfig);
var explicitFactoryUsed = false;
// 设置sqlSessionFactory属性值
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;
}
// 设置sqlSessionTemplate属性值
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 = true;
}
if (!explicitFactoryUsed) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
definition.setLazyInit(lazyInitialization);
if (scopedProxy) {
continue;
}
if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {
definition.setScope(defaultScope);
}
// 后续交由spring框架把bean注入spring上下文中
if (!definition.isSingleton()) {
var proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
registry.removeBeanDefinition(proxyHolder.getBeanName());
}
registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
}
}
}
从上面分析可以得到一个核心类MapperFactoryBean
MapperFactoryBean
核心方法
@Override
protected void checkDaoConfig() {
super.checkDaoConfig();
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
var configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
// 在这里是我们属性的代码,Mybatis增加了mapper
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();
}
}
}
有些人可能会很好奇为啥程序调用checkDaoConfig这个方法?
那我们先看这个类继承示意图:
一层一层父类找你会发现
DaoSupport类的afterPropertiesSet调用
public abstract class DaoSupport implements InitializingBean {
protected final Log logger = LogFactory.getLog(this.getClass());
public DaoSupport() {
}
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
this.checkDaoConfig();
try {
this.initDao();
} catch (Exception var2) {
throw new BeanInitializationException("Initialization of DAO failed", var2);
}
}
protected abstract void checkDaoConfig() throws IllegalArgumentException;
protected void initDao() throws Exception {
}
}
到这里本篇就完结了。