首先我们从 builder 这个类入手,首先我们注意到 BaseBuilder, 其实它的本质上市一个抽象类,它从本质上抽象出了 Builder 的一切,我猜想这里一定使用了建造者模式,但是这个抽象类里面居然没有抽象方法!
其中 XXXValueOf 方法,其实是把 String 字符串转换成了相对应的类型,如下代码。
- protected Boolean booleanValueOf(String value, Boolean defaultValue) {
- return value == null ? defaultValue: Boolean.valueOf(value);
- }
- protected Integer integerValueOf(String value, Integer defaultValue) {
- return value == null ? defaultValue: Integer.valueOf(value);
- }
- protected Set < String > stringSetValueOf(String value, String defaultValue) {
- value = (value == null ? defaultValue: value);
- return new HashSet < String > (Arrays.asList(value.split(",")));
- }
其中 resoveXXXType 的目的就是把 string 转换成相对应的类型。
- protected JdbcType resolveJdbcType(String alias) {
- if (alias == null) {
- return null;
- }
- try {
- return JdbcType.valueOf(alias);
- } catch(IllegalArgumentException e) {
- throw new BuilderException("Error resolving JdbcType. Cause: " + e, e);
- }
- }
- protected ResultSetType resolveResultSetType(String alias) {
- if (alias == null) {
- return null;
- }
- try {
- return ResultSetType.valueOf(alias);
- } catch(IllegalArgumentException e) {
- throw new BuilderException("Error resolving ResultSetType. Cause: " + e, e);
- }
- }
- protected ParameterMode resolveParameterMode(String alias) {
- if (alias == null) {
- return null;
- }
- try {
- return ParameterMode.valueOf(alias);
- } catch(IllegalArgumentException e) {
- throw new BuilderException("Error resolving ParameterMode. Cause: " + e, e);
- }
- }
下面的方法是通过字符串别名解析出相对应的类型,再从类型创建实例。
- protected Object createInstance(String alias) {
- Class < ?>clazz = resolveClass(alias);
- if (clazz == null) {
- return null;
- }
- try {
- return resolveClass(alias).newInstance();
- } catch(Exception e) {
- throw new BuilderException("Error creating instance. Cause: " + e, e);
- }
- }
- protected Class < ?>resolveClass(String alias) {
- if (alias == null) {
- return null;
- }
- try {
- return resolveAlias(alias);
- } catch(Exception e) {
- throw new BuilderException("Error resolving class. Cause: " + e, e);
- }
- }
注意下面的,是 2 个不同的重载类,是第一个调用第二个。首先得到相对应的 TypeHanlder 类型,如果该 TypeHanlder 在 typeHanlderRegisty 注册中心有留存,那么返回,否则从 javatype 里创建一个新的。
- protected TypeHandler < ?>resolveTypeHandler(Class < ?>javaType, String typeHandlerAlias) {
- if (typeHandlerAlias == null) {
- return null;
- }
- Class < ?>type = resolveClass(typeHandlerAlias);
- if (type != null && !TypeHandler.class.isAssignableFrom(type)) {
- throw new BuilderException("Type " + type.getName() + " is not a valid TypeHandler because it does not implement TypeHandler interface");
- }@SuppressWarnings("unchecked") // already verified it is a TypeHandler
- Class < ?extends TypeHandler < ?>>typeHandlerType = (Class < ?extends TypeHandler < ?>>) type;
- return resolveTypeHandler(javaType, typeHandlerType);
- }
- protected TypeHandler < ?>resolveTypeHandler(Class < ?>javaType, Class < ?extends TypeHandler < ?>>typeHandlerType) {
- if (typeHandlerType == null) {
- return null;
- }
- // javaType ignored for injected handlers see issue #746 for full detail
- TypeHandler < ?>handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
- if (handler == null) {
- // not in registry, create a new one
- handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);
- }
- return handler;
- }
其中 MapperBuilderAssistant 在此包下面,并且继承了 BaseBuilder, 下面对此类做一个解析,比如下面的就是解析命名空间的,就是包名。
- public String applyCurrentNamespace(String base, boolean isReference) {
- if (base == null) {
- return null;
- }
- if (isReference) {
- // is it qualified with any namespace yet?
- if (base.contains(".")) {
- return base;
- }
- } else {
- // is it qualified with this namespace yet?
- if (base.startsWith(currentNamespace + ".")) {
- return base;
- }
- if (base.contains(".")) {
- throw new BuilderException("Dots are not allowed in element names, please remove it from " + base);
- }
- }
- return currentNamespace + "." + base;
- }
下面的代码主要是用 namespace 得到 cache 的一个实例,就这么理解。
- public Cache useCacheRef(String namespace) {
- if (namespace == null) {
- throw new BuilderException("cache-ref element requires a namespace attribute.");
- }
- try {
- unresolvedCacheRef = true;
- Cache cache = configuration.getCache(namespace);
- if (cache == null) {
- throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
- }
- currentCache = cache;
- unresolvedCacheRef = false;
- return cache;
- } catch(IllegalArgumentException e) {
- throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
- }
- }
下面的方法是创建 cache。
- public Cache useNewCache(Class<? extends Cache> typeClass,
- Class<? extends Cache> evictionClass,
- Long flushInterval,
- Integer size,
- boolean readWrite,
- boolean blocking,
- Properties props) {
- Cache cache = new CacheBuilder(currentNamespace)
- .implementation(valueOrDefault(typeClass, PerpetualCache.class))
- .addDecorator(valueOrDefault(evictionClass, LruCache.class))
- .clearInterval(flushInterval)
- .size(size)
- .readWrite(readWrite)
- .blocking(blocking)
- .properties(props)
- .build();
- configuration.addCache(cache);
- currentCache = cache;
- return cache;
- }
其中有一个地方要弄清楚,就是 Class<? extends Cache> typeClass, Class<? extends Cache> evictionClass 的区别在哪里?在哪里呢?请看下图,一个是实现,一个是装饰者,你可以暂时理解为作用不同,就这么简单。
下面是 addParameterMap 方法的一些介绍。
- public ParameterMap addParameterMap(String id, Class < ?>parameterClass, List < ParameterMapping > parameterMappings) {
- //得到包名。
- id = applyCurrentNamespace(id, false);
- //工厂方法创建参数Map,并添加到configuration中去。
- ParameterMap parameterMap = new ParameterMap.Builder(configuration, id, parameterClass, parameterMappings).build();
- configuration.addParameterMap(parameterMap);
- return parameterMap;
- }
下面是 buildParameterMap 的介绍,其实它也是利用了工厂方法骑构造。
- public ParameterMapping buildParameterMapping(
- Class<?> parameterType,
- String property,
- Class<?> javaType,
- JdbcType jdbcType,
- String resultMap,
- ParameterMode parameterMode,
- Class<? extends TypeHandler<?>> typeHandler,
- Integer numericScale) {
- resultMap = applyCurrentNamespace(resultMap, true);
- // Class parameterType = parameterMapBuilder.type();
- Class<?> javaTypeClass = resolveParameterJavaType(parameterType, property, javaType, jdbcType);
- TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);
- return new ParameterMapping.Builder(configuration, property, javaTypeClass)
- .jdbcType(jdbcType)
- .resultMapId(resultMap)
- .mode(parameterMode)
- .numericScale(numericScale)
- .typeHandler(typeHandlerInstance)
- .build();
- }
下面的是建立一个结果集,然后把结果集添加到 configuration 里面。
- public ResultMap addResultMap(
- String id,
- Class<?> type,
- String extend,
- Discriminator discriminator,
- List<ResultMapping> resultMappings,
- Boolean autoMapping) {
- id = applyCurrentNamespace(id, false);
- extend = applyCurrentNamespace(extend, true);
- if (extend != null) {
- if (!configuration.hasResultMap(extend)) {
- throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
- }
- ResultMap resultMap = configuration.getResultMap(extend);
- List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings());
- extendedResultMappings.removeAll(resultMappings);
- // Remove parent constructor if this resultMap declares a constructor.
- boolean declaresConstructor = false;
- for (ResultMapping resultMapping : resultMappings) {
- if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
- declaresConstructor = true;
- break;
- }
- }
- if (declaresConstructor) {
- Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator();
- while (extendedResultMappingsIter.hasNext()) {
- if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) {
- extendedResultMappingsIter.remove();
- }
- }
- }
- resultMappings.addAll(extendedResultMappings);
- }
- ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
- .discriminator(discriminator)
- .build();
- configuration.addResultMap(resultMap);
- return resultMap;
- }
当然,下面的也太多了,就不一一介绍了,还有一些这些结果集的一些 Getter 方法;有兴趣的可以自己去看看,不过我们从这里得到了一个很重要的东西,那就是贯穿上下文的一个东西:Configuration!, 这个东西可以说是无处不在,不管是在基类,还是在派生类中。
我们还看到了一些工厂的 Relover,那这些 resover 类其实也是调用了上面的一些 public 方法而已,没啥特别的,真的。
下面我们再看看 SqlSourceBuilder 这个类,这个先从 string 解析成 map, 然后再判断是否是 sql 类型,如果是,继续解析。
- private ParameterMapping buildParameterMapping(String content) {
- Map < String,
- String > propertiesMap = parseParameterMapping(content);
- String property = propertiesMap.get("property");
- Class < ?>propertyType;
- if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params
- propertyType = metaParameters.getGetterType(property);
- } else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
- propertyType = parameterType;
- } else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
- propertyType = java.sql.ResultSet.class;
- } else if (property == null || Map.class.isAssignableFrom(parameterType)) {
- propertyType = Object.class;
- } else {
- MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
- if (metaClass.hasGetter(property)) {
- propertyType = metaClass.getGetterType(property);
- } else {
- propertyType = Object.class;
- }
- }
- ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
- Class < ?>javaType = propertyType;
- String typeHandlerAlias = null;
- for (Map.Entry < String, String > entry: propertiesMap.entrySet()) {
- String name = entry.getKey();
- String value = entry.getValue();
- if ("javaType".equals(name)) {
- javaType = resolveClass(value);
- builder.javaType(javaType);
- } else if ("jdbcType".equals(name)) {
- builder.jdbcType(resolveJdbcType(value));
- } else if ("mode".equals(name)) {
- builder.mode(resolveParameterMode(value));
- } else if ("numericScale".equals(name)) {
- builder.numericScale(Integer.valueOf(value));
- } else if ("resultMap".equals(name)) {
- builder.resultMapId(value);
- } else if ("typeHandler".equals(name)) {
- typeHandlerAlias = value;
- } else if ("jdbcTypeName".equals(name)) {
- builder.jdbcTypeName(value);
- } else if ("property".equals(name)) {
- // Do Nothing
- } else if ("expression".equals(name)) {
- throw new BuilderException("Expression based parameters are not supported yet");
- } else {
- throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}. Valid properties are " + parameterProperties);
- }
- }
- if (typeHandlerAlias != null) {
- builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
- }
- return builder.build();
- }
其中比较重要的就是下面的代码,下面做一个分析,首先会得到 typeHanldler, 然后再在 buidler 里对这个进行注册。
- if (typeHandlerAlias != null) {
- builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
- }
- return builder.build();
我们关键是看看 builder.build 方法,它是一个 private 的方法,它的作用就是 get 到我们开始设置的值,下面的 validate 方法也是做一些基础验证的,具体的可以略过,没啥价值。
- private void resolveTypeHandler() {
- if (parameterMapping.typeHandler == null && parameterMapping.javaType != null) {
- Configuration configuration = parameterMapping.configuration;
- TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
- parameterMapping.typeHandler = typeHandlerRegistry.getTypeHandler(parameterMapping.javaType, parameterMapping.jdbcType);
- }
- }
下面我们再来看看这个类: StaticSqlSource 其实我觉得这个玩意没啥用!真的不是贬低写 mybatis 的人,真没看出有什么用,具体看下面的代码。
- public class StaticSqlSource implements SqlSource {
- private final String sql;
- private final List < ParameterMapping > parameterMappings;
- private final Configuration configuration;
- public StaticSqlSource(Configuration configuration, String sql) {
- this(configuration, sql, null);
- }
- public StaticSqlSource(Configuration configuration, String sql, List < ParameterMapping > parameterMappings) {
- this.sql = sql;
- this.parameterMappings = parameterMappings;
- this.configuration = configuration;
- }
- @Override public BoundSql getBoundSql(Object parameterObject) {
- return new BoundSql(configuration, sql, parameterMappings, parameterObject);
- }
这些类同级的包里面,还有一个 XML 的包,里面包含的 DTD 文件,以及一些工具类,大家理解这些东西,其实就是为了把烦人的 XML 转换成一个可用的 configuration 对象的的工具类就行了,真的没必要深究。
关于 builder 的 annotation 大家应该不陌生了吧?我介绍了这么多。构造函数说得很清楚了,其实把一些基本的注解加进了,CRUD 而已。
- public MapperAnnotationBuilder(Configuration configuration, Class < ?>type) {
- String resource = type.getName().replace('.', '/') + ".java (best guess)";
- this.assistant = new MapperBuilderAssistant(configuration, resource);
- this.configuration = configuration;
- this.type = type;
- sqlAnnotationTypes.add(Select.class);
- sqlAnnotationTypes.add(Insert.class);
- sqlAnnotationTypes.add(Update.class);
- sqlAnnotationTypes.add(Delete.class);
- sqlProviderAnnotationTypes.add(SelectProvider.class);
- sqlProviderAnnotationTypes.add(InsertProvider.class);
- sqlProviderAnnotationTypes.add(UpdateProvider.class);
- sqlProviderAnnotationTypes.add(DeleteProvider.class);
- }
有一个核心方法,比较重要: parse, 作用很明显,就是转换呗,然后是从 configuration 拿玩意,然后转换成有用的东西。其实这个不就是我们写的 mapper 类的 XML 文件吗?!用过 mybatis 的人都知道的。注释写了一点,不过更深入了,我觉得没必要写了,靠大家自己去发掘。
- public void parse() {
- String resource = type.toString();
- if (!configuration.isResourceLoaded(resource)) {
- //载入XML资源文件。
- loadXmlResource();
- //把资源文件添加到configuation里面。
- configuration.addLoadedResource(resource);
- assistant.setCurrentNamespace(type.getName());
- //修改assistant变量
- parseCache();
- //修改assistant变量2
- parseCacheRef();
- Method[] methods = type.getMethods();
- for (Method method: methods) {
- try {
- // issue #237
- if (!method.isBridge()) {
- parseStatement(method);
- }
- } catch(IncompleteElementException e) {
- configuration.addIncompleteMethod(new MethodResolver(this, method));
- }
- }
- }
- parsePendingMethods();
- }
来源: http://www.cnblogs.com/kmsfan/p/8047878.html