一、学习之前思考
在学习mybatis之前,我是首先带着三个疑问去学习mybatis:
(1)数据库链接怎样管理:与数据库链接的线程池创建,sql执行等(个人理解再牛逼的orm框架,最后都需要转成
mysql(本次实例中是使用mysql数据)的原生的sql语句,最后执行的肯定是sql执行语句)
(2)java的dao层怎样操作sql语句:一个dao接口,为什么可以直接执行sql语句,
(3)数据结果是怎样封装的
由上述三个疑问,总结下面的mybatis的orm框架大体图:
二、结构化分析
由上述的描述,总结了下面的mybatis的需要实现的功能的结构化图:
三、DAO层容器
2.1、dao层注入
(1)怎样获取dao??
通过注解@MapperScan获取扫描到的包,而真正去处理的是MapperScannerRegistrar。
(2)MapperScannerRegistrar是怎样处理扫描到的dao接口的??
MapperScannerRegistrar 主要处理过程核心方法:registerBeanDefinitions
MapperScannerRegistrar.registerBeanDefinitions:
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//获取注解的属性
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
//通过spring上下文实例化一个扫描器
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// 非空判断,设置resourceLoader,3.1之后不需要检测
if (resourceLoader != null) {
scanner.setResourceLoader(resourceLoader);
}
//Annotation 注解类设置
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
scanner.setAnnotationClass(annotationClass);
}
//接口标记类加载
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
scanner.setMarkerInterface(markerInterface);
}
//bean name自动生成类加载
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
}
//MapperFactoryBean类加载-----这个就是代理dao的BeanFactory
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
//设置扫描器数据库管理模板和sqlSessionFactory
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
List<String> basePackages = new ArrayList<String>();
for (String pkg : annoAttrs.getStringArray("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : annoAttrs.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));//核心方法,具体扫描所做的逻辑都在此方法中
}
ClassPathMapperScanner.doScan:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
for (String basePackage : basePackages) {
//扫描包,由于包扫描得到了所有的class,去掉一些非spring config的类,得到ScannedGenericBeanDefinition类型的BeanDefinition
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
//获取需要注解的bean的生命周期,如:singleton
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
//设置bean的生命周期
candidate.setScope(scopeMetadata.getScopeName());
//bean名字自动初始化
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
//如果是继承抽象的AbstractBeanDefinition,在进一步初始化,此处初始化主要是为了设置默认值
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
//同上一步一样,如果该bean继承AnnotatedBeanDefinition接口,设置默认初始值
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
//赋给bean定的持有者
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
//判断当前的bean对应scope注解的那种形式,mybatis 没有使用任何形式故结果返回的还是definitionHolder
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//注册bean
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
ClassPathMapperScanner.processBeanDefinitions:
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;//mapperFactoryBean 注册的是此bean,最终我们每个dao都转换成mapperFactoryBean,注意一个dao对应一个mapperFactoryBean
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
if (logger.isDebugEnabled()) {
logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + definition.getBeanClassName() + "' mapperInterface");
}
//definition设置参数,即被代理的bean,查看mapperFactoryBean构造器可以看到,只有一个参数mapperInterface------代理bean构造器初始化
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
definition.setBeanClass(this.mapperFactoryBean.getClass());//设置beanClass,这个值代理对象bean
//代理添加参数addToConfig
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
//如果当前的被代理的bean所指定的sqlSessionFactory有名字,将当前sqlSessionFactory作为参数添加到代理bean中
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
//为空直接使用当前的sqlSessionFactory
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
//同sqlSessionFactory一样原理
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) {
if (logger.isDebugEnabled()) {
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
//设置根据类型自动注入
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);//这行代码,,,,根据type实例化的,,,配置多数据源的时候这个地方就会出问题
}
}
}
(3)总结
一句话:dao层注入,dao接口->definition->BeanDefinitionHolder->MapperFactoryBean,是通过MapperFactoryBean来代理bean的
2.2、xml与dao绑定
上面我们已经注入了dao的MapperFactoryBean代理bean,每一个dao都有一个对应的MapperFactoryBean代理对象,现在就是想知道xml或者sql语句怎么和MapperFactoryBean联系在一起的。
2.2.1、xml文件扫面映射
由于扫描xml文件是在sqlSessionFactory中扫描的,所以我是先从sqlSessionFactory 的bean注入代码去了解的:
javaconfig的SqlSessionFactory:
@Bean(name="sqlSessionFactory1")
@Primary
public SqlSessionFactory sqlSessionFactory1() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(testOne);
List<Resource> resources=new ArrayList<>();
resources.addAll(Arrays.asList(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/*/person.xml")));
factoryBean.setMapperLocations(resources.toArray(new Resource[resources.size()]));
return factoryBean.getObject();
}
核心方法SqlSessionFactoryBean.buildSqlSessionFactory:
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;//整个SqlSessionFactory的核心,没有他SqlSessionFactory就没法去往后操作的,主要是保存SqlSessionFactory的一些配置信息
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {//如果不为空直接解析xml文件
configuration = this.configuration;
if (configuration.getVariables() == null) {
configuration.setVariables(this.configurationProperties);//这个不知道什么参数可以在这里设置,你要是懂,可留言告知我
} else if (this.configurationProperties != null) {
configuration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
if (this.configurationProperties != null) {
configuration.setVariables(this.configurationProperties);
}
}
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
if (this.vfs != null) {
configuration.setVfsImpl(this.vfs);
}
if (hasLength(this.typeAliasesPackage)) {
String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
}
if (!isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type alias: '" + typeAlias + "'");
}
}
}
if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered plugin: '" + plugin + "'");
}
}
}
if (hasLength(this.typeHandlersPackage)) {
String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
configuration.getTypeHandlerRegistry().register(packageToScan);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
}
if (!isEmpty(this.typeHandlers)) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type handler: '" + typeHandler + "'");
}
}
}
if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
if (this.cache != null) {
configuration.addCache(this.cache);
}
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
}
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());//此处是重点,解析了sql的xml文件
xmlMapperBuilder.parse();//解析xml文件,注意,这里解析返回的会有72中mybatis已经规定好的类型别名(我们在定义别名的时候注意不要重复)
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
}
return this.sqlSessionFactoryBuilder.build(configuration);
}
真正解析类:XMLMapperBuilder.parse()
public void parse() {
if (!configuration.isResourceLoaded(resource)) {//资源如果未加载,开始解析
configurationElement(parser.evalNode("/mapper"));//解析mapper节点---对应xml文件<mapper></mapper>
configuration.addLoadedResource(resource);
bindMapperForNamespace();//命名空间绑定:namespace="bootdemo.dao.db1.PersonDaoOne",此处会生成一个对应的MapperProxyFactory 来代理MapperProxyFactoryBean
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
xml解析方法实现:XMLMapperBuilder.configurationElement
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");//加载命名空间
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);//设置命名空间名称---留个疑问:为什么命名空间必须是dao类路径
cacheRefElement(context.evalNode("cache-ref"));//mapper缓存设置
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));//xml文件定的参数map,对应xml标签:<parameterMap></parameterMap>
resultMapElements(context.evalNodes("/mapper/resultMap"));//xml文件定的结果DTO对应map,对应xml标签:<resultMap></resultMap>
sqlElement(context.evalNodes("/mapper/sql"));//xml文件定的结果sql标签,对应xml标签:<sql></sql>
//增删改语句解析,增删改对应的映射类为:XMLStatementBuilder,解析成的XMLStatementBuilder放在configuration中
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
生成MapperProxyFactory:XMLMapperBuilder.bindMapperForNamespace----实际上xml文件是由MapperRegissry来映射的
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
configuration.addLoadedResource("namespace:" + namespace);//空间命名添加到容器中
configuration.addMapper(boundType);//将MapperProxyFactory 代理类添加到容器中
}
}
}
}
//添加MapperProxyFactory代理的核心代码
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
总结:基于上述的代码,我们已经将整个的xml配置文件解析到configuration,并赋给SqlSessionFactory属中的属性:xml->configuration->SqlSessionFactory
四、调用方怎样调用
由第二部分介绍了,一个dao层怎么变成一个bean实例的(MapperFactoryBean),第三部分介绍了怎样xml文件怎样
生成java实例(MapperRegissry),接下来就是怎么将两者联系在一起和怎样去执行方法的。
4.1、xml和注入的dao bean关联
java的类dao生成了MapperFactoryBean实例,从MapperFactoryBean源码可以看到MapperFactoryBean继承了FactoryBean,所以他是一个Bean工厂类,我们可以看到他的bean获取实例方法getObject()。
MapperFactoryBean.getObject():获取实例对象
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
对于采用SqlSessionTemplate模板管理的getMapper方法:
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
MapperRegistry.getMapper():
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
protected T newInstance(MapperProxy<T> mapperProxy) {
//MapperProxy 又是一个代理对象,最终我们生成的bean是由MapperProxy来代理的
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
总结:MapperFactoryBean.getObject()->SqlSession.getMapper()->MapperRegistry.getMapper()->MapperProxyFactory.newInstance()->MapperProxy代理,最终获取到实例
4.2、MapperProxy怎样去执行dao中的方法
首先看MapperProxy代理类的invoke是怎样执行的方法的。
MapperProxy.invoke():
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//如果直接实现代理类,直接运行方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
//如果直接实现代理类,直接运行方法----据说是JDK 1.8属性
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//获取需要执行的方法mapper,从Configuration容器中获取到对应的MapperMethod:
//MapperMethod.SqlCommand 静态类,其构造器有个方法resolveMappedStatement为后去mapper 接口
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);//真正执行的接口
}
sql执行接口MapperMethod.execute():
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);//参数封装
result = rowCountResult(sqlSession.insert(command.getName(), param));//执行添加方法
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);//参数封装
result = rowCountResult(sqlSession.update(command.getName(), param));//执行修改方法
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);//参数封装
result = rowCountResult(sqlSession.delete(command.getName(), param));//执行删除语句
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
以查询为例,真正查询的接sql:BaseExecutor.query()
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();//执行查询
return resultSetHandler.<E> handleResultSets(ps);//结果封装到dto中
}
有啥疑问请指出,联系方式qq:158479841
拒绝转载!!!!!!