- 浏览: 14342 次
- 性别:
- 来自: 成都
最新评论
概述:本文按三个部分依次循序渐进对mybatis源码-原理进行分析
1.MyBatis 启动流程
2.MyBatis 执行流程
3.MyBatis 核心组件
第一部分 启动流程
1. Resources 工具类
Resources 通过对ClassLoaderWrapper进一步封装,使调用方无需关心到底调用的是那个类加载器来加载资源文件,为此提供了多个重载的获取资源的方法,分为如下5 类:
1)getResourceURL 从classpath读取文件,返回为URL
2)getResourceAsStream 从classpath读取返回为输入流
3)getResourceAsProperties 同上,返回为properties
4)getResourceAsReader 返回为Reader
5)getResourceAsFile 返回为File
每个方法都有相应的重载方法可提供外部的ClassLoader来加载资源,再看下ClassLoaderWrapper:
后面这三个类加载器在默认情况下都是同一个AppClassLoader的同一个实例,如图:
这样做的好处就是拓展性,即便我们提供的mybatis.xml文件在外部,假设自定义了一个URL的ClassLoader,对后面加载其他的mapper.xml也不会有任何影响。
2.SessionFactoryBuilder
SessionFactoryBuilder中全是各式各样的Build方法
最主要的还是最后两个构造方法,直接进入XMLConfigBuilder
3.XMLConfigBuilder构造方法
3.1 初始化
3.2 XMLMapperEntityResolver初始化
3. XPathParser 初始化
这个过程中会调用 commonConstructor进行 xpath和entityResolver的初始化,然后调用CreateDocument对document进行初始化,后面使用Document结合Xpath对xml进行解析。xpath是xml文档查询语言,EntityResolver 实现了EntityResolver 接口,此接口只有一个方法
解析器在进行解析时会调用此接口进行xml的实现类进行dtd验证XMLMapperEntityResolver
将dtd文件指向jar包中的mapper.xml 和mybatis.config对应的dtd文件。
到此XmlPaser就创建完毕了,这里就是mybatis解析xml的方式DOM+Xpath。
4. 回到XMLConfigBuilder的构造方法
5.Configuration
Configuration 是Mybatis中的核心配置类,包含了mybatis所有的配置信息
继续进行初始化
Configuration中包含一个
默认注册了所有的基本类型、包装类型、集合类型等简写到class的映射。
在构造函数初始化之前还会对默认变量进行赋值,下面对Configuration中几个重要的变量进行初始化
反射工具包ReflectorFactory
工厂设计模式:DefaultReflectorFactory实现了ReflectorFactory接口,按类缓存Reflector,Reflector中包含当前类的所有元数据信息:getter、setter、可读、可写字段 ,以及getter,Setter对应的Invoker,Invoker是对反射的轻量级封装:
Invoker接口有三个实现类:GetFieldInvoker、MethodInvoker、SetFieldInvoker,其中MethodInvoker:
这样做的好处就是:定义接口将设置值与反射隔离,只关注Invoker,而不用管是到底是怎样设置值。
TODO 后面补充TypeParameterResolver
初始化 DefaultObjectFactory
实现类
DefaultObjectFactory具有创建普通对象和Collection对象的功能
创建 DefaultObjectWrapperFactory
TODO 默认是空的 现在还不清楚这个有什么用
创建ProxyFactory
//TODO
创建 MapperRegistry
//TODO
创建InterceptorChain
//创建 TypeHandlerRegistry
等等
6.初始化父类初始化BaseBuilder
BaseBuilder 两个成员变量typeAliasRegistry和typeHandlerRegistry,可从别名找到类型,再从类型找到类型对应的处理器。
7.继续执行XMLConfigBuilder
初始化私有变量localReflectorFactory,在configuration中也有这样一个reflectorFactory的实例。
8.初始化ErrorContext并设置值
ErrorContext使用ThreadLocal 存储每个线程对应的错误信息
每个错误信息都包含:当前错误文件,当前activity,当前object ,错误消息,sql等,通过reset方法可重置当前线程错误信息上下文,最后通过toString格式化输出。
9. 设置Configuration变量 可覆盖原有配置信息
10.到此XMLConfigBuilder构造方法执行完毕,总结一下这个过程中大致做了哪些工作:
1)XPathParser 解析器的初始化,赋给XMLConfigBuilder的parser
2)初始化Configuration
1.MyBatis 启动流程
2.MyBatis 执行流程
3.MyBatis 核心组件
第一部分 启动流程
String resource = "mybatis.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession openSession = sqlSessionFactory.openSession(); Dept dept = openSession.selectOne("dao.DeptDAO.select",1); System.out.println(dept.toString());
1. Resources 工具类
public class Resources { private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();
Resources 通过对ClassLoaderWrapper进一步封装,使调用方无需关心到底调用的是那个类加载器来加载资源文件,为此提供了多个重载的获取资源的方法,分为如下5 类:
1)getResourceURL 从classpath读取文件,返回为URL
2)getResourceAsStream 从classpath读取返回为输入流
3)getResourceAsProperties 同上,返回为properties
4)getResourceAsReader 返回为Reader
5)getResourceAsFile 返回为File
每个方法都有相应的重载方法可提供外部的ClassLoader来加载资源,再看下ClassLoaderWrapper:
public class ClassLoaderWrapper { ClassLoader defaultClassLoader; ClassLoader systemClassLoader; ClassLoaderWrapper() { try { //当前系统AppClassLoader,负责加载classpath,是所有应用classloader的父亲 systemClassLoader = ClassLoader.getSystemClassLoader(); } catch (SecurityException ignored) { // AccessControlException on Google App Engine } } /* * Get a resource as a URL using the current class path * * @param resource - the resource to locate * @return the resource or null */ public URL getResourceAsURL(String resource) { return getResourceAsURL(resource, getClassLoaders(null)); } /* * Get a resource from the classpath, starting with a specific class loader * * @param resource - the resource to find * @param classLoader - the first classloader to try * @return the stream or null */ public URL getResourceAsURL(String resource, ClassLoader classLoader) { return getResourceAsURL(resource, getClassLoaders(classLoader)); } /* * Get a resource from the classpath * * @param resource - the resource to find * @return the stream or null */ public InputStream getResourceAsStream(String resource) { return getResourceAsStream(resource, getClassLoaders(null)); } /* * Get a resource from the classpath, starting with a specific class loader * * @param resource - the resource to find * @param classLoader - the first class loader to try * @return the stream or null */ public InputStream getResourceAsStream(String resource, ClassLoader classLoader) { return getResourceAsStream(resource, getClassLoaders(classLoader)); } /* * Find a class on the classpath (or die trying) * * @param name - the class to look for * @return - the class * @throws ClassNotFoundException Duh. */ public Class<?> classForName(String name) throws ClassNotFoundException { return classForName(name, getClassLoaders(null)); } /* * Find a class on the classpath, starting with a specific classloader (or die trying) * * @param name - the class to look for * @param classLoader - the first classloader to try * @return - the class * @throws ClassNotFoundException Duh. */ public Class<?> classForName(String name, ClassLoader classLoader) throws ClassNotFoundException { return classForName(name, getClassLoaders(classLoader)); } /* * Try to get a resource from a group of classloaders * * @param resource - the resource to get * @param classLoader - the classloaders to examine * @return the resource or null */ InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) { for (ClassLoader cl : classLoader) { if (null != cl) { // try to find the resource as passed InputStream returnValue = cl.getResourceAsStream(resource); // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource if (null == returnValue) { returnValue = cl.getResourceAsStream("/" + resource); } if (null != returnValue) { return returnValue; } } } return null; } /* * Get a resource as a URL using the current class path * * @param resource - the resource to locate * @param classLoader - the class loaders to examine * @return the resource or null */ URL getResourceAsURL(String resource, ClassLoader[] classLoader) { URL url; for (ClassLoader cl : classLoader) { if (null != cl) { // look for the resource as passed in... url = cl.getResource(resource); // ...but some class loaders want this leading "/", so we'll add it // and try again if we didn't find the resource if (null == url) { url = cl.getResource("/" + resource); } // "It's always in the last place I look for it!" // ... because only an idiot would keep looking for it after finding it, so stop looking already. if (null != url) { return url; } } } // didn't find it anywhere. return null; } /* * Attempt to load a class from a group of classloaders * * @param name - the class to load * @param classLoader - the group of classloaders to examine * @return the class * @throws ClassNotFoundException - Remember the wisdom of Judge Smails: Well, the world needs ditch diggers, too. */ Class<?> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException { for (ClassLoader cl : classLoader) { if (null != cl) { try { Class<?> c = Class.forName(name, true, cl); if (null != c) { return c; } } catch (ClassNotFoundException e) { // we'll ignore this until all classloaders fail to locate the class } } } throw new ClassNotFoundException("Cannot find class: " + name); } //如果传入了外部classLoader则getResourceAsStream的时候调用默认使用,传入的第一个 //defaultClassLoader 在此类中未定义 //Thread.currentThread().getContextClassLoader() 创建当前线程的类加载器 // getClass().getClassLoader()加载当前类定义的类加载器默认是AppClassLoader 即ClassLoader.getSystemClassLoader //systemClassLoader ClassLoader[] getClassLoaders(ClassLoader classLoader) { return new ClassLoader[]{ classLoader, defaultClassLoader, Thread.currentThread().getContextClassLoader(), getClass().getClassLoader(), systemClassLoader}; } }
后面这三个类加载器在默认情况下都是同一个AppClassLoader的同一个实例,如图:
这样做的好处就是拓展性,即便我们提供的mybatis.xml文件在外部,假设自定义了一个URL的ClassLoader,对后面加载其他的mapper.xml也不会有任何影响。
2.SessionFactoryBuilder
SessionFactoryBuilder中全是各式各样的Build方法
public class SqlSessionFactoryBuilder { public SqlSessionFactory build(Reader reader) { return build(reader, null, null); } public SqlSessionFactory build(Reader reader, String environment) { return build(reader, environment, null); } public SqlSessionFactory build(Reader reader, Properties properties) { return build(reader, null, properties); } public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } public SqlSessionFactory build(InputStream inputStream) { return build(inputStream, null, null); } public SqlSessionFactory build(InputStream inputStream, String environment) { return build(inputStream, environment, null); } public SqlSessionFactory build(InputStream inputStream, Properties properties) { return build(inputStream, null, properties); } public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(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. } } } public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
最主要的还是最后两个构造方法,直接进入XMLConfigBuilder
3.XMLConfigBuilder构造方法
3.1 初始化
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); }
3.2 XMLMapperEntityResolver初始化
public class XMLMapperEntityResolver implements EntityResolver { private static final String IBATIS_CONFIG_SYSTEM = "ibatis-3-config.dtd"; private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd"; private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd"; private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd"; private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd"; private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd"; /* * Converts a public DTD into a local one * * @param publicId The public id that is what comes after "PUBLIC" * @param systemId The system id that is what comes after the public id. * @return The InputSource for the DTD * * @throws org.xml.sax.SAXException If anything goes wrong */ @Override public InputSource resolveEntity(String publicId, String systemId) throws SAXException { try { if (systemId != null) { String lowerCaseSystemId = systemId.toLowerCase(Locale.ENGLISH); if (lowerCaseSystemId.contains(MYBATIS_CONFIG_SYSTEM) || lowerCaseSystemId.contains(IBATIS_CONFIG_SYSTEM)) { return getInputSource(MYBATIS_CONFIG_DTD, publicId, systemId); } else if (lowerCaseSystemId.contains(MYBATIS_MAPPER_SYSTEM) || lowerCaseSystemId.contains(IBATIS_MAPPER_SYSTEM)) { return getInputSource(MYBATIS_MAPPER_DTD, publicId, systemId); } } return null; } catch (Exception e) { throw new SAXException(e.toString()); } } private InputSource getInputSource(String path, String publicId, String systemId) { InputSource source = null; if (path != null) { try { InputStream in = Resources.getResourceAsStream(path); source = new InputSource(in); source.setPublicId(publicId); source.setSystemId(systemId); } catch (IOException e) { // ignore, null is ok } } return source; } }
3. XPathParser 初始化
public class XPathParser { private final Document document; private boolean validation; private EntityResolver entityResolver; private Properties variables; private XPath xpath; public XPathParser(String xml) { commonConstructor(false, null, null); this.document = createDocument(new InputSource(new StringReader(xml))); } public XPathParser(Reader reader) { commonConstructor(false, null, null); this.document = createDocument(new InputSource(reader)); } public XPathParser(InputStream inputStream) { commonConstructor(false, null, null); this.document = createDocument(new InputSource(inputStream)); } public XPathParser(Document document) { commonConstructor(false, null, null); this.document = document; } public XPathParser(String xml, boolean validation) { commonConstructor(validation, null, null); this.document = createDocument(new InputSource(new StringReader(xml))); } public XPathParser(Reader reader, boolean validation) { commonConstructor(validation, null, null); this.document = createDocument(new InputSource(reader)); } public XPathParser(InputStream inputStream, boolean validation) { commonConstructor(validation, null, null); this.document = createDocument(new InputSource(inputStream)); } public XPathParser(Document document, boolean validation) { commonConstructor(validation, null, null); this.document = document; } public XPathParser(String xml, boolean validation, Properties variables) { commonConstructor(validation, variables, null); this.document = createDocument(new InputSource(new StringReader(xml))); } public XPathParser(Reader reader, boolean validation, Properties variables) { commonConstructor(validation, variables, null); this.document = createDocument(new InputSource(reader)); } public XPathParser(InputStream inputStream, boolean validation, Properties variables) { commonConstructor(validation, variables, null); this.document = createDocument(new InputSource(inputStream)); } public XPathParser(Document document, boolean validation, Properties variables) { commonConstructor(validation, variables, null); this.document = document; } public XPathParser(String xml, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); this.document = createDocument(new InputSource(new StringReader(xml))); } public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); this.document = createDocument(new InputSource(reader)); } public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); this.document = createDocument(new InputSource(inputStream)); } public XPathParser(Document document, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); this.document = document; } public void setVariables(Properties variables) { this.variables = variables; } public String evalString(String expression) { return evalString(document, expression); } public String evalString(Object root, String expression) { String result = (String) evaluate(expression, root, XPathConstants.STRING); result = PropertyParser.parse(result, variables); return result; } public Boolean evalBoolean(String expression) { return evalBoolean(document, expression); } public Boolean evalBoolean(Object root, String expression) { return (Boolean) evaluate(expression, root, XPathConstants.BOOLEAN); } public Short evalShort(String expression) { return evalShort(document, expression); } public Short evalShort(Object root, String expression) { return Short.valueOf(evalString(root, expression)); } public Integer evalInteger(String expression) { return evalInteger(document, expression); } public Integer evalInteger(Object root, String expression) { return Integer.valueOf(evalString(root, expression)); } public Long evalLong(String expression) { return evalLong(document, expression); } public Long evalLong(Object root, String expression) { return Long.valueOf(evalString(root, expression)); } public Float evalFloat(String expression) { return evalFloat(document, expression); } public Float evalFloat(Object root, String expression) { return Float.valueOf(evalString(root, expression)); } public Double evalDouble(String expression) { return evalDouble(document, expression); } public Double evalDouble(Object root, String expression) { return (Double) evaluate(expression, root, XPathConstants.NUMBER); } public List<XNode> evalNodes(String expression) { return evalNodes(document, expression); } public List<XNode> evalNodes(Object root, String expression) { List<XNode> xnodes = new ArrayList<XNode>(); NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET); for (int i = 0; i < nodes.getLength(); i++) { xnodes.add(new XNode(this, nodes.item(i), variables)); } return xnodes; } public XNode evalNode(String expression) { return evalNode(document, expression); } public XNode evalNode(Object root, String expression) { Node node = (Node) evaluate(expression, root, XPathConstants.NODE); if (node == null) { return null; } return new XNode(this, node, variables); } private Object evaluate(String expression, Object root, QName returnType) { try { return xpath.evaluate(expression, root, returnType); } catch (Exception e) { throw new BuilderException("Error evaluating XPath. Cause: " + e, e); } } private Document createDocument(InputSource inputSource) { // important: this must only be called AFTER common constructor try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(validation); factory.setNamespaceAware(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(false); factory.setCoalescing(false); factory.setExpandEntityReferences(true); DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(entityResolver); builder.setErrorHandler(new ErrorHandler() { @Override public void error(SAXParseException exception) throws SAXException { throw exception; } @Override public void fatalError(SAXParseException exception) throws SAXException { throw exception; } @Override public void warning(SAXParseException exception) throws SAXException { } }); return builder.parse(inputSource); } catch (Exception e) { throw new BuilderException("Error creating document instance. Cause: " + e, e); } } private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) { this.validation = validation; this.entityResolver = entityResolver; this.variables = variables; XPathFactory factory = XPathFactory.newInstance(); this.xpath = factory.newXPath(); } }
这个过程中会调用 commonConstructor进行 xpath和entityResolver的初始化,然后调用CreateDocument对document进行初始化,后面使用Document结合Xpath对xml进行解析。xpath是xml文档查询语言,EntityResolver 实现了EntityResolver 接口,此接口只有一个方法
public abstract InputSource resolveEntity (String publicId, String systemId) throws SAXException, IOException;
解析器在进行解析时会调用此接口进行xml的实现类进行dtd验证XMLMapperEntityResolver
将dtd文件指向jar包中的mapper.xml 和mybatis.config对应的dtd文件。
到此XmlPaser就创建完毕了,这里就是mybatis解析xml的方式DOM+Xpath。
4. 回到XMLConfigBuilder的构造方法
public XMLConfigBuilder(Reader reader, String environment, Properties props) { this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); }执行完后,继续调用
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; }
5.Configuration
Configuration 是Mybatis中的核心配置类,包含了mybatis所有的配置信息
继续进行初始化
public Configuration() { typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class); typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class); typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class); typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class); typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class); typeAliasRegistry.registerAlias("FIFO", FifoCache.class); typeAliasRegistry.registerAlias("LRU", LruCache.class); typeAliasRegistry.registerAlias("SOFT", SoftCache.class); typeAliasRegistry.registerAlias("WEAK", WeakCache.class); typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class); typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class); typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class); typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class); typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class); typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class); typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class); typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class); typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class); typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class); typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class); typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class); languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class); languageRegistry.register(RawLanguageDriver.class); }
Configuration中包含一个
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();属性,其中
public class TypeAliasRegistry { private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>(); public TypeAliasRegistry() { registerAlias("string", String.class); registerAlias("byte", Byte.class); registerAlias("long", Long.class); registerAlias("short", Short.class); registerAlias("int", Integer.class); registerAlias("integer", Integer.class); registerAlias("double", Double.class); registerAlias("float", Float.class); registerAlias("boolean", Boolean.class); registerAlias("byte[]", Byte[].class); registerAlias("long[]", Long[].class); registerAlias("short[]", Short[].class); registerAlias("int[]", Integer[].class); registerAlias("integer[]", Integer[].class); registerAlias("double[]", Double[].class); registerAlias("float[]", Float[].class); registerAlias("boolean[]", Boolean[].class); registerAlias("_byte", byte.class); registerAlias("_long", long.class); registerAlias("_short", short.class); registerAlias("_int", int.class); registerAlias("_integer", int.class); registerAlias("_double", double.class); registerAlias("_float", float.class); registerAlias("_boolean", boolean.class); registerAlias("_byte[]", byte[].class); registerAlias("_long[]", long[].class); registerAlias("_short[]", short[].class); registerAlias("_int[]", int[].class); registerAlias("_integer[]", int[].class); registerAlias("_double[]", double[].class); registerAlias("_float[]", float[].class); registerAlias("_boolean[]", boolean[].class); registerAlias("date", Date.class); registerAlias("decimal", BigDecimal.class); registerAlias("bigdecimal", BigDecimal.class); registerAlias("biginteger", BigInteger.class); registerAlias("object", Object.class); registerAlias("date[]", Date[].class); registerAlias("decimal[]", BigDecimal[].class); registerAlias("bigdecimal[]", BigDecimal[].class); registerAlias("biginteger[]", BigInteger[].class); registerAlias("object[]", Object[].class); registerAlias("map", Map.class); registerAlias("hashmap", HashMap.class); registerAlias("list", List.class); registerAlias("arraylist", ArrayList.class); registerAlias("collection", Collection.class); registerAlias("iterator", Iterator.class); registerAlias("ResultSet", ResultSet.class); } @SuppressWarnings("unchecked") // throws class cast exception as well if types cannot be assigned public <T> Class<T> resolveAlias(String string) { try { if (string == null) { return null; } // issue #748 String key = string.toLowerCase(Locale.ENGLISH); Class<T> value; if (TYPE_ALIASES.containsKey(key)) { value = (Class<T>) TYPE_ALIASES.get(key); } else { value = (Class<T>) Resources.classForName(string); } return value; } catch (ClassNotFoundException e) { throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e); } } public void registerAliases(String packageName){ registerAliases(packageName, Object.class); } public void registerAliases(String packageName, Class<?> superType){ ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>(); resolverUtil.find(new ResolverUtil.IsA(superType), packageName); Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses(); for(Class<?> type : typeSet){ // Ignore inner classes and interfaces (including package-info.java) // Skip also inner classes. See issue #6 if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) { registerAlias(type); } } } public void registerAlias(Class<?> type) { String alias = type.getSimpleName(); Alias aliasAnnotation = type.getAnnotation(Alias.class); if (aliasAnnotation != null) { alias = aliasAnnotation.value(); } registerAlias(alias, type); } public void registerAlias(String alias, Class<?> value) { if (alias == null) { throw new TypeException("The parameter alias cannot be null"); } // issue #748 String key = alias.toLowerCase(Locale.ENGLISH); if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) { throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'."); } TYPE_ALIASES.put(key, value); } public void registerAlias(String alias, String value) { try { registerAlias(alias, Resources.classForName(value)); } catch (ClassNotFoundException e) { throw new TypeException("Error registering type alias "+alias+" for "+value+". Cause: " + e, e); } } /** * @since 3.2.2 */ public Map<String, Class<?>> getTypeAliases() { return Collections.unmodifiableMap(TYPE_ALIASES); } }
默认注册了所有的基本类型、包装类型、集合类型等简写到class的映射。
在构造函数初始化之前还会对默认变量进行赋值,下面对Configuration中几个重要的变量进行初始化
反射工具包ReflectorFactory
public class DefaultReflectorFactory implements ReflectorFactory { private boolean classCacheEnabled = true; private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<Class<?>, Reflector>(); public DefaultReflectorFactory() { } @Override public boolean isClassCacheEnabled() { return classCacheEnabled; } @Override public void setClassCacheEnabled(boolean classCacheEnabled) { this.classCacheEnabled = classCacheEnabled; } @Override public Reflector findForClass(Class<?> type) { if (classCacheEnabled) { // synchronized (type) removed see issue #461 Reflector cached = reflectorMap.get(type); if (cached == null) { cached = new Reflector(type); reflectorMap.put(type, cached); } return cached; } else { return new Reflector(type); } } } public interface ReflectorFactory { boolean isClassCacheEnabled(); void setClassCacheEnabled(boolean classCacheEnabled); Reflector findForClass(Class<?> type); } public class Reflector { private final Class<?> type; private final String[] readablePropertyNames; private final String[] writeablePropertyNames; private final Map<String, Invoker> setMethods = new HashMap<String, Invoker>(); private final Map<String, Invoker> getMethods = new HashMap<String, Invoker>(); private final Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>(); private final Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>(); private Constructor<?> defaultConstructor; private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>(); public Reflector(Class<?> clazz) { type = clazz; addDefaultConstructor(clazz); addGetMethods(clazz); addSetMethods(clazz); addFields(clazz); readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]); writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]); for (String propName : readablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } for (String propName : writeablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } } // private void addDefaultConstructor(Class<?> clazz) { Constructor<?>[] consts = clazz.getDeclaredConstructors(); for (Constructor<?> constructor : consts) { if (constructor.getParameterTypes().length == 0) { //检查是否能访问私有方法、属性 if (canAccessPrivateMethods()) { try { constructor.setAccessible(true); } catch (Exception e) { // Ignored. This is only a final precaution, nothing we can do. } } //检查constructor 是否设置上能访问,否则defaultConstructor =null if (constructor.isAccessible()) { this.defaultConstructor = constructor; } } } } private void addGetMethods(Class<?> cls) { Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>(); Method[] methods = getClassMethods(cls); for (Method method : methods) { if (method.getParameterTypes().length > 0) { continue; } String name = method.getName(); if ((name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2)) { name = PropertyNamer.methodToProperty(name); // addMethodConflict(conflictingGetters, name, method); } } resolveGetterConflicts(conflictingGetters); } private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) { //list中最多只能有 一个isB 一个getB ,分为返回值相同 返回值不同,返回值不同分为类型 //不同,和超类子类的返回值不同,所以getB有bool,返回类型A,返回类型B,isB也是三 //种,其中B和A有父子关系,一共有9种可能 for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) { Method winner = null; String propName = entry.getKey(); for (Method candidate : entry.getValue()) { if (winner == null) { winner = candidate; continue; } Class<?> winnerType = winner.getReturnType(); Class<?> candidateType = candidate.getReturnType(); if (candidateType.equals(winnerType)) {// 返回类型相等只有 A,B和bool三种情况 //两个类型相等 后者如果不是bool ,无论谁在前面都抛出异常说明is和get返回相同值 if (!boolean.class.equals(candidateType)) { throw new ReflectionException( "Illegal overloaded getter method with ambiguous type for property " + propName + " in class " + winner.getDeclaringClass() + ". This breaks the JavaBeans specification and can cause unpredictable results."); //返回类型相同,并且同时是bool但是后面这个是以is开头,交换 } else if (candidate.getName().startsWith("is")) { winner = candidate; } //返回值不同,且前者是后者的子类-->winner保持当前值 } else if (candidateType.isAssignableFrom(winnerType)) { // OK getter type is descendant //返回值不同,且前者是后者的父类->交换调整返回子类 } else if (winnerType.isAssignableFrom(candidateType)) { winner = candidate; } else { //抛出异常两个返回类型直接没有任何关联并且其中一个是boolean类型 throw new ReflectionException( "Illegal overloaded getter method with ambiguous type for property " + propName + " in class " + winner.getDeclaringClass() + ". This breaks the JavaBeans specification and can cause unpredictable results."); } } addGetMethod(propName, winner); } } private void addGetMethod(String name, Method method) { if (isValidPropertyName(name)) { getMethods.put(name, new MethodInvoker(method)); Type returnType = TypeParameterResolver.resolveReturnType(method, type); getTypes.put(name, typeToClass(returnType)); } } private void addSetMethods(Class<?> cls) { Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>(); Method[] methods = getClassMethods(cls); for (Method method : methods) { String name = method.getName(); if (name.startsWith("set") && name.length() > 3) { if (method.getParameterTypes().length == 1) { name = PropertyNamer.methodToProperty(name); addMethodConflict(conflictingSetters, name, method); } } } resolveSetterConflicts(conflictingSetters); } private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) { List<Method> list = conflictingMethods.get(name); if (list == null) { list = new ArrayList<Method>(); conflictingMethods.put(name, list); } list.add(method); } private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) { for (String propName : conflictingSetters.keySet()) { List<Method> setters = conflictingSetters.get(propName); Class<?> getterType = getTypes.get(propName); Method match = null; ReflectionException exception = null; for (Method setter : setters) { Class<?> paramType = setter.getParameterTypes()[0]; if (paramType.equals(getterType)) { // should be the best match match = setter; break; } if (exception == null) { try { match = pickBetterSetter(match, setter, propName); } catch (ReflectionException e) { // there could still be the 'best match' match = null; exception = e; } } } if (match == null) { throw exception; } else { addSetMethod(propName, match); } } } private Method pickBetterSetter(Method setter1, Method setter2, String property) { if (setter1 == null) { return setter2; } Class<?> paramType1 = setter1.getParameterTypes()[0]; Class<?> paramType2 = setter2.getParameterTypes()[0]; if (paramType1.isAssignableFrom(paramType2)) { return setter2; } else if (paramType2.isAssignableFrom(paramType1)) { return setter1; } throw new ReflectionException("Ambiguous setters defined for property '" + property + "' in class '" + setter2.getDeclaringClass() + "' with types '" + paramType1.getName() + "' and '" + paramType2.getName() + "'."); } private void addSetMethod(String name, Method method) { if (isValidPropertyName(name)) { setMethods.put(name, new MethodInvoker(method)); Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type); setTypes.put(name, typeToClass(paramTypes[0])); } } private Class<?> typeToClass(Type src) { Class<?> result = null; if (src instanceof Class) { result = (Class<?>) src; } else if (src instanceof ParameterizedType) { result = (Class<?>) ((ParameterizedType) src).getRawType(); } else if (src instanceof GenericArrayType) { Type componentType = ((GenericArrayType) src).getGenericComponentType(); if (componentType instanceof Class) { result = Array.newInstance((Class<?>) componentType, 0).getClass(); } else { Class<?> componentClass = typeToClass(componentType); result = Array.newInstance((Class<?>) componentClass, 0).getClass(); } } if (result == null) { result = Object.class; } return result; } private void addFields(Class<?> clazz) { Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (canAccessPrivateMethods()) { try { field.setAccessible(true); } catch (Exception e) { // Ignored. This is only a final precaution, nothing we can do. } } if (field.isAccessible()) { if (!setMethods.containsKey(field.getName())) { // issue #379 - removed the check for final because JDK 1.5 allows // modification of final fields through reflection (JSR-133). (JGB) // pr #16 - final static can only be set by the classloader int modifiers = field.getModifiers(); if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) { addSetField(field); } } if (!getMethods.containsKey(field.getName())) { addGetField(field); } } } if (clazz.getSuperclass() != null) { addFields(clazz.getSuperclass()); } } private void addSetField(Field field) { if (isValidPropertyName(field.getName())) { setMethods.put(field.getName(), new SetFieldInvoker(field)); Type fieldType = TypeParameterResolver.resolveFieldType(field, type); setTypes.put(field.getName(), typeToClass(fieldType)); } } private void addGetField(Field field) { if (isValidPropertyName(field.getName())) { getMethods.put(field.getName(), new GetFieldInvoker(field)); Type fieldType = TypeParameterResolver.resolveFieldType(field, type); getTypes.put(field.getName(), typeToClass(fieldType)); } } private boolean isValidPropertyName(String name) { return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name)); } /* * This method returns an array containing all methods * declared in this class and any superclass. * We use this method, instead of the simpler Class.getMethods(), * because we want to look for private methods as well. * * @param cls The class * @return An array containing all methods in this class */ //返回当前类、超类所有方法包括私有方法 private Method[] getClassMethods(Class<?> cls) { Map<String, Method> uniqueMethods = new HashMap<String, Method>(); Class<?> currentClass = cls; while (currentClass != null && currentClass != Object.class) {//循环直到Object addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods()); // we also need to look for interface methods - // because the class may be abstract Class<?>[] interfaces = currentClass.getInterfaces(); for (Class<?> anInterface : interfaces) { addUniqueMethods(uniqueMethods, anInterface.getMethods()); } currentClass = currentClass.getSuperclass(); } Collection<Method> methods = uniqueMethods.values(); return methods.toArray(new Method[methods.size()]); } private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) { for (Method currentMethod : methods) { //Bridge方法是 1.5引入泛型后,class字节码为了兼容自动生成的方法 if (!currentMethod.isBridge()) { // //returnType#methodName:parameter1,parameter2... String signature = getSignature(currentMethod); // check to see if the method is already known // if it is known, then an extended class must have // overridden a method if (!uniqueMethods.containsKey(signature)) { if (canAccessPrivateMethods()) { try { currentMethod.setAccessible(true); } catch (Exception e) { // Ignored. This is only a final precaution, nothing we can do. } } uniqueMethods.put(signature, currentMethod); } } } } //returnType#methodName:parameter1,parameter2... private String getSignature(Method method) { StringBuilder sb = new StringBuilder(); Class<?> returnType = method.getReturnType(); if (returnType != null) { sb.append(returnType.getName()).append('#'); } sb.append(method.getName()); Class<?>[] parameters = method.getParameterTypes(); for (int i = 0; i < parameters.length; i++) { if (i == 0) { sb.append(':'); } else { sb.append(','); } sb.append(parameters[i].getName()); } return sb.toString(); } private static boolean canAccessPrivateMethods() { try { SecurityManager securityManager = System.getSecurityManager(); if (null != securityManager) { securityManager.checkPermission(new ReflectPermission("suppressAccessChecks")); } } catch (SecurityException e) { return false; } return true; } /* * Gets the name of the class the instance provides information for * * @return The class name */ public Class<?> getType() { return type; } public Constructor<?> getDefaultConstructor() { if (defaultConstructor != null) { return defaultConstructor; } else { throw new ReflectionException("There is no default constructor for " + type); } } public boolean hasDefaultConstructor() { return defaultConstructor != null; } public Invoker getSetInvoker(String propertyName) { Invoker method = setMethods.get(propertyName); if (method == null) { throw new ReflectionException("There is no setter for property named '" + propertyName + "' in '" + type + "'"); } return method; } public Invoker getGetInvoker(String propertyName) { Invoker method = getMethods.get(propertyName); if (method == null) { throw new ReflectionException("There is no getter for property named '" + propertyName + "' in '" + type + "'"); } return method; } /* * Gets the type for a property setter * * @param propertyName - the name of the property * @return The Class of the propery setter */ public Class<?> getSetterType(String propertyName) { Class<?> clazz = setTypes.get(propertyName); if (clazz == null) { throw new ReflectionException("There is no setter for property named '" + propertyName + "' in '" + type + "'"); } return clazz; } /* * Gets the type for a property getter * * @param propertyName - the name of the property * @return The Class of the propery getter */ public Class<?> getGetterType(String propertyName) { Class<?> clazz = getTypes.get(propertyName); if (clazz == null) { throw new ReflectionException("There is no getter for property named '" + propertyName + "' in '" + type + "'"); } return clazz; } /* * Gets an array of the readable properties for an object * * @return The array */ public String[] getGetablePropertyNames() { return readablePropertyNames; } /* * Gets an array of the writeable properties for an object * * @return The array */ public String[] getSetablePropertyNames() { return writeablePropertyNames; } /* * Check to see if a class has a writeable property by name * * @param propertyName - the name of the property to check * @return True if the object has a writeable property by the name */ public boolean hasSetter(String propertyName) { return setMethods.keySet().contains(propertyName); } /* * Check to see if a class has a readable property by name * * @param propertyName - the name of the property to check * @return True if the object has a readable property by the name */ public boolean hasGetter(String propertyName) { return getMethods.keySet().contains(propertyName); } public String findPropertyName(String name) { return caseInsensitivePropertyMap.get(name.toUpperCase(Locale.ENGLISH)); } }
工厂设计模式:DefaultReflectorFactory实现了ReflectorFactory接口,按类缓存Reflector,Reflector中包含当前类的所有元数据信息:getter、setter、可读、可写字段 ,以及getter,Setter对应的Invoker,Invoker是对反射的轻量级封装:
public interface Invoker { Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException; Class<?> getType(); }
Invoker接口有三个实现类:GetFieldInvoker、MethodInvoker、SetFieldInvoker,其中MethodInvoker:
public class MethodInvoker implements Invoker { private final Class<?> type; private final Method method; public MethodInvoker(Method method) { this.method = method; if (method.getParameterTypes().length == 1) { type = method.getParameterTypes()[0]; } else { type = method.getReturnType(); } } @Override public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException { return method.invoke(target, args); } @Override public Class<?> getType() { return type; } }
这样做的好处就是:定义接口将设置值与反射隔离,只关注Invoker,而不用管是到底是怎样设置值。
TODO 后面补充TypeParameterResolver
初始化 DefaultObjectFactory
public interface ObjectFactory { /** * Sets configuration properties. * @param properties configuration properties */ void setProperties(Properties properties); /** * Creates a new object with default constructor. * @param type Object type * @return */ <T> T create(Class<T> type); /** * Creates a new object with the specified constructor and params. * @param type Object type * @param constructorArgTypes Constructor argument types * @param constructorArgs Constructor argument values * @return */ / <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs); /** * Returns true if this object can have a set of other objects. * It's main purpose is to support non-java.util.Collection objects like Scala collections. * * @param type Object type * @return whether it is a collection or not * @since 3.1.0 */ <T> boolean isCollection(Class<T> type); }
实现类
public class DefaultObjectFactory implements ObjectFactory, Serializable { private static final long serialVersionUID = -8855120656740914948L; @Override public <T> T create(Class<T> type) { return create(type, null, null); } @SuppressWarnings("unchecked") @Override public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { //传入指定类型,构造参数类型、构造函数值 创建对象,后面两个参数其中一个为空则表示调用默认构造函数 //检查对象是否是集合对象 Class<?> classToCreate = resolveInterface(type); // we know types are assignable return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs); } @Override public void setProperties(Properties properties) { // no props for default //如果要自定义对象工厂,可在config文件中指定property属性 } private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { try { Constructor<T> constructor; if (constructorArgTypes == null || constructorArgs == null) { constructor = type.getDeclaredConstructor(); if (!constructor.isAccessible()) { constructor.setAccessible(true); } return constructor.newInstance(); } constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()])); if (!constructor.isAccessible()) { constructor.setAccessible(true); } return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()])); } catch (Exception e) { StringBuilder argTypes = new StringBuilder(); if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) { for (Class<?> argType : constructorArgTypes) { argTypes.append(argType.getSimpleName()); argTypes.append(","); } argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing , } StringBuilder argValues = new StringBuilder(); if (constructorArgs != null && !constructorArgs.isEmpty()) { for (Object argValue : constructorArgs) { argValues.append(String.valueOf(argValue)); argValues.append(","); } argValues.deleteCharAt(argValues.length() - 1); // remove trailing , } throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e); } } protected Class<?> resolveInterface(Class<?> type) { Class<?> classToCreate; if (type == List.class || type == Collection.class || type == Iterable.class) { classToCreate = ArrayList.class; } else if (type == Map.class) { classToCreate = HashMap.class; } else if (type == SortedSet.class) { // issue #510 Collections Support classToCreate = TreeSet.class; } else if (type == Set.class) { classToCreate = HashSet.class; } else { classToCreate = type; } return classToCreate; } @Override public <T> boolean isCollection(Class<T> type) { return Collection.class.isAssignableFrom(type); }
DefaultObjectFactory具有创建普通对象和Collection对象的功能
创建 DefaultObjectWrapperFactory
public interface ObjectWrapperFactory { boolean hasWrapperFor(Object object); ObjectWrapper getWrapperFor(MetaObject metaObject, Object object); } public class DefaultObjectWrapperFactory implements ObjectWrapperFactory { @Override public boolean hasWrapperFor(Object object) { return false; } @Override public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) { throw new ReflectionException("The DefaultObjectWrapperFactory should never be called to provide an ObjectWrapper."); }
TODO 默认是空的 现在还不清楚这个有什么用
创建ProxyFactory
//TODO
创建 MapperRegistry
//TODO
创建InterceptorChain
//创建 TypeHandlerRegistry
等等
6.初始化父类初始化BaseBuilder
BaseBuilder 两个成员变量typeAliasRegistry和typeHandlerRegistry,可从别名找到类型,再从类型找到类型对应的处理器。
7.继续执行XMLConfigBuilder
初始化私有变量localReflectorFactory,在configuration中也有这样一个reflectorFactory的实例。
8.初始化ErrorContext并设置值
ErrorContext使用ThreadLocal 存储每个线程对应的错误信息
private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>(); private ErrorContext stored; private String resource; private String activity; private String object; private String message; private String sql; private Throwable cause;
每个错误信息都包含:当前错误文件,当前activity,当前object ,错误消息,sql等,通过reset方法可重置当前线程错误信息上下文,最后通过toString格式化输出。
9. 设置Configuration变量 可覆盖原有配置信息
10.到此XMLConfigBuilder构造方法执行完毕,总结一下这个过程中大致做了哪些工作:
1)XPathParser 解析器的初始化,赋给XMLConfigBuilder的parser
2)初始化Configuration
发表评论
-
MyBatis原理(2)-执行流程 4 Mapper的执行
2018-09-07 11:15 763执行方式2: DeptMapper mapper ... -
MyBatis原理(2)-执行流程 3 处理结果集
2018-09-07 10:24 872DefaultResultSetHandler#handleR ... -
MyBatis原理(2)-执行流程 2
2018-08-31 17:47 546@Override public <E> L ... -
MyBatis原理(2)-执行流程 1 BoundSql生成
2018-08-31 17:09 1265MyBatis执行两种方式: 1. SqlSession ... -
MyBatis原理(1)-启动流程3- mapper 加载
2018-08-25 22:27 8611.接着上一篇文章解析mapper第一步 mapperEle ... -
MyBatis原理(1)-启动流程2
2018-08-24 17:21 5551.XMLConfigBuilder.parse ...
相关推荐
- **运行Java类**:通过调用`org.mybatis.generator.api.MyBatisGenerator`类的静态方法并传入配置文件路径和是否覆盖已存在文件等参数,来启动代码生成过程。 - **模板定制**:Mybatis Generator支持自定义代码...
本文将详细介绍如何在Spring Boot项目中集成MyBatis,并基于`spring-boot-starter-mybatis-spring-boot-2.1.2.zip`这个压缩包,来解析整合过程。 1. **Spring Boot Starter MyBatis** `spring-boot-starter-...
1. **SqlSessionFactoryBuilder**: 用于构建SqlSessionFactory,它是MyBatis的核心工厂类,可以创建SqlSessionFactory实例,通常在应用程序启动时初始化。 2. **SqlSessionFactory**: 提供SqlSession的创建,...
总结,Spring Boot 2.2.0版本与MyBatis的集成极大地简化了开发过程,使得开发者可以更专注于业务逻辑而不是繁琐的配置。通过理解其集成原理,我们可以快速搭建出高效稳定的Java Web应用。在Windows环境下,合理利用...
1. **MyBatis-Plus 简介** MyBatis-Plus 是 MyBatis 的轻量级框架,它在 MyBatis 的基础上做了增强,提供了一些实用功能,如自动 CRUD 操作、条件构造器、分页插件等。MyBatis-Plus 可以理解为 MyBatis 的简化版,...
本文将深入探讨Spring Boot如何与MyBatis集成,并以`spring-boot-starter-mybatis-spring-boot-2.1.4`为例,解析集成过程及其实现原理。 一、Spring Boot与MyBatis的集成原理 Spring Boot通过`spring-boot-starter...
它是一个Bean后处理器,会在Spring启动时扫描指定包下的所有接口,如果这些接口实现了MyBatis的`Mapper`接口,那么就会为每个接口生成一个对应的代理类,并将其注册到Spring容器中。这样,我们就可以在Spring中直接...
综上所述,Spring Boot 1.2.2集成MyBatis是一个相对简单的流程,通过引入起步依赖、配置数据源和MyBatis,以及创建Mapper接口,开发者可以快速构建起数据库操作的基础设施。虽然随着版本的更新,Spring Boot的集成...
本文将重点介绍Spring Boot如何集成MyBatis,并以1.3.1版本为例,探讨其具体实现过程。 一、Spring Boot与MyBatis的集成原理 Spring Boot通过starter机制简化了MyBatis的集成。`spring-boot-starter-mybatis`是...
总之,"跑通的Mybatis后端源码-WhenToMint"项目提供了一个实际运行的Mybatis应用示例,通过学习和分析这个项目,你可以深入理解Mybatis的工作原理,以及如何在实际项目中使用Mybatis进行数据库操作。同时,良好的...
通过这个项目,开发者可以学习到如何在SpringBoot项目中整合Mybatis,理解Spring MVC的工作原理,以及如何利用Redis优化数据访问。这对于提升个人技能和解决实际问题具有重要价值。 总结,"springboot-mybatis(1)....
此外,还可以了解到Spring Boot的自动配置原理、MyBatis的动态SQL特性以及微服务架构的基本理念。这个项目是一个理想的起点,对于希望深入了解Spring Boot和MyBatis集成的人来说,具有很高的学习价值。
MyBatis Generator的主要工作原理是通过读取数据库元数据,然后根据预设的模板生成相应的Java源代码和XML配置文件。这样,开发者就可以避免手动编写重复性的代码,从而节省大量时间,提高开发效率。 **主要组件和...
通过深入研究"Mybatis-3"的源码,我们可以理解其内部的执行流程,例如Executor的执行策略、StatementHandler的预编译和执行过程、ParameterHandler参数绑定的逻辑,以及ResultSetHandler结果集处理的细节。...
通过这样的配置,Spring能够自动管理MyBatis的生命周期,简化了开发过程,同时利用Spring的强大功能,如AOP事务管理,使得整体架构更为合理和高效。 总的来说,MyBatis是一个强大的持久层框架,通过理解其核心概念...
MyBatis-Spring整合包是将流行的ORM框架MyBatis与Spring框架紧密集成的一个库,旨在简化在Spring应用中使用MyBatis的过程。这个整合包提供了便捷的数据访问接口和事务管理,使得开发者能够更容易地在Spring应用中...
PageHelper的核心原理是利用Java的ThreadLocal存储分页参数,确保在多线程环境下分页信息的独立性,并通过MyBatis的拦截器机制动态修改SQL,实现分页查询。 **一、PageHelper的安装与配置** 1. **添加依赖**:在...
1. **Configuration**:这是MyBatis的核心配置对象,它包含了全局配置信息,如数据源、事务管理器、Mappers等。XMLConfigBuilder类负责解析`mybatis-config.xml`文件,构建Configuration实例。 2. **...
在使用MyBatis框架进行开发时,我们可能会遇到这样的错误提示:“`xxxMapper is not known to the MapperRegistry`”。这个错误通常表示MyBatis无法找到你定义的Mapper接口,这可能是配置问题或者是类扫描的问题。...
1. **无侵入性**:Mybatis-Plus作为MyBatis的一个扩展包,不会对现有的MyBatis架构造成任何影响,同时支持MyBatis的所有特性。 2. **依赖简单**:仅依赖MyBatis和MyBatis-Spring两个库,减少了项目中的依赖复杂度。 ...