- 浏览: 13433 次
- 性别:
- 来自: 成都
最新评论
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 699执行方式2: DeptMapper mapper ... -
MyBatis原理(2)-执行流程 3 处理结果集
2018-09-07 10:24 814DefaultResultSetHandler#handleR ... -
MyBatis原理(2)-执行流程 2
2018-08-31 17:47 483@Override public <E> L ... -
MyBatis原理(2)-执行流程 1 BoundSql生成
2018-08-31 17:09 1183MyBatis执行两种方式: 1. SqlSession ... -
MyBatis原理(1)-启动流程3- mapper 加载
2018-08-25 22:27 8201.接着上一篇文章解析mapper第一步 mapperEle ... -
MyBatis原理(1)-启动流程1
2018-08-23 17:10 1070概述:本文按三个部分依次循序渐进对mybatis源码-原理进行 ...
相关推荐
Spring+SpringMVC+Mybatis框架集成公共模块,包括公共配置、MybatisGenerator扩展插件、通用BaseService、工具类等。 > zheng-admin 基于bootstrap实现的响应式Material Design风格的通用后台管理系统,`zheng`...
7.1 启动流程(Springboot 1.50版本) 128 7.1.1 创建SpringApplication对象 129 7.1.2 运行run方法 130 7.1.3 编写事件监听机制 132 8 Spring Boot自定义starters 136 8.1 概述 136 8.2 步骤 137 9 更多Springboot...
│ Java面试题48.struts2的执行流程或者struts2的原理.mp4 │ Java面试题49.Struts2的拦截器是什么?你都用它干什么?.mp4 │ Java面试题50.Spring MVC的执行流程.mp4 │ Java面试题51.SpringMVC和Struts2的不同.mp4...
◦定时任务管理 通过配置以实现某时刻重复执行的系统任务,如配置每月最后一天进行库存清算任务,并且启动库存清算审批流程。 ◦系统日志管理 记录进入系统中的每个用户访问的每个功能 ◦数据源管理 可以设置多种...
部署说明:本资源提供了详细的部署说明,包括如何配置数据库连接、如何启动项目等。此外,还介绍了如何在不同操作系统上进行部署,以满足不同开发者的需求。演示视频:本资源附带了一个演示视频,展示了系统的运行...
这是一个基于SSM(Spring、SpringMVC、MyBatis)框架和Vue前端技术栈的小学生课外知识学习网站的源码资源包。该资源包包含了完整的项目源码、部署说明、演示视频以及源码介绍,旨在帮助用户快速搭建和运行一个功能...
《Spring3.x企业应用开发实战》是在《精通Spring2.x——企业应用开发详解》的基础上,经过历时一年的重大调整改版而成的,本书延续了上一版本追求深度,注重原理,不停留在技术表面的写作风格,力求使读者在熟练...
《Spring3.x企业应用开发实战》是在《精通Spring2.x——企业应用开发详解》的基础上,经过历时一年的重大调整改版而成的,本书延续了上一版本追求深度,注重原理,不停留在技术表面的写作风格,力求使读者在熟练...
启动配置..................................................................................................................................... 9 配置空间...................................................
│ 第48节:VCL的子程序和Request流程.avi │ 第49节:VCL的变量和常见的应用片断.avi │ 第50节:使用CLI来管理Varnish.avi │ 第51节:Varnishd命令和运行期参数.avi │ 第52节:Varnish的日志操作.avi │ 第53节...
1. 目录 1. 2. 目录 .........................................................................................................................................................1 JVM ........................