mp源码分析
1.MybatisSessionFactoryBuilder 中的build方法引入解析XML的类MybatisXMLConfigBuilder
MybatisSessionFactoryBuilder mf = new MybatisSessionFactoryBuilder();
MybatisSessionFactoryBuilder.java 调用 build方法
@Override
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
MybatisXMLConfigBuilder parser = new MybatisXMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
MybatisXMLConfigBuilder.java 构造方法
private MybatisXMLConfigBuilder(XPathParser parser, String environment, Properties props) {
//TODO 自定义 Configuration
super(new MybatisConfiguration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
实例化MybatisConfiguration.java类,加载ISqlInjector和 MybatisMapperRegistry
/*
* SQL 注入器,实现 ISqlInjector 或继承 AutoSqlInjector 自定义方法
*/
public static ISqlInjector SQL_INJECTOR = new AutoSqlInjector();
/*
* Mapper 注册
*/
public final MybatisMapperRegistry mybatisMapperRegistry = new MybatisMapperRegistry(this);
上一个类调用的parse()方法
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
Properties settings = settingsAsPropertiess(root.evalNode("settings"));
// issue #117 read properties first
propertiesElement(root.evalNode("properties"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
private void mapperElement(XNode parent) throws Exception {
/**
* 定义集合 用来分类放置mybatis的Mapper与XML 按顺序依次遍历
*/
if (parent != null) {
//指定在classpath中的mapper文件
Set<String> resources = new HashSet<String>();
//指向一个mapper接口
Set<Class<?>> mapperClasses = new HashSet<Class<?>>();
setResource(parent, resources, mapperClasses);
// 依次遍历 首先 resource 然后 mapper
for (String resource : resources) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
//TODO
MybatisXMLMapperBuilder mapperParser = new MybatisXMLMapperBuilder(inputStream, configuration, resource,
configuration.getSqlFragments());
mapperParser.parse();
}
for (Class<?> mapper : mapperClasses) {
//TODO
configuration.addMapper(mapper);
}
}
}
MybatisXMLMapperBuilder 调用parse()方法
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
}
bindMapperForNamespace 具体方法
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
// ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a
// flag
// to prevent loading again this resource from the mapper
// interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
//TODO 注入 CURD 动态 SQL
if (BaseMapper.class.isAssignableFrom(boundType)) {
MybatisConfiguration.SQL_INJECTOR.inspectInject(configuration, builderAssistant, boundType);
}
}
}
}
addMapper方法 MybatisMapperRegistry.java代理类方法
@Override
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
//TODO 如果之前注入 直接返回
return;
// throw new BindingException("Type " + type +
// " is already known to the MybatisPlusMapperRegistry.");
}
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.
//TODO 自定义无 XML 注入
MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
MybatisMapperAnnotationBuilder 中的parse()方法
@Override
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
boolean existXml = loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
Method[] methods = type.getMethods();
//TODO 注入存在 xxMapper.xml CURD (应该在注解之前注入)
inspectInject(existXml);
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
动态sql注入器
/*
* 注入 CURD 动态 SQL(XML不存在时注入)
*/
private void inspectInject(boolean flag) {
if (!flag && BaseMapper.class.isAssignableFrom(type)) {
MybatisConfiguration.SQL_INJECTOR.inspectInject(configuration, assistant, type);
}
}
AutoSqlInjector 开始注入动态sql语句
总结
mp也是遵循mybatis设计理念,通过设置注解的方法实现加载动态sql语句
mybatis 注解实现 MapperAnnotationBuilder
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
String resource = type.getName().replace('.', '/') + ".java (best guess)";
this.assistant = new MapperBuilderAssistant(configuration, resource);
this.configuration = configuration;
this.type = type;
sqlAnnotationTypes.add(Select.class);
sqlAnnotationTypes.add(Insert.class);
sqlAnnotationTypes.add(Update.class);
sqlAnnotationTypes.add(Delete.class);
sqlProviderAnnotationTypes.add(SelectProvider.class);
sqlProviderAnnotationTypes.add(InsertProvider.class);
sqlProviderAnnotationTypes.add(UpdateProvider.class);
sqlProviderAnnotationTypes.add(DeleteProvider.class);
}
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
参考
注解实现sql注入:https://blog.csdn.net/wo_shi_LTB/article/details/79242889
网友评论