一.集成原理步骤
1.maven引入mybatis-spring-boot-starter
2.mybatis-spring-boot-starter引入了mybatis-spring-boot-autoconfigure
3.mybatis-spring-boot-autoconfigure的spring.factories自动注入自动装配类org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
4.自动装配类MybatisAutoConfiguration里面初始化SqlSessionFactory,SqlSessionTemplate等bean
5.下面为启动扫描,初始化bean,执行调用的整个mybatis源码,总是报mapper接口扫描不到,可以看下下面的逻辑
二.源代码分析
1.在启动类上增加@Mapperscan注解,扫描配置包下所有注解了@Mapper的接口,并生成spring的bean
@SpringBootApplication
@MapperScan(basePackages={com.test.mapper},annotationClass=Mapper.class)
public class OauthApplication {
public static void main(String[] args) {
SpringApplication.run(OauthApplication.class, args);
}
}
2.@Mapperscan引入mapperscan注册配置,@Import类MapperScannerRegistrar
@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 "";
}
3.通过MapperScannerRegistrar,初始化MapperScannerConfigurer
public class MapperScannerRegistrar implements
ImportBeanDefinitionRegistrar, ResourceLoaderAware {
void registerBeanDefinitions(AnnotationMetadata annoMeta
, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry
, String beanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(MapperScannerConfigurer.class);
registry.registerBeanDefinition(beanName
, builder.getBeanDefinition());
}
4.如果在启动类上没有加?@Mapperscan,则解析MybatisAutoConfiguration类中MapperScannerRegistrarNotFoundConfiguration,@importAutoConfiguredMapperScannerRegistrar
public class MybatisAutoConfiguration
implements InitializingBean {
@org.springframework.context.annotation.Configuration
@Import(AutoConfiguredMapperScannerRegistrar.class)
@ConditionalOnMissingBean({ MapperFactoryBean.class
, MapperScannerConfigurer.class })
public static class MapperScannerRegistrarNotFoundConfiguration
implements InitializingBean {
@Override
public void afterPropertiesSet() {
logger.debug(
"Not found configuration for registering
mapper bean using @MapperScan, MapperFactoryBean
and MapperScannerConfigurer.");
}
}
}
5.AutoConfiguredMapperScannerRegistrar初始化?MapperScannerConfigurer
public static class AutoConfiguredMapperScannerRegistrar implements
BeanFactoryAware, EnvironmentAware
, ImportBeanDefinitionRegistrar {
private BeanFactory beanFactory;
private Environment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata
importingClassMetadata, BeanDefinitionRegistry registry) {
if (!AutoConfigurationPackages.has(this.beanFactory)) {
logger.debug("Could not determine auto-configuration package
, automatic mapper scanning disabled.");
return;
}
logger.debug("Searching for mappers annotated with @Mapper");
List<String> packages = AutoConfigurationPackages
.get(this.beanFactory);
if (logger.isDebugEnabled()) {
packages.forEach(pkg -> logger
.debug("Using auto-configuration base package '{}'", pkg));
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
builder.addPropertyValue("annotationClass", Mapper.class);
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
BeanWrapper beanWrapper =
new BeanWrapperImpl(MapperScannerConfigurer.class);
Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors())
.map(PropertyDescriptor::getName)
.collect(Collectors.toSet());
if (propertyNames.contains("lazyInitialization")) {
// Need to mybatis-spring 2.0.2+
builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
}
if (propertyNames.contains("defaultScope")) {
// Need to mybatis-spring 2.0.6+
builder.addPropertyValue("defaultScope"
, "${mybatis.mapper-default-scope:}");
}
// for spring-native
boolean injectSqlSession = environment
.getProperty("mybatis.inject-sql-session-on-mapper-scan", Boolean.class,
Boolean.TRUE);
if (injectSqlSession
&& this.beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory)
this.beanFactory;
Optional<String> sqlSessionTemplateBeanName = Optional
.ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory));
Optional<String> sqlSessionFactoryBeanName = Optional
.ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory));
if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) {
builder.addPropertyValue("sqlSessionTemplateBeanName",
sqlSessionTemplateBeanName.orElse("sqlSessionTemplate"));
} else {
builder.addPropertyValue("sqlSessionFactoryBeanName", sqlSessionFactoryBeanName.get());
}
}
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(MapperScannerConfigurer
.class.getName(), builder.getBeanDefinition());
}
6.MapperScannerConfigurer扫描包下面所有的mapper并且初始化成动态bean,MapperFactoryBean
public class MapperScannerConfigurer implements
BeanDefinitionRegistryPostProcessor, InitializingBean
, ApplicationContextAware, BeanNameAware {
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(this.lazyInitialization)) {
scanner.setLazyInitialization(Boolean
.valueOf(this.lazyInitialization));
}
if (StringUtils.hasText(this.defaultScope)) {
scanner.setDefaultScope(this.defaultScope);
}
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}
7.ClassPathMapperScanner扫描注册MapperFactoryBean,放入了mapperInterface此为mapper接口类
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> {
return "No MyBatis mapper was found in '"
+ Arrays.toString(basePackages) + "' package. Please check your configuration.";
});
} else {
this.processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
BeanDefinitionRegistry registry = this.getRegistry();
Iterator var4 = beanDefinitions.iterator();
while(var4.hasNext()) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)var4.next();
AbstractBeanDefinition definition = (AbstractBeanDefinition)holder.getBeanDefinition();
boolean scopedProxy = false;
if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
definition = (AbstractBeanDefinition)Optional.ofNullable(((RootBeanDefinition)definition)
.getDecoratedDefinition()).map(BeanDefinitionHolder::getBeanDefinition)
.orElseThrow(() -> {
return new IllegalStateException("The target
bean definition of scoped proxy bean not found.
Root bean definition[" + holder + "]");
});
scopedProxy = true;
}
String beanClassName = definition.getBeanClassName();
try {
definition.getPropertyValues().add("mapperInterface", Resources.classForName(beanClassName));
} catch (ClassNotFoundException var10) {
}
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
}
}
}
}
}
8.mapper注入到service使用,由于Mapper接口都是通过FactoryBean生成对象,所以调用getObject获取bean对象,最终由MapperProxyFactory通过jdk动态代理生成mapper的代理对象MapperProxy,在MapperProxy中通过MapperMethod,再调用sqlSession执行数据库操作
public class MapperFactoryBean<T> extends SqlSessionDaoSupport
implements FactoryBean<T> {
private Class<T> mapperInterface;
public T getObject() throws Exception {
return this.getSqlSession().getMapper(this.mapperInterface);
}
}
public class DefaultSqlSession implements SqlSession {
public <T> T getMapper(Class<T> type) {
return this.configuration.getMapper(type, this);
}
}
public class Configuration {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
】
public class MapperRegistry {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory
= (MapperProxyFactory)this.knownMappers.get(type);
return mapperProxyFactory
.newInstance(sqlSession);
}
public class MapperProxyFactory<T> {
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface
.getClassLoader()
, new Class[]{this.mapperInterface}, mapperProxy);
}
}
public class MapperProxy<T> implements InvocationHandler, Serializable {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
interface MapperMethodInvoker {
Object invoke(Object var1, Method var2, Object[] var3, SqlSession var4) throws Throwable;
}
private static class DefaultMethodInvoker implements MapperProxy.MapperMethodInvoker {
private final MethodHandle methodHandle;
public DefaultMethodInvoker(MethodHandle methodHandle) {
this.methodHandle = methodHandle;
}
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return this.methodHandle.bindTo(proxy).invokeWithArguments(args);
}
}
private static class PlainMethodInvoker implements MapperProxy.MapperMethodInvoker {
private final MapperMethod mapperMethod;
public PlainMethodInvoker(MapperMethod mapperMethod) {
this.mapperMethod = mapperMethod;
}
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return this.mapperMethod.execute(sqlSession, args);
}
}
}
public class MapperMethod {
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
Object param;
switch(this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + "' attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
}
|