17、通用枚举类型处理(二)

问题

要是我们一个项目特别大,有几十个甚至是上百个枚举呢,那怎么办,难道我要一个个在typeHandler里面去加,加一百几十行?!个人觉得非常麻烦,影响我们的开发速度,so,能不能实现我们想要的像扫描下枚举的所在的包目录就可以注册的枚举做自动转换呢?具体怎么实现?

typeHandler的注册流程

具体过程参考15

切入点

Cosnfiguration对象得到typeHandlerRegistry管理器,入手的方法就是typeHandlerElement方法,在里面我们添加多一个包扫描功能,为万能转换处理器注册所指定的包下面所有的枚举

方案一

SqlSessionFactoryBean

由于我们项目使用了Spring, 是用Spring集成的Mybatis(废话,大家都是这么干的)。Spring通过SqlSessionFactoryBean来初始化启动Mybatis。 因此,我们应该在它身上下手,然而,一切并不是那么顺利。

查看了一下SqlSessionFactoryBean的源码,发现SqlSessionFactoryBean并没有任何地方可以让我们切入, 进而来调用TypeHandlerRegistry进行注册我们的枚举。 更令人蛋疼的是其所有属性全是private, 这下不仅AOP切入不行,连通过继承偷懒都不行了。
只有老老实实的重写一遍SqlSessionFactoryBean的代码了

DefaultSqlSessionFactoryBean

  • DefaultSqlSessionFactoryBean继承SqlSessionFactoryBean。
  • 将SqlSessionFactoryBean中的代码全部copy到DefaultSqlSessionFactoryBean。
  • 调用以下方法。
package com.lf.sqlSessionFactoryBean;

import com.lf.BaseEnum;
import com.lf.typehandle.BaseEnumTypeHandle;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.io.ResolverUtil;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Set;

/**
 * Created by LF on 2017/6/12.
 */
public class DefaultSqlSessionFactoryBean extends SqlSessionFactoryBean {
    protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

        Configuration configuration;

        XMLConfigBuilder xmlConfigBuilder = null;
        if (this.configuration != null) {
            configuration = this.configuration;
            if (configuration.getVariables() == null) {
                configuration.setVariables(this.configurationProperties);
            } else if (this.configurationProperties != null) {
                configuration.getVariables().putAll(this.configurationProperties);
            }
        } else if (this.configLocation != null) {
            xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
            configuration = xmlConfigBuilder.getConfiguration();
        } else {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
            }
            configuration = new Configuration();
            if (this.configurationProperties != null) {
                configuration.setVariables(this.configurationProperties);
            }
        }

        if (this.objectFactory != null) {
            configuration.setObjectFactory(this.objectFactory);
        }

        if (this.objectWrapperFactory != null) {
            configuration.setObjectWrapperFactory(this.objectWrapperFactory);
        }

        if (this.vfs != null) {
            configuration.setVfsImpl(this.vfs);
        }

        if (hasLength(this.typeAliasesPackage)) {
            String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
                    ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
            for (String packageToScan : typeAliasPackageArray) {
                configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                        typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
                }
            }
        }

        if (!isEmpty(this.typeAliases)) {
            for (Class typeAlias : this.typeAliases) {
                configuration.getTypeAliasRegistry().registerAlias(typeAlias);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Registered type alias: '" + typeAlias + "'");
                }
            }
        }

        if (!isEmpty(this.plugins)) {
            for (Interceptor plugin : this.plugins) {
                configuration.addInterceptor(plugin);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Registered plugin: '" + plugin + "'");
                }
            }
        }

        if (hasLength(this.typeHandlersPackage)) {
            String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
                    ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
            for (String packageToScan : typeHandlersPackageArray) {
                configuration.getTypeHandlerRegistry().register(packageToScan);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
                }
            }
        }

        if (!isEmpty(this.typeHandlers)) {
            for (TypeHandler typeHandler : this.typeHandlers) {
                configuration.getTypeHandlerRegistry().register(typeHandler);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Registered type handler: '" + typeHandler + "'");
                }
            }
        }

        if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
            try {
                configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
            } catch (SQLException e) {
                throw new NestedIOException("Failed getting a databaseId", e);
            }
        }

        if (this.cache != null) {
            configuration.addCache(this.cache);
        }

        if (xmlConfigBuilder != null) {
            try {
                xmlConfigBuilder.parse();

                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
                }
            } catch (Exception ex) {
                throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
            } finally {
                ErrorContext.instance().reset();
            }
        }

        if (this.transactionFactory == null) {
            this.transactionFactory = new SpringManagedTransactionFactory();
        }

        configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));

        if (!isEmpty(this.mapperLocations)) {
            for (Resource mapperLocation : this.mapperLocations) {
                if (mapperLocation == null) {
                    continue;
                }

                try {
                    XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                            configuration, mapperLocation.toString(), configuration.getSqlFragments());
                    TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
                    //重点
                    regist(typeHandlerRegistry, BaseEnumTypeHandle.class, "com.lf,dist");
                    xmlMapperBuilder.parse();
                } catch (Exception e) {
                    throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
                } finally {
                    ErrorContext.instance().reset();
                }

                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
                }
            }
        } else {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
            }
        }

        return this.sqlSessionFactoryBuilder.build(configuration);
    }

    private void regist(TypeHandlerRegistry typeHandlerRegistry, Class typeHandlerClass, String... packageNames) {
        ResolverUtil> resolverUtil = new ResolverUtil>();
        resolverUtil.findImplementations(BaseEnum.class, packageNames);
        Set>> mTypes = resolverUtil.getClasses();
        for (Class javaTypeClass : mTypes) {
            typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
        }
    }

}

