- 浏览: 14340 次
- 性别:
- 来自: 成都
最新评论
1.XMLConfigBuilder.parse
2.调用XPathParser.evalNode返回代码configuration根节点的XNode对象,并将之前设置的dom对象传入
3.XPathParser调用xpath对象的evaluate方法构造返回org.w3c.dom.Node对象,evalNode的重载方法构造返回XNode对象,XNode是mybatis对Node节点的封装:
Xnode在构造方法会对Node中的属性调用parseAttributes再调用PropertyParser进行解析,对“${}”表达式进行解析,使用XpathParser中的variables对当前节点的attributes进行设值,此处的node是Configuration ,attributeNodes.getLength()长度为0直接跳出。
parseAttributes后再对parseBody进行对当前节点的所有的CDDATA节点或者文本节点中的表达式进行赋值,设置为当前节点的body。到此configuration作为根节点XNode返回。
4.parseConfiguration
4.1 propertiesElement(root.evalNode("properties"));
和上面的流程一样,不同的是根节点自身持有对XPathParser的引用,随后XPathParser调用evalNode,最后由XPathParser,xpath对象调用xpath.evaluate(expression, root, returnType)方法返回原生dom对象,再对XNode进行初始化
4.1.1 获取resource或者url属性对应的外部属性文件并加加载到configuration的variables和当前XpathParser的variables中。准备给后续的表达式进行赋值,这一步需要最先进行解析,值得注意的是url和resource属性只能设置一个。
4.2 解析 <setting>节点
流程和解析properties一样,可以看出来 evalNode的作用就是在构造XNode,在构造XNode的过程中,调用parseAttributes对节点attribute进行解析并放入Node的attribute属性备用,接着调用parseBody对Node节点body进行解析,如果第一次getBodyData返回不为空即是文本或者CDATA节点,如果其中包含表达式使用PropertyParser.pase解析并赋值。
解析节点之后调用settingsAsProperties方法将settings中的setting节点,name和value解析为properties对象,然后调用MetaClass对Configuration进行属性检查,如果在Configuration中没有的属性,则抛出异常。在解析的过程中第一步调用getChildren()和getChildrenAsProperties方法将所有子节点返回。然后调用MetaClass.forClass工厂方法构造指定Configuration类型的Reflector和reflectorFactory,reflectorFactory是Reflector的缓存,Reflector则包含了对getter setter field 的封装,初始化这两个对象后作为MetaClass的私有属性,MetaClass则对reflector进行进一步的封装。下面在Metaclass中对setting节点进行检查是否有对应的 setter方法
4.3加载定义VFS
//TODO 等后面用到VFS在进行添加
4.4 加载 类型别名typeAliases
扫描该节点下的子节点如果子节点名称是package则扫描这个包下面所有的类,每一个在包 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。如果不是package则默认对每个
在进行扫描包的过程中
ResolverUtil能根据相应的条件(某个类的超类,子类,注解等)在指定路径下查找对应满足条件的所有类加载到matches这个set中。
ResolverUtil有一个接口Test 以及IsA、AnnotatedWith两个实现类,IsA的matches表示当前类是传入类的父亲,AnnotatedWith的matches表示当前是否标注指定注解,classloader属性是一个单独的属性,用来加载查找到后的类,没有设置则默认为
findImplementations和findAnnotated都会调用find进行类的加载和查找,其中find方法调用VFS查询指定包下面的所有子路径,找出class结尾的路径后调用addIfMatching方法,addIfMatching负责对类进行加载校验并添加到matches集合后,供getClasses方法返回。
4.5 加载插件
注:从4.3开始不在对evalNode方法进行跟踪
首先遍历子节点,获取interceptor属性,即包名resolveClass方法最终会调用BaseBuilder的typeAliasRegistry的resolveAlias方法,这个方法会对先检查是否已经存在此别名,不存在则使用Resources进行加载。继续读取interceptor节点的property子节点,并设置到Inteceptor中,在最后添加到Configuration中的拦截器链中。
4.6 加载objectFactory
这部分逻辑和加载plugin类似
4.7 加载objectWrapperFactory和上面类似
4.8 加载reflectorFactory 反射工厂,和上面类似
4.9 加载settings
这部分就是对settings下面的子节点进行解析写入到Configuration中
4.10 加载environments,事务工厂和数据源的初始化
4.11 解析databaseIdProvider,根据不同的数据库标识使用不同的映射语句
如果配置了 type=VENDOR(英文释义:厂商)当前只有这一个值,首先获取节点的properties,再构造DatabaseProvider的实例,在
最后将configuration中的databaseId设置为这个时候获取到的"oracle"或是mysql 就可以在update,delete 等语句中进行指定。
4.12 加载typeHandlers
如果typehandlers子节点有package获取名称属性调用
进行扫描resolverUtil上面有讲到,否则挨个的进行注册。
public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; }
2.调用XPathParser.evalNode返回代码configuration根节点的XNode对象,并将之前设置的dom对象传入
public XNode evalNode(String expression) { return evalNode(document, expression); }
3.XPathParser调用xpath对象的evaluate方法构造返回org.w3c.dom.Node对象,evalNode的重载方法构造返回XNode对象,XNode是mybatis对Node节点的封装:
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); } }
Xnode在构造方法会对Node中的属性调用parseAttributes再调用PropertyParser进行解析,对“${}”表达式进行解析,使用XpathParser中的variables对当前节点的attributes进行设值,此处的node是Configuration ,attributeNodes.getLength()长度为0直接跳出。
parseAttributes后再对parseBody进行对当前节点的所有的CDDATA节点或者文本节点中的表达式进行赋值,设置为当前节点的body。到此configuration作为根节点XNode返回。
4.parseConfiguration
private void parseConfiguration(XNode root) { try { //issue #117 read properties first propertiesElement(root.evalNode("properties")); Properties settings = settingsAsProperties(root.evalNode("settings")); 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); } }
4.1 propertiesElement(root.evalNode("properties"));
和上面的流程一样,不同的是根节点自身持有对XPathParser的引用,随后XPathParser调用evalNode,最后由XPathParser,xpath对象调用xpath.evaluate(expression, root, returnType)方法返回原生dom对象,再对XNode进行初始化
4.1.1 获取resource或者url属性对应的外部属性文件并加加载到configuration的variables和当前XpathParser的variables中。准备给后续的表达式进行赋值,这一步需要最先进行解析,值得注意的是url和resource属性只能设置一个。
private void propertiesElement(XNode context) throws Exception { if (context != null) { Properties defaults = context.getChildrenAsProperties(); String resource = context.getStringAttribute("resource"); String url = context.getStringAttribute("url"); if (resource != null && url != null) { throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); } if (resource != null) { defaults.putAll(Resources.getResourceAsProperties(resource)); } else if (url != null) { defaults.putAll(Resources.getUrlAsProperties(url)); } Properties vars = configuration.getVariables(); if (vars != null) { defaults.putAll(vars); } parser.setVariables(defaults); configuration.setVariables(defaults); } }
4.2 解析 <setting>节点
流程和解析properties一样,可以看出来 evalNode的作用就是在构造XNode,在构造XNode的过程中,调用parseAttributes对节点attribute进行解析并放入Node的attribute属性备用,接着调用parseBody对Node节点body进行解析,如果第一次getBodyData返回不为空即是文本或者CDATA节点,如果其中包含表达式使用PropertyParser.pase解析并赋值。
解析节点之后调用settingsAsProperties方法将settings中的setting节点,name和value解析为properties对象,然后调用MetaClass对Configuration进行属性检查,如果在Configuration中没有的属性,则抛出异常。在解析的过程中第一步调用getChildren()和getChildrenAsProperties方法将所有子节点返回。然后调用MetaClass.forClass工厂方法构造指定Configuration类型的Reflector和reflectorFactory,reflectorFactory是Reflector的缓存,Reflector则包含了对getter setter field 的封装,初始化这两个对象后作为MetaClass的私有属性,MetaClass则对reflector进行进一步的封装。下面在Metaclass中对setting节点进行检查是否有对应的 setter方法
public boolean hasSetter(String name) { PropertyTokenizer prop = new PropertyTokenizer(name); if (prop.hasNext()) { if (reflector.hasSetter(prop.getName())) { MetaClass metaProp = metaClassForProperty(prop.getName()); return metaProp.hasSetter(prop.getChildren()); } else { return false; } } else { return reflector.hasSetter(prop.getName()); } }
4.3加载定义VFS
loadCustomVfs(settings);
private void loadCustomVfs(Properties props) throws ClassNotFoundException { String value = props.getProperty("vfsImpl"); if (value != null) { String[] clazzes = value.split(","); for (String clazz : clazzes) { if (!clazz.isEmpty()) { @SuppressWarnings("unchecked") Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz); configuration.setVfsImpl(vfsImpl); } } } }
//TODO 等后面用到VFS在进行添加
4.4 加载 类型别名typeAliases
typeAliasesElement(root.evalNode("typeAliases"));
扫描该节点下的子节点如果子节点名称是package则扫描这个包下面所有的类,每一个在包 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。如果不是package则默认对每个
<typeAlias alias="Author" type="domain.blog.Author"/>进行添加。
private void typeAliasesElement(XNode parent) { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { String typeAliasPackage = child.getStringAttribute("name"); configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage); } else { String alias = child.getStringAttribute("alias"); String type = child.getStringAttribute("type"); try { Class<?> clazz = Resources.classForName(type); if (alias == null) { typeAliasRegistry.registerAlias(clazz); } else { typeAliasRegistry.registerAlias(alias, clazz); } } catch (ClassNotFoundException e) { throw new BuilderException("Error registering typeAlias for '" + alias + "'. 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); } } }
ResolverUtil能根据相应的条件(某个类的超类,子类,注解等)在指定路径下查找对应满足条件的所有类加载到matches这个set中。
public class ResolverUtil<T> { /* * An instance of Log to use for logging in this class. */ private static final Log log = LogFactory.getLog(ResolverUtil.class); /** * A simple interface that specifies how to test classes to determine if they * are to be included in the results produced by the ResolverUtil. */ public interface Test { /** * Will be called repeatedly with candidate classes. Must return True if a class * is to be included in the results, false otherwise. */ boolean matches(Class<?> type); } /** * A Test that checks to see if each class is assignable to the provided class. Note * that this test will match the parent type itself if it is presented for matching. */ public static class IsA implements Test { private Class<?> parent; /** Constructs an IsA test using the supplied Class as the parent class/interface. */ public IsA(Class<?> parentType) { this.parent = parentType; } /** Returns true if type is assignable to the parent type supplied in the constructor. */ @Override public boolean matches(Class<?> type) { return type != null && parent.isAssignableFrom(type); } @Override public String toString() { return "is assignable to " + parent.getSimpleName(); } } /** * A Test that checks to see if each class is annotated with a specific annotation. If it * is, then the test returns true, otherwise false. */ public static class AnnotatedWith implements Test { private Class<? extends Annotation> annotation; /** Constructs an AnnotatedWith test for the specified annotation type. */ public AnnotatedWith(Class<? extends Annotation> annotation) { this.annotation = annotation; } /** Returns true if the type is annotated with the class provided to the constructor. */ @Override public boolean matches(Class<?> type) { return type != null && type.isAnnotationPresent(annotation); } @Override public String toString() { return "annotated with @" + annotation.getSimpleName(); } } /** The set of matches being accumulated. */ private Set<Class<? extends T>> matches = new HashSet<Class<? extends T>>(); /** * The ClassLoader to use when looking for classes. If null then the ClassLoader returned * by Thread.currentThread().getContextClassLoader() will be used. */ private ClassLoader classloader; /** * Provides access to the classes discovered so far. If no calls have been made to * any of the {@code find()} methods, this set will be empty. * * @return the set of classes that have been discovered. */ public Set<Class<? extends T>> getClasses() { return matches; } /** * Returns the classloader that will be used for scanning for classes. If no explicit * ClassLoader has been set by the calling, the context class loader will be used. * * @return the ClassLoader that will be used to scan for classes */ public ClassLoader getClassLoader() { return classloader == null ? Thread.currentThread().getContextClassLoader() : classloader; } /** * Sets an explicit ClassLoader that should be used when scanning for classes. If none * is set then the context classloader will be used. * * @param classloader a ClassLoader to use when scanning for classes */ public void setClassLoader(ClassLoader classloader) { this.classloader = classloader; } /** * Attempts to discover classes that are assignable to the type provided. In the case * that an interface is provided this method will collect implementations. In the case * of a non-interface class, subclasses will be collected. Accumulated classes can be * accessed by calling {@link #getClasses()}. * * @param parent the class of interface to find subclasses or implementations of * @param packageNames one or more package names to scan (including subpackages) for classes */ public ResolverUtil<T> findImplementations(Class<?> parent, String... packageNames) { if (packageNames == null) { return this; } Test test = new IsA(parent); for (String pkg : packageNames) { find(test, pkg); } return this; } /** * Attempts to discover classes that are annotated with the annotation. Accumulated * classes can be accessed by calling {@link #getClasses()}. * * @param annotation the annotation that should be present on matching classes * @param packageNames one or more package names to scan (including subpackages) for classes */ public ResolverUtil<T> findAnnotated(Class<? extends Annotation> annotation, String... packageNames) { if (packageNames == null) { return this; } Test test = new AnnotatedWith(annotation); for (String pkg : packageNames) { find(test, pkg); } return this; } /** * Scans for classes starting at the package provided and descending into subpackages. * Each class is offered up to the Test as it is discovered, and if the Test returns * true the class is retained. Accumulated classes can be fetched by calling * {@link #getClasses()}. * * @param test an instance of {@link Test} that will be used to filter classes * @param packageName the name of the package from which to start scanning for * classes, e.g. {@code net.sourceforge.stripes} */ public ResolverUtil<T> find(Test test, String packageName) { String path = getPackagePath(packageName); try { List<String> children = VFS.getInstance().list(path); for (String child : children) { if (child.endsWith(".class")) { addIfMatching(test, child); } } } catch (IOException ioe) { log.error("Could not read package: " + packageName, ioe); } return this; } /** * Converts a Java package name to a path that can be looked up with a call to * {@link ClassLoader#getResources(String)}. * * @param packageName The Java package name to convert to a path */ protected String getPackagePath(String packageName) { return packageName == null ? null : packageName.replace('.', '/'); } /** * Add the class designated by the fully qualified class name provided to the set of * resolved classes if and only if it is approved by the Test supplied. * * @param test the test used to determine if the class matches * @param fqn the fully qualified name of a class */ @SuppressWarnings("unchecked") protected void addIfMatching(Test test, String fqn) { try { String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.'); ClassLoader loader = getClassLoader(); if (log.isDebugEnabled()) { log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]"); } Class<?> type = loader.loadClass(externalName); if (test.matches(type)) { matches.add((Class<T>) type); } } catch (Throwable t) { log.warn("Could not examine class '" + fqn + "'" + " due to a " + t.getClass().getName() + " with message: " + t.getMessage()); } } }
ResolverUtil有一个接口Test 以及IsA、AnnotatedWith两个实现类,IsA的matches表示当前类是传入类的父亲,AnnotatedWith的matches表示当前是否标注指定注解,classloader属性是一个单独的属性,用来加载查找到后的类,没有设置则默认为
Thread.currentThread().getContextClassLoader()
public ResolverUtil<T> findImplementations(Class<?> parent, String... packageNames) public ResolverUtil<T> findAnnotated(Class<? extends Annotation> annotation, String... packageNames) public ResolverUtil<T> find(Test test, String packageName)
findImplementations和findAnnotated都会调用find进行类的加载和查找,其中find方法调用VFS查询指定包下面的所有子路径,找出class结尾的路径后调用addIfMatching方法,addIfMatching负责对类进行加载校验并添加到matches集合后,供getClasses方法返回。
4.5 加载插件
pluginElement(root.evalNode("plugins"));
注:从4.3开始不在对evalNode方法进行跟踪
首先遍历子节点,获取interceptor属性,即包名resolveClass方法最终会调用BaseBuilder的typeAliasRegistry的resolveAlias方法,这个方法会对先检查是否已经存在此别名,不存在则使用Resources进行加载。继续读取interceptor节点的property子节点,并设置到Inteceptor中,在最后添加到Configuration中的拦截器链中。
private void pluginElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { String interceptor = child.getStringAttribute("interceptor"); Properties properties = child.getChildrenAsProperties(); Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(); interceptorInstance.setProperties(properties); configuration.addInterceptor(interceptorInstance); } } }
4.6 加载objectFactory
objectFactoryElement(root.evalNode("objectFactory"));
这部分逻辑和加载plugin类似
private void objectFactoryElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); Properties properties = context.getChildrenAsProperties(); ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance(); factory.setProperties(properties); configuration.setObjectFactory(factory); } }值得注意的是如果配置了对象工厂会覆盖Configuration中默认配置的DefaultObjectFactory
4.7 加载objectWrapperFactory和上面类似
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
private void objectWrapperFactoryElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance(); configuration.setObjectWrapperFactory(factory); } }
4.8 加载reflectorFactory 反射工厂,和上面类似
reflectorFactoryElement(root.evalNode("reflectorFactory"));
private void reflectorFactoryElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance(); configuration.setReflectorFactory(factory); } }
4.9 加载settings
settingsElement(settings);
这部分就是对settings下面的子节点进行解析写入到Configuration中
4.10 加载environments,事务工厂和数据源的初始化
environmentsElement(root.evalNode("environments")); private void environmentsElement(XNode context) throws Exception { if (context != null) { if (environment == null) { environment = context.getStringAttribute("default"); } for (XNode child : context.getChildren()) { String id = child.getStringAttribute("id"); if (isSpecifiedEnvironment(id)) { TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); } } } }
4.11 解析databaseIdProvider,根据不同的数据库标识使用不同的映射语句
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
如果配置了 type=VENDOR(英文释义:厂商)当前只有这一个值,首先获取节点的properties,再构造DatabaseProvider的实例,在
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);Configuration初始化的时候就已经默认注册了此别名,前面也说道resolveClass最终会调用typeAliasRegistry的resolveAlias方法查找和创建实例,VendorDatabaseIdProvider是DatabaseIdProvider接口的唯一实例,可自定义覆盖掉configuration中的配置,VendorDatabaseIdProvider的实现很简单,传入数据源获取Connection获取connection元数据信息,如果getDatabaseProductName返回的数据和在<property>子节点定义的name一致则返回property对应的value,例如:如果配置 <property name="Oracle" value="oracle" /> ,如果 getDatabaseProductName() 返回“Oracle (DataDirect)”,databaseId 将被设置为“oracle”。
最后将configuration中的databaseId设置为这个时候获取到的"oracle"或是mysql 就可以在update,delete 等语句中进行指定。
private void databaseIdProviderElement(XNode context) throws Exception { DatabaseIdProvider databaseIdProvider = null; if (context != null) { String type = context.getStringAttribute("type"); // awful patch to keep backward compatibility if ("VENDOR".equals(type)) { type = "DB_VENDOR"; } Properties properties = context.getChildrenAsProperties(); databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance(); databaseIdProvider.setProperties(properties); } Environment environment = configuration.getEnvironment(); if (environment != null && databaseIdProvider != null) { String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource()); configuration.setDatabaseId(databaseId); } } public class VendorDatabaseIdProvider implements DatabaseIdProvider { private static final Log log = LogFactory.getLog(VendorDatabaseIdProvider.class); private Properties properties; @Override public String getDatabaseId(DataSource dataSource) { if (dataSource == null) { throw new NullPointerException("dataSource cannot be null"); } try { return getDatabaseName(dataSource); } catch (Exception e) { log.error("Could not get a databaseId from dataSource", e); } return null; } @Override public void setProperties(Properties p) { this.properties = p; } private String getDatabaseName(DataSource dataSource) throws SQLException { String productName = getDatabaseProductName(dataSource); if (this.properties != null) { for (Map.Entry<Object, Object> property : properties.entrySet()) { if (productName.contains((String) property.getKey())) { return (String) property.getValue(); } } // no match, return null return null; } return productName; } private String getDatabaseProductName(DataSource dataSource) throws SQLException { Connection con = null; try { con = dataSource.getConnection(); DatabaseMetaData metaData = con.getMetaData(); return metaData.getDatabaseProductName(); } finally { if (con != null) { try { con.close(); } catch (SQLException e) { // ignored } } } } } <select id="SelectTime" resultType="String" databaseId="mysql"> SELECT NOW() FROM dual </select> <select id="SelectTime" resultType="String" databaseId="oracle"> SELECT 'oralce'||to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') FROM dual </select>
4.12 加载typeHandlers
private void typeHandlerElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { String typeHandlerPackage = child.getStringAttribute("name"); typeHandlerRegistry.register(typeHandlerPackage); } else { String javaTypeName = child.getStringAttribute("javaType"); String jdbcTypeName = child.getStringAttribute("jdbcType"); String handlerTypeName = child.getStringAttribute("handler"); Class<?> javaTypeClass = resolveClass(javaTypeName); JdbcType jdbcType = resolveJdbcType(jdbcTypeName); Class<?> typeHandlerClass = resolveClass(handlerTypeName); if (javaTypeClass != null) { if (jdbcType == null) { typeHandlerRegistry.register(javaTypeClass, typeHandlerClass); } else { typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); } } else { typeHandlerRegistry.register(typeHandlerClass); } } } } }
如果typehandlers子节点有package获取名称属性调用
public void register(String packageName) { ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>(); resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName); Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses(); for (Class<?> type : handlerSet) { //Ignore inner classes and interfaces (including package-info.java) and abstract classes if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) { register(type); } } }
进行扫描resolverUtil上面有讲到,否则挨个的进行注册。
发表评论
-
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)-启动流程1
2018-08-23 17:10 1142概述:本文按三个部分依次循序渐进对mybatis源码-原理进行 ...
相关推荐
1. **mybatis-generator-core.jar**:这是Mybatis Generator的主要实现,包含了所有生成代码所需的核心类和资源。运行时,你需要这个JAR文件在类路径中。 2. **Mysql数据库驱动**:压缩包内预装了Mysql的JDBC驱动,...
综上所述,Spring Boot 1.2.2集成MyBatis是一个相对简单的流程,通过引入起步依赖、配置数据源和MyBatis,以及创建Mapper接口,开发者可以快速构建起数据库操作的基础设施。虽然随着版本的更新,Spring Boot的集成...
总之,"跑通的Mybatis后端源码-WhenToMint"项目提供了一个实际运行的Mybatis应用示例,通过学习和分析这个项目,你可以深入理解Mybatis的工作原理,以及如何在实际项目中使用Mybatis进行数据库操作。同时,良好的...
MyBatis Generator的主要工作原理是通过读取数据库元数据,然后根据预设的模板生成相应的Java源代码和XML配置文件。这样,开发者就可以避免手动编写重复性的代码,从而节省大量时间,提高开发效率。 **主要组件和...
在"lijie_study-mybatis-3-master.zip"这个压缩包中,包含了MyBatis 3项目的源码,这为我们深入理解MyBatis的工作原理提供了宝贵的资料。以下是关于MyBatis核心知识点的详细解析: 1. **配置文件**: - `mybatis-...
1. **Configuration**:这是MyBatis的核心配置对象,它包含了全局配置信息,如数据源、事务管理器、Mappers等。XMLConfigBuilder类负责解析`mybatis-config.xml`文件,构建Configuration实例。 2. **...
在mybatis-springboot-demo中,我们将学习如何配置Spring Boot的启动器、添加Mybatis依赖、配置Mybatis的主配置文件(mybatis-config.xml)、Mapper接口以及XML映射文件。此外,还会涉及到实体类、Service层、...
1. **添加依赖**:在项目的pom.xml文件中,我们需要引入Mybatis-Generator的Maven依赖。这确保了在构建过程中,可以调用到相应的库来执行代码生成。 2. **创建配置文件**:创建一个XML配置文件,如generatorConfig....
总结来说,深入理解MyBatis原理与实战,意味着你需要掌握其核心组件的工作机制、XML映射文件的编写、注解的应用、执行流程、缓存机制以及插件的使用。这将帮助你在实际项目中更高效、更灵活地管理数据库操作,提升...
1. **简化开发流程**:Spring 框架本身提供了丰富的功能支持,如事务管理、依赖注入等,集成 Sharding-JDBC 后可以进一步简化数据分片逻辑的实现。 2. **提高代码可维护性**:通过 Spring 的管理机制,能够更方便地...
在深入理解 MyBatis 的工作原理和体系结构之前,我们需要先回顾一下其基本工作流程。 1. **启动与配置解析**: 当 MyBatis 启动时,首要任务是解析配置文件,包括全局配置文件(mybatis-config.xml)和映射器配置...
1. 添加Mybatis和相关依赖:在`pom.xml`文件中,我们需要添加Mybatis、Mybatis-Spring-Boot-Starter以及数据库驱动的依赖。 2. 配置Mybatis:在`application.properties`或`application.yml`文件中,配置数据源(如...
在IT行业中,SpringBoot、Shiro和MyBatis-Plus是三个非常重要的开源框架,它们分别专注于应用开发的快速启动、权限管理和数据操作。在这个"springboot+shiro+mybatis-plus.7z"的压缩包中,提供了一个集成这三个框架...
在深入理解MyBatis的原理之前,我们先来看一下MyBatis的主要组成部分: 1. SQL Mapper XML 文件:这是MyBatis的核心配置文件,它包含了SQL语句的定义、参数映射和结果映射。通过XML配置,我们可以灵活地定制SQL,...
SpringBoot以其简洁的配置和快速的启动能力深受开发者喜爱,而Mybatis作为轻量级的持久层框架,提供了灵活的SQL操作,使得数据库访问更为便捷。将两者进行整合,能够构建出高效、易维护的Web应用。本篇文章将深入...
MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动...了解这一流程有助于更好地掌握MyBatis的工作原理,从而在实际开发中更加高效地使用MyBatis框架。
2. **MyBatis简介**: MyBatis是一个轻量级的ORM(对象关系映射)框架,它允许开发者编写SQL语句并与Java对象进行绑定,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis提供了动态SQL的功能,使得...
【标题】:Mybatis系列教程Mybatis架构原理共4页.pdf 【描述】:这个压缩文件包含了一份关于Mybatis架构原理的系列教程,总计四页的内容。Mybatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。...
通过"ssm-mybatis.zip"这个项目,你可以快速地学习和理解SSM框架的工作原理,以及如何在实际开发中整合和运用这三个组件。这个项目可以作为初学者的入门示例,也可以作为资深开发者快速启动新项目的模板。
本文将深入探讨MapperScannerConfigurer的工作原理、使用方法及其与MyBatis的配合。 首先,理解MapperScannerConfigurer的基本概念。MapperScannerConfigurer是MyBatis-Spring扩展中的一个类,它的主要功能是在应用...