方案二

没有使用spring的时候
其他的配置和正常使用的时候一样

package com.lf;

import com.lf.typehandle.BaseEnumTypeHandle;
import org.apache.ibatis.io.ResolverUtil;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.type.TypeHandlerRegistry;

import java.io.IOException;
import java.io.InputStream;
import java.util.Set;

public class App {
    public static void main(String[] args) throws IOException {
        String resouce = "mybatis-config.xml";
        InputStream is = Resources.getResourceAsStream(resouce);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        TypeHandlerRegistry typeHandlerRegistry = sqlSessionFactory.getConfiguration().getTypeHandlerRegistry();
        regist(typeHandlerRegistry, BaseEnumTypeHandle.class,"com.lf.dist");
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Object one = sqlSession.selectOne("com.lf.dao.BlogPostMapper.selectByPrimaryKey", "1");
        System.err.println(one);
        sqlSession.commit();
    }
    private static void regist(TypeHandlerRegistry typeHandlerRegistry, Class typeHandlerClass, String... packageNames) {
        ResolverUtil> resolverUtil = new ResolverUtil>();
        resolverUtil.findImplementations(BaseEnum.class, packageNames);
        Set>> mTypes = resolverUtil.getClasses();
        for (Class javaTypeClass : mTypes) {
            typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
        }
    }
}

方案三

修改mybatis的源码
修改注解MappedTypes,添加扫描包的功能


package org.apache.ibatis.type;

import java.lang.annotation.*;

/**
 * @author Eduardo Macarron
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MappedTypes {

    Class[] value() default {};

    String[] basePackage() default {};
}

修改register源码


    // Only handler type

    public void register(Class typeHandlerClass) {
        boolean mappedTypeFound = false;
        MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
        if (mappedTypes != null) {
            for(String basePackage:mappedTypes.basePackage()){
                ResolverUtil> resolverUtil = new ResolverUtil>();
                resolverUtil.find(new ResolverUtil.IsA(BaseEnum.class), basePackage);
                Set>> mTypes = resolverUtil.getClasses();
                for (Class javaTypeClass : mTypes) {
                    register(javaTypeClass, typeHandlerClass);
                    mappedTypeFound = true;
                }
            }

            for (Class javaTypeClass : mappedTypes.value()) {
                register(javaTypeClass, typeHandlerClass);
                mappedTypeFound = true;
            }
        }
        if (!mappedTypeFound) {
            register(getInstance(null, typeHandlerClass));
        }
    }

配置

    <typeAliases>
        <package name="com.lf.entity"/>
    typeAliases>

你可能感兴趣的:(Mybatis源码解析)