zoukankan      html  css  js  c++  java
  • 手写开源ORM框架介绍

    手写开源ORM框架介绍

    简介

    前段时间利用空闲时间,参照mybatis的基本思路手写了一个ORM框架。一直没有时间去补充相应的文档,现在正好抽时间去整理下。通过思路历程和代码注释,一方面重温下知识,另一方面准备后期去完善这个框架。

    传统JDBC连接

    参照传统的JDBC连接数据库过程如下,框架所做的事情就是把这些步骤进行封装。

    // 1. 注册 JDBC 驱动
    Class.forName(JDBC_DRIVER);
    // 2. 打开链接
    conn = DriverManager.getConnection(DB_URL,USER,PASS);
    // 3. 实例化statement
    stmt = conn.prepareStatement(sql);
    // 4. 填充数据
    stmt.setString(1,id);
    // 5. 执行Sql连接
    ResultSet rs = stmt.executeQuery();
    // 6. 结果查询
    while(rs.next()){
        // 7. 通过字段检索
        int id  = rs.getInt("id");
        String name = rs.getString("name");
        String url = rs.getString("url");
    }

    思路历程

    上述是执行Java连接数据库通用语句,ORM框架就是对象关系映射框架。我们需要做的重点是步骤4和7,将对象字段填充至数据库,将数据库的内容取出作为对象返回。了解了基本的方法后,开始动手写一个框架了。

    数据库驱动

    开始,需要构建一个驱动注册类,这个类主要用来对数据库驱动进行构造。详见代码块1

    代码块1

    数据库驱动类(com.simple.ibatis.dirver)

    /**
     * @Author  xiabing
     * @Desc    驱动注册中心,对数据库驱动进行注册的地方
     **/
    public class DriverRegister {
    
        /*
         * mysql的driver类
         * */
        private static final String MYSQLDRIVER = "com.mysql.jdbc.Driver";
    
        /*
         * 构建driver缓存,存储已经注册了的driver的类型
         * */
        private static final Map<String,Driver> registerDrivers = new ConcurrentHashMap<>();
    
        /*
        *   初始化,此处是将DriverManager中已经注册了驱动放入自己缓存中。当然,你也可以在这个方法内注册
            常见的数据库驱动,这样后续就可以直接使用,不用自己手动注册了。
        * */
        static {
            Enumeration<Driver> driverEnumeration = DriverManager.getDrivers();
            while (driverEnumeration.hasMoreElements()){
                Driver driver = driverEnumeration.nextElement();
                registerDrivers.put(driver.getClass().getName(),driver);
            }
        }
    
        /*
         *  加载mysql驱动,此个方法可以写在静态代码块内,代表项目启动时即注册mysql驱动
         * */
        public void loadMySql(){
           if(! registerDrivers.containsKey(MYSQLDRIVER)){
               loadDriver(MYSQLDRIVER);
           }
        }
    
        /*
         *  加载数据库驱动通用方法,并注册到registerDrivers缓存中,此处是注册数据库驱动的方法
         * */
        public void loadDriver(String driverName){
    
            Class<?> driverType;
            try {
                // 注册驱动,返回驱动类
                driverType = Class.forName(driverName);
                // 将驱动实例放入驱动缓存中
                registerDrivers.put(driverType.getName(),(Driver)driverType.newInstance());
            }catch (ClassNotFoundException e){
                throw new RuntimeException(e);
            }catch (Exception e){
                throw new RuntimeException(e);
            }
        }
    }

    驱动注册后,需要建立数据库连接。但是框架如果一个请求建一个连接,那木势必耗费大量的资源。所以该框架引入池化的概念,对连接进行池化管理。详情见代码2

    代码块2

    数据源类(com.simple.ibatis.datasource)

    PoolConnection

    /**
     * @Author  xiabing
     * @Desc    连接代理类,是对Connection的一个封装。除了提供基本的连接外,还想记录
                这个连接的连接时间,因为有的连接如果一直连接不释放,那木我可以通过查看
                这个连接已连接的时间,如果超时了,我可以主动释放。
     **/
    public class PoolConnection {
        
        // 真实的数据库连接
        public Connection connection;
        
        // 数据开始连接时间
        private Long CheckOutTime;
        
        // 连接的hashCode
        private int hashCode = 0;
    
        public PoolConnection(Connection connection) {
            this.connection = connection;
            this.hashCode = connection.hashCode();
        }
    
        public Long getCheckOutTime() {
            return CheckOutTime;
        }
    
        public void setCheckOutTime(Long checkOutTime) {
            CheckOutTime = checkOutTime;
        }
        
        // 判断两个PoolConnection对象是否相等,其实是判断其中真实连接的hashCode是否相等
        @Override
        public boolean equals(Object obj) {
            if(obj instanceof  PoolConnection){
                return connection.hashCode() ==
                        ((PoolConnection) obj).connection.hashCode();
            }else if(obj instanceof Connection){
                return obj.hashCode() == hashCode;
            }else {
                return false;
            }
        }
    
        @Override
        public int hashCode() {
            return hashCode;
        }
    }

    NormalDataSource

    /**
     * @Author  xiabing
     * @Desc    普通数据源,这个数据源是用来产生数据库连接的。来一个请求就会建立一个数据库连接,没有池化的概念
     **/
    public class NormalDataSource implements DataSource{
        
        // 驱动名称
        private String driverClassName;
        
        // 数据库连接URL
        private String url;
        
        // 数据库用户名
        private String userName;
        
        // 数据库密码
        private String passWord;
    
        // 驱动注册中心
        private DriverRegister driverRegister = new DriverRegister();
    
        public NormalDataSource(String driverClassName, String url, String userName, String passWord) {
            // 初始化时将驱动进行注册
            this.driverRegister.loadDriver(driverClassName);
            this.driverClassName = driverClassName;
            this.url = url;
            this.userName = userName;
            this.passWord = passWord;
        }
        
        // 获取数据库连接
        @Override
        public Connection getConnection() throws SQLException {
            return DriverManager.getConnection(url,userName,passWord);
    
        }
        
        // 移除数据库连接,此方法没有真正移除,只是将连接中未提交的事务进行回滚操作。
        public void removeConnection(Connection connection) throws SQLException{
            if(!connection.getAutoCommit()){
                connection.rollback();
            }
        }
        
        // 获取数据库连接
        @Override
        public Connection getConnection(String username, String password) throws SQLException {
            return DriverManager.getConnection(url,username,password);
        }
        
        // 后续方法因为没有用到,所有没有进行重写了
        @Override
        public <T> T unwrap(Class<T> iface) throws SQLException {
            return null;
        }
    
        @Override
        public boolean isWrapperFor(Class<?> iface) throws SQLException {
            return false;
        }
    
        @Override
        public PrintWriter getLogWriter() throws SQLException {
            return null;
        }
    
        @Override
        public void setLogWriter(PrintWriter out) throws SQLException {
    
        }
    
        @Override
        public void setLoginTimeout(int seconds) throws SQLException {
    
        }
    
        @Override
        public int getLoginTimeout() throws SQLException {
            return 0;
        }
    
        @Override
        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
            return null;
        }
    
    }

    PoolDataSource

    /**
     * @Author  xiabing
     * @Desc    池化线程池,对连接进行管理
     **/
    public class PoolDataSource implements DataSource{
    
        private Integer maxActiveConnectCount = 10; // 最大活跃线程数,此处可根据实际情况自行设置
    
        private Integer maxIdleConnectCount = 10; // 最大空闲线程数,此处可根据实际情况自行设置
    
        private Long maxConnectTime = 30*1000L; // 连接最长使用时间,在自定义连接中配置了连接开始时间,在这里来判断该连接是否超时
    
        private Integer waitTime = 2000; // 线程wait等待时间
    
        private NormalDataSource normalDataSource; // 使用normalDataSource来产生连接
    
        private Queue<PoolConnection> activeConList = new LinkedList<>(); // 存放活跃连接的队列
    
        private Queue<PoolConnection> idleConList = new LinkedList<>(); // 存放空闲连接的队列
    
        public PoolDataSource(String driverClassName, String url, String userName, String passWord) {
    
            this(driverClassName,url,userName,passWord,10,10);
    
        }
    
        public PoolDataSource(String driverClassName, String url, String userName, String passWord,Integer maxActiveConnectCount,Integer maxIdleConnectCount) {
            // 初始化normalDataSource,因为normalDataSource已经封装了新建连接的方法
            this.normalDataSource = new NormalDataSource(driverClassName,url,userName,passWord);
            this.maxActiveConnectCount = maxActiveConnectCount;
            this.maxIdleConnectCount = maxIdleConnectCount;
    
        }
        /**
         * @Desc 获取连接时先从空闲连接列表中获取。若没有,则判断现在活跃连接是否已超过设置的最大活跃连接数,没超过,new一个
         *        若超过,则判断第一个连接是否已超时,若超时,则移除掉在新建。若未超时,则wait()等待。
         **/
        @Override
        public Connection getConnection(){
            Connection connection = null;
            try {
                connection =  doGetPoolConnection().connection;
            }catch (SQLException e){
                throw new RuntimeException(e);
            }
            return connection;
        }
    
        public void removeConnection(Connection connection){
            PoolConnection poolConnection = new PoolConnection(connection);
            doRemovePoolConnection(poolConnection);
        }
        
        private PoolConnection doGetPoolConnection() throws SQLException{
            PoolConnection connection = null;
            while (connection == null){
                // 加锁
                synchronized (this){
                    // 判断是否有空闲连接
                    if(idleConList.size() < 1){ 
                        // 判断活跃连接数是否已经超过预设的最大活跃连接数
                        if(activeConList.size() < maxActiveConnectCount){
                            // 如果还可以新建连接,就新建一个连接
                            connection = new PoolConnection(normalDataSource.getConnection());
                        }else {
                            // 走到这一步,说明没有空闲连接,并且活跃连接已经满了,则只能看哪些连接超时,判断第一个连接是否超时
                            PoolConnection poolConnection = activeConList.peek();
                            // 这就是我为啥要给数据库连接加连接时间了
                            if(System.currentTimeMillis() - poolConnection.getCheckOutTime() > maxConnectTime){
                                // 若第一个连接已经超时了,移除第一个活跃连接
                                PoolConnection timeOutConnect = activeConList.poll();
                                if(!timeOutConnect.connection.getAutoCommit()){
                                    // 如果该连接设的是非自动提交,则对事物进行回滚操作
                                    timeOutConnect.connection.rollback();
                                }
                                // 置为空,让垃圾收集器去收集
                                timeOutConnect.connection.close();
                                timeOutConnect = null;
                                // 新建一个连接
                                connection = new PoolConnection(normalDataSource.getConnection());
                            }else {
                                // 走到这一步,代表所有连接都没有超时,只能等了
                                try{
                                    // 于是等一会
                                    this.wait(waitTime);
                                }catch (InterruptedException e){
                                    // ignore错误,并退出
                                    break;  
                                }
                            }
                        }
                    }else {
                        // 这一步代表空闲连接队列里面有连接,则直接取出
                        connection = idleConList.poll();
                    }
                    if(connection != null){
                        // 设置这个连接的连接时间
                        connection.setCheckOutTime(System.currentTimeMillis());
                        // 然后放入活跃连接队列中
                        activeConList.add(connection);
                    }
                }
            }
    
            return connection;
        }
        
        // 移除连接
        private void doRemovePoolConnection(PoolConnection connection){
            // 加锁
            synchronized (this){
                // 移除连接,这里需要重写PoolConnection的equal方法,因为只要真实连接相同,就可以代表这个封装的连接相等了
                activeConList.remove(connection);
                if(idleConList.size() < maxIdleConnectCount){
                    // 加入空闲连接队列中
                    idleConList.add(connection);
                }
                // 通知其他等待线程,让他们继续获取连接
                this.notifyAll();
            }
        }
    
        @Override
        public Connection getConnection(String username, String password) throws SQLException {
            return getConnection();
        }
        
        // 后面的方法没有用到就没有重写了
        @Override
        public <T> T unwrap(Class<T> iface) throws SQLException {
            return null;
        }
    
        @Override
        public boolean isWrapperFor(Class<?> iface) throws SQLException {
            return false;
        }
    
        @Override
        public PrintWriter getLogWriter() throws SQLException {
            return null;
        }
    
        @Override
        public void setLogWriter(PrintWriter out) throws SQLException {
    
        }
    
        @Override
        public void setLoginTimeout(int seconds) throws SQLException {
    
        }
    
        @Override
        public int getLoginTimeout() throws SQLException {
            return 0;
        }
    
        @Override
        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
            return null;
        }
    }

    使用数据库连接池获取连接后,接下来就是关键了,生成SQL预处理语句,并进行数据填充。这让我想到了mybatis使用方法。

    @Select("select * from users")
    List<User> getUsers();

    上面是mybatis基于注解的配置方式。除了注解,还支持xml。于是框架准备先使用基于注解的方式进行代码配置。首先,需要知道这些接口类的类名。于是提供一个配置,根据指定包名获取包下所有类的类名详情见代码3

    代码块3

    方法类(com.simple.ibatis.util)

    PackageUtil

    /**
     * @Author  xiabing
     * @Desc    获取指定包下所有类的名称
     **/
    public class PackageUtil {
    
        private static final String CLASS_SUFFIX = ".class";
        private static final String CLASS_FILE_PREFIX = "classes"  + File.separator;
        private static final String PACKAGE_SEPARATOR = ".";
    
        /**
         * 查找包下的所有类的名字
         * @param packageName
         * @param showChildPackageFlag 是否需要显示子包内容
         * @return List集合,内容为类的全名
         */
        public static List<String> getClazzName(String packageName, boolean showChildPackageFlag){
            List<String> classNames = new ArrayList<>();
            String suffixPath = packageName.replaceAll("\.", "/");
            // 获取类加载器
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            try{
                Enumeration<URL> urls = loader.getResources(suffixPath);
                while(urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    if(url != null){
                        if ("file".equals(url.getProtocol())) {
                            String path = url.getPath();
                            classNames.addAll(getAllClassName(new File(path),showChildPackageFlag));
                        }
                    }
                }
            }catch (IOException e){
                throw new RuntimeException("load resource is error , resource is "+packageName);
            }
            return classNames;
        }
    
        
        // 获取所有类的名称
        private static List<String> getAllClassName(File file,boolean flag){
    
            List<String> classNames = new ArrayList<>();
    
            if(!file.exists()){
                return classNames;
            }
            if(file.isFile()){
                String path = file.getPath();
                if(path.endsWith(CLASS_SUFFIX)){
                    path = path.replace(CLASS_SUFFIX,"");
                    String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length()).replace(File.separator, PACKAGE_SEPARATOR);
                    classNames.add(clazzName);
                }
            }else {
                File[] listFiles = file.listFiles();
                if(listFiles != null && listFiles.length > 0){
                    for (File f : listFiles){
                        if(flag) {
                            classNames.addAll(getAllClassName(f, flag));
                        }else {
                            if(f.isFile()){
                                String path = f.getPath();
                                if(path.endsWith(CLASS_SUFFIX)) {
                                    path = path.replace(CLASS_SUFFIX, "");
                                    String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length()).replace(File.separator,PACKAGE_SEPARATOR);
                                    classNames.add(clazzName);
                                }
                            }
                        }
                    }
                }
            }
            return classNames;
        }
    }

    在知道接口类存放在哪些类后,我需要解析这些类中的各个方法,包括每个方法的注解,入参,和返回值。同时,我需要使用一个全局配置类,用于存放我解析后生成的对象,并且一些数据源配置信息也可以放在这个全局类中。详情见代码4

    代码块4

    核心类(com.simple.ibatis.core)

    Config

    /**
     * @Author  xiabing
     * @Desc    框架的核心配置类
     **/
    public class Config {
    
        // 数据源
        private PoolDataSource dataSource;
    
        // mapper包地址(即待解析的包名),后续改为List<String>,能同时加载多个mapper包 todo
        private String daoSource;
    
        // mapper核心文件(存放类解析后的对象)
        private MapperCore mapperCore;
    
        // 是否启用事务
        private boolean openTransaction;
    
        // 是否开启缓存
        private boolean openCache;
    
        public Config(String mapperSource,PoolDataSource dataSource){
            this.dataSource = dataSource;
            this.daoSource = mapperSource;
            this.mapperCore = new MapperCore(this);
        }
        
        // 生成SQL语句执行器
        public SimpleExecutor getExecutor(){
            return new SimpleExecutor(this,this.getDataSource(),openTransaction,openCache);
        }
        
        // 后文都是基本的get,set方法
        public PoolDataSource getDataSource() {
            return dataSource;
        }
    
        public void setDataSource(PoolDataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        public String getDaoSource() {
            return daoSource;
        }
    
        public void setDaoSource(String daoSource) {
            this.daoSource = daoSource;
        }
    
        public MapperCore getMapperCore() {
            return mapperCore;
        }
    
        public void setMapperCore(MapperCore mapperCore) {
            this.mapperCore = mapperCore;
        }
    
        public boolean isOpenTransaction() {
            return openTransaction;
        }
    
        public void setOpenTransaction(boolean openTransaction) {
            this.openTransaction = openTransaction;
        }
    
        public boolean isOpenCache() {
            return openCache;
        }
    
        public void setOpenCache(boolean openCache) {
            this.openCache = openCache;
        }
    
    }

    SqlSource

    /**
     * @Author  xiabing5
     * @Desc    将sql语句拆分为sql部分和参数部分,因为我们需要使用java对象对数据库预处理对象进行注入。具体看下面例子
     
     * @example select * from users where id = {user.id} and name = {user.name}
     *            -> sql = select * from users where id = ? and name = ?
     *            -> paramResult = {user.id,user.name}
     **/
    public class SqlSource {
        /**sql语句,待输入字段替换成?*/
        private String sql;
        /**待输入字段*/
        private List<String> param;
        /**select update insert delete*/
        private Integer executeType;
    
        public SqlSource(String sql){
            this.param = new ArrayList<>();
            this.sql = sqlInject(this.param,sql);
        }
        
        /**sql注入,对要注入的属性,必须使用{} 包裹,并替换为? 见例子 */
        private String sqlInject(List<String> paramResult, String sql){
    
            String labelPrefix = "{";
    
            String labelSuffix = "}";
            // 将语句的所有的{}全部都解析成?,并将{xxx}中的xxx提取出来。
            while (sql.indexOf(labelPrefix) > 0 && sql.indexOf(labelSuffix) > 0){
                String sqlParamName = sql.substring(sql.indexOf(labelPrefix),sql.indexOf(labelSuffix)+1);
                sql = sql.replace(sqlParamName,"?");
                paramResult.add(sqlParamName.replace("{","").replace("}",""));
            }
            return sql;
        }
    
        public String getSql() {
            return sql;
        }
    
        public void setSql(String sql) {
            this.sql = sql;
        }
    
        public List<String> getParam() {
            return param;
        }
    
        public void setParam(List<String> param) {
            this.param = param;
        }
    
        public Integer getExecuteType() {
            return executeType;
        }
    
        public void setExecuteType(Integer executeType) {
            this.executeType = executeType;
        }
    }

    MapperCore

    /**
     * @Author  xiabing5
     * @Desc    mapper方法解析核心类,此处是将接口中的方法进行解析的关键类
     **/
    public class MapperCore {
        
        // select方法
        private static final Integer SELECT_TYPE = 1;
        // update方法
        private static final Integer UPDATE_TYPE = 2;
        // delete方法
        private static final Integer DELETE_TYPE = 3;
        // insert方法
        private static final Integer INSERT_TYPE = 4;
    
        /**mapper文件解析类缓存,解析后都放在里面,避免重复对一个mapper文件进行解析*/
        private static Map<String,MethodDetails> cacheMethodDetails = new ConcurrentHashMap<>();
    
        /**
         * 全局配置
         * */
        private Config config;
    
    
        public MapperCore(Config config){
            this.config = config;
            load(config.getDaoSource());
        }
    
        /*
         * 加载,解析指定包名下的类
         * */
        private void load(String source){
            /**加载mapper包下的文件*/
            List<String> clazzNames = PackageUtil.getClazzName(source,true);
            try{
                for(String clazz: clazzNames){
                    Class<?> nowClazz = java.lang.Class.forName(clazz);
                    // 不是接口跳过,只能解析接口
                    if(!nowClazz.isInterface()){
                        continue;
                    }
    
                    /**接口上没有@Dao跳过。我们对接口类上加了@Dao后,就代表要解析*/
                    boolean skip = false;
                    Annotation[] annotations = nowClazz.getDeclaredAnnotations();
                    for(Annotation annotation:annotations){
                        // 该接口中的注释中是否有带@Dao
                        if(annotation instanceof Dao) {
                            skip = true;
                            break;
                        }
                    }
                    if(!skip){
                        continue;
                    }
                    // 调用反射接口,获取所有接口中的方法
                    Method[] methods = nowClazz.getDeclaredMethods();
                    for( Method method : methods){
                        // 解析方法详情
                        MethodDetails methodDetails = handleParameter(method);
                        // 解析@SELECT()等注解中的SQL内容
                        methodDetails.setSqlSource(handleAnnotation(method));
                        // 解析完成后放入缓存
                        cacheMethodDetails.put(generateStatementId(method),methodDetails);
                    }
                }
            }catch (ClassNotFoundException e){
                throw new RuntimeException(" class load error,class is not exist");
            }
        }
    
        /**
         * 获得方法详情,从方法缓存池中获取,因为每个方法事先已经完成解析
         * */
        public MethodDetails getMethodDetails(Method method){
            String statementId = generateStatementId(method);
            if(cacheMethodDetails.containsKey(statementId)){
                return cacheMethodDetails.get(statementId);
            }
            return new MethodDetails();
        }
    
        /**
         * 获得方法对应的sql语句
         * */
        public SqlSource getStatement(Method method){
            String statementId = generateStatementId(method);
            if(cacheMethodDetails.containsKey(statementId)){
                return cacheMethodDetails.get(statementId).getSqlSource();
            }
            throw new RuntimeException(method + " is not sql");
        }
    
        /**
         * 获得方法对应的参数名
         * */
        public List<String> getParameterName(Method method){
            String statementId = generateStatementId(method);
            if(cacheMethodDetails.containsKey(statementId)){
                return cacheMethodDetails.get(statementId).getParameterNames();
            }
            return new ArrayList<>();
        }
    
        /**
         *  获取方法返回类型
         * */
        public Class getReturnType(Method method){
            String statementId = generateStatementId(method);
            if(cacheMethodDetails.containsKey(statementId)){
                return cacheMethodDetails.get(statementId).getReturnType();
            }
            return null;
        }
    
        /**
         * 获得方法对应的参数类型
         * */
        public Class<?>[] getParameterType(Method method) {
            String statementId = generateStatementId(method);
            if(cacheMethodDetails.containsKey(statementId)){
                return cacheMethodDetails.get(statementId).getParameterTypes();
            }
            return new Class<?>[]{};
        }
    
        /**
         * 获得方法是SELECT UPDATE DELETE INSERT
         * */
        public Integer getMethodType(Method method){
           String statementId = generateStatementId(method);
            if(cacheMethodDetails.containsKey(statementId)){
                return cacheMethodDetails.get(statementId).getSqlSource().getExecuteType();
            }
            return null;
        }
    
    
        /**
         * 获得方法是否返回集合类型list
         * */
        public boolean getHasSet(Method method){
            String statementId = generateStatementId(method);
            if(cacheMethodDetails.containsKey(statementId)){
                return cacheMethodDetails.get(statementId).isHasSet();
            }
            return false;
        }
    
        /**
         * 解析方法内的注解
         * */
        private MethodDetails handleParameter(Method method){
            
            MethodDetails methodDetails = new MethodDetails();
            
            // 获取方法输入参数数量
            int parameterCount = method.getParameterCount();
            
            // 获取方法输入各参数类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            
            // 获取方法输入各参数名称
            List<String> parameterNames = new ArrayList<>();
    
            Parameter[] params = method.getParameters();
            for(Parameter parameter:params){
                parameterNames.add(parameter.getName());
            }
    
            /*
             * 获得方法参数的注解值替代默认值,如果使用了@Param注解,则默认使用注解中的值作为参数名
             * */
            for(int i = 0; i < parameterCount; i++){
                parameterNames.set(i,getParamNameFromAnnotation(method,i,parameterNames.get(i)));
            }
    
            methodDetails.setParameterTypes(parameterTypes);
            methodDetails.setParameterNames(parameterNames);
    
            /** 获取方法返回类型*/
            Type methodReturnType = method.getGenericReturnType();
            Class<?> methodReturnClass = method.getReturnType();
            if(methodReturnType instanceof ParameterizedType){
                /** 返回是集合类 目前仅支持List  todo*/
                if(!List.class.equals(methodReturnClass)){
                    throw new RuntimeException("now ibatis only support list");
                }
                Type type = ((ParameterizedType) methodReturnType).getActualTypeArguments()[0];
                /** 设置 返回集合中的对象类型 */
                methodDetails.setReturnType((Class<?>) type);   
                /** 标注该方法返回类型是集合类型 */
                methodDetails.setHasSet(true);
            }else {
                /** 不是集合类型,就直接设置返回类型 */
                methodDetails.setReturnType(methodReturnClass);
                /** 标注该方法返回类型不是集合类型 */
                methodDetails.setHasSet(false);
            }
    
            return methodDetails;
        }
    
        /**
         * 解析@select,@update等注解,获取SQL语句,并封装成对象
         * */
        private SqlSource handleAnnotation(Method method){
            SqlSource sqlSource = null;
            String sql = null;
            /** 获取方法上所有注解 */
            Annotation[]  annotations = method.getDeclaredAnnotations();
            for(Annotation annotation : annotations){
                /** 如果有@select注解 */
                if(Select.class.isInstance(annotation)){
                    Select selectAnnotation = (Select)annotation;
                    sql = selectAnnotation.value();
                    /** 语句封装成sqlSource对象 */
                    sqlSource = new SqlSource(sql);
                    /** 设置执行语句类型 */
                    sqlSource.setExecuteType(SELECT_TYPE);
                    break;
                }else if(Update.class.isInstance(annotation)){
                    Update updateAnnotation = (Update)annotation;
                    sql = updateAnnotation.value();
                    sqlSource = new SqlSource(sql);
                    sqlSource.setExecuteType(UPDATE_TYPE);
                    break;
                }else if(Delete.class.isInstance(annotation)){
                    Delete deleteAnnotation = (Delete) annotation;
                    sql = deleteAnnotation.value();
                    sqlSource = new SqlSource(sql);
                    sqlSource.setExecuteType(DELETE_TYPE);
                    break;
                }else if(Insert.class.isInstance(annotation)){
                    Insert insertAnnotation = (Insert) annotation;
                    sql = insertAnnotation.value();
                    sqlSource = new SqlSource(sql);
                    sqlSource.setExecuteType(INSERT_TYPE);
                    break;
                }
            }
            if(sqlSource == null){
                throw  new RuntimeException("method annotation not null");
            }
            return sqlSource;
        }
    
        /**
         * 获取@Param注解内容
         * */
        private String getParamNameFromAnnotation(Method method, int i, String paramName) {
            final Object[] paramAnnos = method.getParameterAnnotations()[i];
            for (Object paramAnno : paramAnnos) {
                if (paramAnno instanceof Param) {
                    paramName = ((Param) paramAnno).value();
                }
            }
            return paramName;
        }
    
        /**
         * 生成唯一的statementId
         * */
        private static String generateStatementId(Method method){
            return method.getDeclaringClass().getName() + "." + method.getName();
        }
        
        /**
         * 每个mapper方法的封装类
         * */
        public static class MethodDetails{
            /**方法返回类型,若是集合,则代表集合的对象类,目前集合类仅支持返回List  */
            private Class<?> returnType;
    
            /**方法返回类型是否是集合*/
            private boolean HasSet;
    
            /**执行类型,SELECT,UPDATE,DELETE,INSERT*/
            private Integer executeType;
    
            /**方法输入参数类型集合*/
            private Class<?>[] parameterTypes;
    
            /**方法输入参数名集合*/
            private List<String> parameterNames;
    
            /**sql语句集合*/
            private SqlSource sqlSource;
    
            public Class<?> getReturnType() {
                return returnType;
            }
    
            public void setReturnType(Class<?> returnType) {
                this.returnType = returnType;
            }
    
            public boolean isHasSet() {
                return HasSet;
            }
    
            public void setHasSet(boolean hasSet) {
                HasSet = hasSet;
            }
    
            public Integer getExecuteType() {
                return executeType;
            }
    
            public void setExecuteType(Integer executeType) {
                this.executeType = executeType;
            }
    
            public Class<?>[] getParameterTypes() {
                return parameterTypes;
            }
    
            public void setParameterTypes(Class<?>[] parameterTypes) {
                this.parameterTypes = parameterTypes;
            }
    
            public List<String> getParameterNames() {
                return parameterNames;
            }
    
            public void setParameterNames(List<String> parameterNames) {
                this.parameterNames = parameterNames;
            }
    
            public SqlSource getSqlSource() {
                return sqlSource;
            }
    
            public void setSqlSource(SqlSource sqlSource) {
                this.sqlSource = sqlSource;
            }
        }
    }

    SQL语句已经解析完毕,后面开始执行SQL语句。在之前,我想给执行SQL语句时加点功能,如提供select缓存,事务功能,详情见代码块5,代码块6

    代码块5

    缓存类(com.simple.ibatis.cache)

    Cache

    /**
     * @author xiabing
     * @description: 自定义缓存接口,主要提供增删改查功能
     */
    public interface Cache {
    
        /**放入缓存*/
        void putCache(String key,Object val);
    
        /**获取缓存*/
        Object getCache(String key);
    
        /**清空缓存*/
        void cleanCache();
    
        /**获取缓存健数量*/
        int getSize();
    
        /**移除key的缓存*/
        void removeCache(String key);
    }

    SimpleCache

    /**
     * @author xiabing
     * @description: 缓存的简单实现
     */
    public class SimpleCache implements Cache{
        
        /**用HashMap来实现缓存的增删改查*/
        private static Map<String,Object> map = new HashMap<>();
    
    
        @Override
        public void putCache(String key, Object val) {
            map.put(key,val);
        }
    
        @Override
        public Object getCache(String key) {
            return map.get(key);
        }
    
        @Override
        public void cleanCache() {
            map.clear();
        }
    
        @Override
        public int getSize() {
            return map.size();
        }
    
        @Override
        public void removeCache(String key) {
            map.remove(key);
        }
    }

    LruCache

    /**
     * @author xiabing
     * @description: 缓存包装类没,使缓存具有LRU功能
     */
    public class LruCache implements Cache{
        
        /**缓存大小*/
        private static Integer cacheSize = 2000;
        
        /**HashMap负载因子,可以自己指定*/
        private static Float loadFactory = 0.75F;
        
        /**真实缓存*/
        private Cache trueCache;
        
        /**使用LinkedHashMap来实现Lru功能*/
        private Map<String,Object> linkedCache;
        
        /**待移除的元素*/
        private static Map.Entry removeEntry;
    
        public LruCache(Cache trueCache){
            this(cacheSize,loadFactory,trueCache);
        }
    
        public LruCache(Integer cacheSize, Float loadFactory, Cache trueCache) {
            this.trueCache = trueCache;
            /**初始化LinkedHashMap,并重写removeEldestEntry() 方法,不了解的可以看我之前的博客*/
            this.linkedCache = new LinkedHashMap<String, Object>(cacheSize,loadFactory,true){
                @Override
                protected boolean removeEldestEntry(Map.Entry eldest) {
                    // 当缓存容量超过设置的容量时,返回true,并记录待删除的对象
                    if(getSize() >  cacheSize){
                        removeEntry = eldest;
                        return true;
                    }
                    return false;
                }
            };
        }
    
        // 放入缓存,当待删除的元素不为空时,就在真实缓存中删除该元素
        @Override
        public void putCache(String key, Object val) {
            // 真实缓存中放入
            this.trueCache.putCache(key,val);
            // linkedHashMap中放入,此处放入会调用重写的removeEldestEntry()方法
            this.linkedCache.put(key,val);
            // 当removeEldestEntry()执行后,如果有待删除元素,就开始执行删除操作
            if(removeEntry != null){
                removeCache((String)removeEntry.getKey());
                removeEntry = null;
            }
        }
    
        @Override
        public Object getCache(String key) {
            // 因为调用linkedHashMap中的get()方法会对链表进行再一次排序。见之前关于缓存的博客
            linkedCache.get(key);
            return trueCache.getCache(key);
        }
    
        @Override
        public void cleanCache() {
            trueCache.cleanCache();
            linkedCache.clear();
        }
    
        @Override
        public int getSize() {
            return trueCache.getSize();
        }
    
        @Override
        public void removeCache(String key) {
            trueCache.removeCache(key);
        }
    }

    代码块6

    事务类(com.simple.ibatis.transaction)

    Transaction

    /**
     * @Author  xiabing
     * @Desc    封装事务功能
     **/
    public interface Transaction {
    
        /**获取数据库连接*/
        Connection getConnection() throws SQLException;
        
        /**事务提交*/
        void commit() throws SQLException;
        
        /**事务回滚*/
        void rollback() throws SQLException;
        
        /**关闭连接*/
        void close() throws SQLException;
    }

    SimpleTransaction

    /**
     * @Author  xiabing
     * @Desc    事务的简单实现,封装了数据库的事务功能
     **/
    public class SimpleTransaction implements Transaction{
    
        private Connection connection; // 数据库连接
        private PoolDataSource dataSource; // 数据源
        private Integer level = Connection.TRANSACTION_REPEATABLE_READ; // 事务隔离级别
        private Boolean autoCommmit = true; // 是否自动提交
    
        public SimpleTransaction(PoolDataSource dataSource){
            this(dataSource,null,null);
        }
    
        public SimpleTransaction(PoolDataSource dataSource, Integer level, Boolean autoCommmit) {
            this.dataSource = dataSource;
            if(level != null){
                this.level = level;
            }
            if(autoCommmit != null){
                this.autoCommmit = autoCommmit;
            }
        }
        
        /**获取数据库连接,并设置该连接的事务隔离级别,是否自动提交*/
        @Override
        public Connection getConnection() throws SQLException{
            if(connection != null){
                return connection;
            }
            this.connection = dataSource.getConnection();
    
            this.connection.setAutoCommit(autoCommmit);
    
            this.connection.setTransactionIsolation(level);
    
            return this.connection;
        }
    
        @Override
        public void commit() throws SQLException{
            if(this.connection != null){
                this.connection.commit();
            }
        }
    
        @Override
        public void rollback() throws SQLException{
            if(this.connection != null){
                this.connection.rollback();
            }
        }
        
        /**关闭连接*/
        @Override
        public void close() throws SQLException{
            /**如果该连接设置自动连接为false,则在关闭前进行事务回滚一下*/
            if(autoCommmit != null && !autoCommmit){
                connection.rollback();
            }
    
            if(connection != null){
                dataSource.removeConnection(connection);
            }
    
            this.connection = null;
        }
    }

    TransactionFactory

    /**
     * @Author  xiabing
     * @Desc    事务工厂,封装获取事务的功能
     **/
    public class TransactionFactory {
    
        public static SimpleTransaction newTransaction(PoolDataSource poolDataSource, Integer level, Boolean autoCommmit){
            return new SimpleTransaction(poolDataSource,level,autoCommmit);
        }
        
    }

    代码真正执行SQL的地方,见代码块7

    代码块7

    语句执行类(com.simple.ibatis.execute)

    Executor

    /**
     * @Author  xiabing
     * @Desc    执行接口类,后续执行器需要实现此接口
     **/
    public interface Executor {
    
        /**获取Mapper接口代理类,使用了动态代理技术,见实现类分析*/
        <T> T getMapper(Class<T> type);
        
        void commit() throws SQLException;
    
        void rollback() throws SQLException;
    
        void close() throws SQLException;
    }

    SimpleExecutor

    /**
     * @Author  xiabing
     * @Desc    简单执行器类,对于Sql的执行和处理都依赖此类
     **/
    public class SimpleExecutor implements Executor{
    
        /**全局配置类*/
        public Config config;
        
        /**mapper核心类,解析mapper接口类*/
        public MapperCore mapperCore;
        
        /**数据库源*/
        public PoolDataSource poolDataSource;
        
        /**事务*/
        public Transaction transaction;
        
        /**缓存*/
        public Cache cache;
    
        public SimpleExecutor(Config config,PoolDataSource poolDataSource){
            this(config,poolDataSource,false,false);
        }
    
        public SimpleExecutor(Config config,PoolDataSource poolDataSource,boolean openTransaction,boolean openCache){
            this.config = config;
            this.mapperCore = config.getMapperCore();
            this.poolDataSource = poolDataSource;
            if(openCache){
                // 设置缓存策略为LRU
                this.cache = new LruCache(new SimpleCache());
            }
            if(openTransaction){
                // 关闭自动提交
                this.transaction = TransactionFactory.newTransaction(poolDataSource,Connection.TRANSACTION_REPEATABLE_READ,false);
            }else {
                // 开启自动提交
                this.transaction = TransactionFactory.newTransaction(poolDataSource,null,null);
            }
        }
        
        /**
        * @Desc   获取mapper接口的代理类
                  为什么要使用代理类,因为我们写mapper接口类只写了接口,却没有提供它的实现。于是
                  使用动态代理机制对这些接口进行代理,在代理类中实现sql执行的方法。此处是参照
                  mybatis的设计。
        **/
        @Override
        public <T> T getMapper(Class<T> type){
            /**MapperProxy代理类分析见详情8.1*/
            MapperProxy mapperProxy = new MapperProxy(type,this);
            return (T)mapperProxy.initialization();
        }
        
        /*select 语句执行流程分析**/
        public <E> List<E> select(Method method,Object[] args) throws Exception{
        
            /**PreparedStatementHandle 生成器见9.1 */
            PreparedStatementHandle preparedStatementHandle = new PreparedStatementHandle(mapperCore,transaction,method,args);
            PreparedStatement preparedStatement = preparedStatementHandle.generateStatement();
            
            /**执行查询接口,此处是真实调用sql语句的地方 */
            preparedStatement.executeQuery();
            ResultSet resultSet = preparedStatement.getResultSet();
            
            /**查询方法的返回参数值,若是void类型,就不用返回任务东西 */
            Class returnClass = mapperCore.getReturnType(method);
            if(returnClass == null || void.class.equals(returnClass)){
                return null;
            }else {
                /**ResultSetHandle 结果处理器见9.2 */
                ResultSetHandle resultSetHandle = new ResultSetHandle(returnClass,resultSet);
                return resultSetHandle.handle();
            }
        }
        
        /*update 语句执行流程分析,update,delete,insert都是调用的这个方法**/
        public int update(Method method,Object[] args)throws SQLException{
            PreparedStatementHandle preparedStatementHandle = null;
            PreparedStatement preparedStatement = null;
            Integer count = null;
            try{
                /**PreparedStatementHandle 生成器见9.1 */
                preparedStatementHandle = new PreparedStatementHandle(mapperCore,transaction,method,args);
                preparedStatement = preparedStatementHandle.generateStatement();
                
                /**返回受影响的行数 */
                count =  preparedStatement.executeUpdate();
            }finally {
                if(preparedStatement != null){
                    preparedStatement.close();
                }
            }
            return count;
        }
        
        /**后续方法直接调用transaction相关方法*/
        @Override
        public void commit() throws SQLException{
            transaction.commit();
        }
    
        @Override
        public void rollback() throws SQLException{
            transaction.rollback();
        }
    
        @Override
        public void close() throws SQLException{
            transaction.close();
        }
    }

    因为我们mapper接口类只有接口,没有实现。但为什么可以调用这些接口方法?此处参照mybatis源码,自己通过动态代理技术实现接口的动态代理,在代理方法里写我们自己执行Sql的逻辑。代理类见代码块8

    代码块8

    mapper接口代理类(com.simple.ibatis.execute)

    MapperProxy

    /**
     * @Author  xiabing
     * @Desc    mapper接口代理类
     **/
    public class MapperProxy<T> implements InvocationHandler{
        
        /**要代理的mapper接口类*/
        private Class<T> interfaces;
        
        /**具体的执行器,执行mapper中的一个方法相当于执行一条sql语句*/
        private SimpleExecutor executor;
    
        public MapperProxy(Class<T> interfaces,SimpleExecutor executor) {
            this.interfaces = interfaces;
            this.executor = executor;
        }
        
        /**反射方法,该方法自定义需要代理的逻辑。不了解可以去看下JAVA动态代理技术*/
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = null;
            /**object方法直接代理*/
            if(Object.class.equals(method.getDeclaringClass())){
                result = method.invoke(this,args);
            }else {
                // 获取方法是select还是非select方法
                Integer methodType = executor.mapperCore.getMethodType(method);
    
                if(methodType == null){
                    throw new RuntimeException("method is normal sql method");
                }
                if(methodType == 1){
                    /**调用执行器的select方法,返回集合类*/
                    List<Object> list =  executor.select(method,args);
                    result = list;
                    /**查看接口的返回是List,还是单个对象*/
                    if(!executor.mapperCore.getHasSet(method)){
                        if(list.size() == 0){
                            result = null;
                        }else {
                            /**单个对象就直接取第一个*/
                            result = list.get(0);
                        }
                    }
    
                }else{
                    /**返回受影响的行数*/
                    Integer count = executor.update(method,args);
                    result = count;
                }
            }
            return result;
        }
        
        /**构建代理类*/
        public T initialization(){
            return (T)Proxy.newProxyInstance(interfaces.getClassLoader(),new Class[] { interfaces },this);
        }
    }

    代码块7中执行SQL其实包含3个过程,生成SQL预处理语句,执行SQL,将SQL结果转为JAVA对象。详情见代码块9

    代码块9

    各生成器类(com.simple.ibatis.statement)

    PreparedStatementHandle

    /**
     * @Author  xiabing
     * @Desc    PreparedStatement生成器
     **/
    public class PreparedStatementHandle {
        /**
         * 全局核心mapper解析类
         */
        private MapperCore mapperCore;
    
        /**
         * 待执行的方法
         */
        private Method method;
    
        private Transaction transaction;
    
        private Connection connection;
    
        /**
         * 方法输入参数
         */
        private Object[] args;
    
        public PreparedStatementHandle(MapperCore mapperCore, Transaction transaction,Method method, Object[] args)throws SQLException {
            this.mapperCore = mapperCore;
            this.method = method;
            this.transaction = transaction;
            this.args = args;
            connection = transaction.getConnection();
        }
    
        /**
         * @Author  xiabing5
         * @Desc    参数处理核心方法  todo
         **/
        public PreparedStatement generateStatement() throws SQLException{
            // 获取已经解析方法的sqlSource类,已经将待注入字段变为?,后续直接填充就好
            SqlSource sqlSource = mapperCore.getStatement(method);
            // 调用connection方法生成预处理语句
            PreparedStatement preparedStatement = connection.prepareStatement(sqlSource.getSql());
            
            // 获取方法输入参数的类型
            Class<?>[] clazzes = mapperCore.getParameterType(method);
            List<String> paramNames = mapperCore.getParameterName(method);
            List<String> params = sqlSource.getParam();
            // 详见typeInject方法,注入SQL参数
            preparedStatement = typeInject(preparedStatement,clazzes,paramNames,params,args);
            return preparedStatement;
        }
    
        /**
         * @Author  xiabing
         * @Desc    preparedStatement构建,将参数注入到SQL语句中
         * @Param  preparedStatement 待构建的preparedStatement
         * @Param  clazzes 该方法中参数类型数组
         * @Param  paramNames 该方法中参数名称列表,若有@Param注解,则为此注解的值,默认为类名首字母小写
         * @Param  params 待注入的参数名,如user.name或普通类型如name
         * @Param  args 真实参数值
         **/
        private PreparedStatement typeInject(PreparedStatement preparedStatement,Class<?>[] clazzes,List<String> paramNames,List<String> params,Object[] args)throws SQLException{
    
            for(int i = 0; i < paramNames.size(); i++){
                // 第i个参数名称
                String paramName = paramNames.get(i);
                // 第i个参数类型
                Class type = clazzes[i];
                if(String.class.equals(type)){
                    // 原始SQL中需要注入的参数名中是否有此参数名称
                    // example: select * from users where id = {id} and name = {name}  则{id}中的id和name就是如下的params里面内容
                    int injectIndex = params.indexOf(paramName);
                    /**此处是判断sql中是否有待注入的名称({name})和方法内输入对象名(name)相同,若相同,则直接注入*/
                    if(injectIndex >= 0){
                        preparedStatement.setString(injectIndex+1,(String)args[i]);
                    }
                }else if(Integer.class.equals(type) || int.class.equals(type)){
                    int injectIndex = params.indexOf(paramName);
                    if(injectIndex >= 0){
                        preparedStatement.setInt(injectIndex+1,(Integer)args[i]);
                    }
                }else if(Float.class.equals(type) || float.class.equals(type)){
                    int injectIndex = params.indexOf(paramName);
                    if(injectIndex >= 0){
                        preparedStatement.setFloat(injectIndex+1,(Float)args[i]);
                    }
                }else {
                    /** 若待注入的是对象。example:
                     @SELECT(select * from users where id = {user.id} and name = {user.name})
                     List<User> getUser(User user)
                    */
                    // 对象工厂,获取对象包装实例,见代码块10
                    ObjectWrapper objectWrapper = ObjectWrapperFactory.getInstance(args[i]);
                    for(int j = 0; j < params.size(); j++){
                        /**此处是判断对象的属性 如user.name,需要先获取user对象,在调用getName方法获取值*/
                        if((params.get(j).indexOf(paramName)) >= 0 ){
                            try{
                                String paramProperties = params.get(j).substring(params.get(j).indexOf(".")+1);
                                Object object = objectWrapper.getVal(paramProperties);
                                Class childClazz = object.getClass();
                                if(String.class.equals(childClazz)){
                                    preparedStatement.setString(j+1,(String)object);
                                }else if(Integer.class.equals(childClazz) || int.class.equals(childClazz)){
                                    preparedStatement.setInt(j+1,(Integer)object);
                                }else if(Float.class.equals(childClazz) || float.class.equals(childClazz)){
                                    preparedStatement.setFloat(j+1,(Float)object);
                                }else {
                                    /**目前不支持对象中包含对象,如dept.user.name  todo*/
                                    throw new RuntimeException("now not support object contain object");
                                }
                            }catch (Exception e){
                                throw new RuntimeException(e.getMessage());
                            }
                        }
                    }
                }
            }
            return preparedStatement;
        }
    
        public void closeConnection() throws SQLException{
            transaction.close();
        }
    }

    ResultSetHandle

    /**
     * @Author  xiabing
     * @Desc    ResultSet结果处理器,执行之后返回的json对象,转为java对象的过程
     **/
    public class ResultSetHandle {
    
        /**转换的目标类型*/
        Class<?> typeReturn;
    
        /**待转换的ResultSet*/
        ResultSet resultSet;
    
        Boolean hasSet;
    
        public ResultSetHandle(Class<?> typeReturn,ResultSet resultSet){
            this.resultSet = resultSet;
            this.typeReturn = typeReturn;
        }
    
        /**
         * ResultSet处理方法,目前仅支持String,int,Float,不支持属性是集合类 todo
         * */
        public <T> List<T> handle() throws Exception{
    
            List<T> res = new ArrayList<>(resultSet.getRow());
            Object object = null;
            ObjectWrapper objectWrapper = null;
            Set<ClazzWrapper.FiledExpand> filedExpands = null;
            // 返回类型若不是基本数据类型
            if(!TypeUtil.isBaseType(typeReturn)){
                // 生成对象
                object = generateObj(typeReturn);
                // 将对象封装成包装类
                objectWrapper = ObjectWrapperFactory.getInstance(object);
    
                /** 获取对象属性 */
                filedExpands = objectWrapper.getMapperFiledExpands();
            }
    
            while (resultSet.next()){
                /** 若返回是基础数据类型,则直接将结果放入List中并返回 */
                if(String.class.equals(typeReturn)){
                    String val = resultSet.getString(1);
                    if(val != null){
                        res.add((T)val);
                    }
                }else if(Integer.class.equals(typeReturn) || int.class.equals(typeReturn)){
                    Integer val = resultSet.getInt(1);
                    if(val != null){
                        res.add((T)val);
                    }
                }else if(Float.class.equals(typeReturn) || float.class.equals(typeReturn)){
                    Float val = resultSet.getFloat(1);
                    if(val != null){
                        res.add((T)val);
                    }
                }else { // 若返回的是对象(如User这种)
                    // 查找对象属性,一个个注入
                    for(ClazzWrapper.FiledExpand filedExpand:filedExpands){
                        // 如果对象属性是String类型,例如User.name是String类型
                        if(String.class.equals(filedExpand.getType())){
                            // resultSet中获取该属性
                            String val = resultSet.getString(filedExpand.getPropertiesName());
                            if(val != null){
                                // 填充到对象包装类中
                                objectWrapper.setVal(filedExpand.getPropertiesName(),val);
                            }
                        }else if(Integer.class.equals(filedExpand.getType()) || int.class.equals(filedExpand.getType())){
                            Integer val = resultSet.getInt(filedExpand.getPropertiesName());
                            if(val != null){
                                objectWrapper.setVal(filedExpand.getPropertiesName(),val);
                            }
                        }else if(Float.class.equals(filedExpand.getType()) || float.class.equals(filedExpand.getType())){
                            Float val = resultSet.getFloat(filedExpand.getPropertiesName());
                            if(val != null){
                                objectWrapper.setVal(filedExpand.getPropertiesName(),val);
                            }
                        }else {
                            continue;
                        }
                    }
                    // 后续将对象包装类转为真实对象,放入List中并返回。对象包装类见代码10
                    res.add((T)objectWrapper.getRealObject());
                }
            }
            return res;
        }
    
        // 根据类型,根据反射,实例化对象
        private Object generateObj(Class<?> clazz) throws Exception{
            Constructor[] constructors  = clazz.getConstructors();
            Constructor usedConstructor = null;
            // 获取无参构造器,若对象没有无参构造方法,则失败
            for(Constructor constructor:constructors){
                if(constructor.getParameterCount() == 0){
                    usedConstructor = constructor;
                    break;
                }
            }
            if(constructors == null) {
                throw new RuntimeException(typeReturn + " is not empty constructor");
            }
            // 利用反射生成实例
            return usedConstructor.newInstance();
        }
    }

    代码块9中大量用到了对象增强,类增强。使用这些原因是 在编写框架代码时,因为并不知道每个属性名,不可能把每个get或者set方法写死,只能通过将对象封装成包装类。然后利用反射,要填充某个对象,就调用setVal(属性名),获取对象类似。详情见代码块10

    代码块10

    类增强(com.simple.ibatis.reflect)

    ClazzWrapper

    /**
     * @Author  xiabing
     * @Desc    clazz解析类
     **/
    public class ClazzWrapper {
        /**
         * 待解析类
         * */
        private Class<?> clazz;
        /**
         * 该类存储的属性名
         * */
        private Set<String> propertiesSet = new HashSet<>();
    
        /**
         * 该类存储的属性名及属性类
         * */
        private Set<FiledExpand> filedExpandSet = new HashSet<>();
    
        /**
         * 该类存储的get方法。key为属性名,value为getxxx方法
         * */
        private Map<String,Method> getterMethodMap = new HashMap<>();
        /**
         * 该类存储的set方法。key为属性名,value为setxxx方法
         * */
        private Map<String,Method> setterMethodMap = new HashMap<>();
        /**
         * 缓存,避免对同一个类多次解析
         * */
        private static Map<String,ClazzWrapper> clazzWrapperMap = new ConcurrentHashMap<>();
    
        public ClazzWrapper(Class clazz){
            this.clazz = clazz;
            // 对类进行解析,如果已经解析了,则不用二次解析
            if(!clazzWrapperMap.containsKey(clazz.getName())){
                // 获取该类的所有属性
                Field[] fields = clazz.getDeclaredFields();
                for(Field field : fields){
                    // 获取属性的名称,属性类型
                    FiledExpand filedExpand = new FiledExpand(field.getName(),field.getType());
                    filedExpandSet.add(filedExpand);
                    propertiesSet.add(field.getName());
                }
                // 获取该类的方法
                Method[] methods = clazz.getMethods();
                for(Method method:methods){
                    String name = method.getName();
                    // 如果是get方法
                    if(name.startsWith("get")){
                        name = name.substring(3);
                        if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
                            name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
                        }
                        // 放入get方法缓存中
                        if(propertiesSet.contains(name)){
                            getterMethodMap.put(name,method);
                        }
                    }else if(name.startsWith("set")){
                        name = name.substring(3);
                        if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
                            name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
                        }
                        if(propertiesSet.contains(name)){
                            setterMethodMap.put(name,method);
                        }
                    }
                    else {
                        continue;
                    }
                }
                // 放入缓存,代表该类已经完成解析
                clazzWrapperMap.put(clazz.getName(),this);
            }
        }
        
        // 查找该类是否包含指定属性的get方法
        public boolean hasGetter(String properties){
            ClazzWrapper clazzWrapper = clazzWrapperMap.get(clazz.getName());
    
            return clazzWrapper.getterMethodMap.containsKey(properties);
        }
    
        // 查找该类是否包含指定属性的set方法
        public boolean hasSetter(String properties){
            ClazzWrapper clazzWrapper = clazzWrapperMap.get(clazz.getName());
    
            return clazzWrapper.setterMethodMap.containsKey(properties);
        }
    
        // 获取该类指定属性的set方法
        public Method getSetterMethod(String properties){
            if(!hasSetter(properties)){
               throw new RuntimeException("properties " + properties + " is not set method") ;
            }
            ClazzWrapper clazzWrapper = clazzWrapperMap.get(clazz.getName());
            return clazzWrapper.setterMethodMap.get(properties);
        }
    
        // 获取该类指定属性的get方法
        public Method getGetterMethod(String properties){
            if(!hasGetter(properties)){
                throw new RuntimeException("properties " + properties + " is not get method") ;
            }
            ClazzWrapper clazzWrapper = clazzWrapperMap.get(clazz.getName());
            return clazzWrapper.getterMethodMap.get(properties);
        }
    
        // 获取该类所有属性
        public Set<String> getProperties(){
    
            ClazzWrapper clazzWrapper = clazzWrapperMap.get(clazz.getName());
    
            return clazzWrapper.propertiesSet;
        }
    
        // 获取该类所有属性增强
        public Set<FiledExpand> getFiledExpandSet(){
            ClazzWrapper clazzWrapper = clazzWrapperMap.get(clazz.getName());
    
            return clazzWrapper.filedExpandSet;
        }
    
        /**
         *  属性增强类
         * */
        public static class FiledExpand{
    
            // 属性名称
            String propertiesName;
    
            // 属性类型
            Class type;
    
            public FiledExpand() {
            }
    
            public FiledExpand(String propertiesName, Class type) {
                this.propertiesName = propertiesName;
                this.type = type;
            }
    
            public String getPropertiesName() {
                return propertiesName;
            }
    
            public void setPropertiesName(String propertiesName) {
                this.propertiesName = propertiesName;
            }
    
            public Class getType() {
                return type;
            }
    
            public void setType(Class type) {
                this.type = type;
            }
    
            @Override
            public int hashCode() {
                return propertiesName.hashCode();
            }
    
            @Override
            public boolean equals(Object obj) {
                if(obj instanceof FiledExpand){
                    return ((FiledExpand) obj).propertiesName.equals(propertiesName);
                }
                return false;
            }
        }
    }

    ObjectWrapper

    /**
     * @Author  xiabing
     * @Desc    对象增强,封装了get,set方法
     **/
    public class ObjectWrapper {
        // 真实对象
        private Object realObject;
        // 该对象的类的增强
        private ClazzWrapper clazzWrapper;
    
        public ObjectWrapper(Object realObject){
            this.realObject = realObject;
            this.clazzWrapper = new ClazzWrapper(realObject.getClass());
        }
    
        // 调用对象指定属性的get方法
        public Object getVal(String property) throws Exception{
    
            return clazzWrapper.getGetterMethod(property).invoke(realObject,null);
        }
    
        // 调用对象指定属性的set方法
        public void setVal(String property,Object value) throws Exception{
    
            clazzWrapper.getSetterMethod(property).invoke(realObject,value);
        }
    
        public Set<String> getProperties(){
    
            return clazzWrapper.getProperties();
        }
    
        public Set<ClazzWrapper.FiledExpand> getMapperFiledExpands(){
    
            return clazzWrapper.getFiledExpandSet();
        }
    
        public Object getRealObject(){
    
            return realObject;
    
        }
    }

    ObjectWrapperFactory

    /**
     * @Author  xiabing5
     * @Desc    对象增强类。使用对象增强是因为我们不知道每个对象的getXXX方法,只能利用反射获取对象的属性和方法,然后
                统一提供getVal或者setVal来获取或者设置指定属性,这就是对象的增强的重要性
     **/
    public class ObjectWrapperFactory {
    
        public static ObjectWrapper getInstance(Object o){
            return new ObjectWrapper(o);
        }
    
    }

    总结感悟

    上述是我在写这个框架时的一些思路历程,代码演示请见我github。通过自己手写源码,对mybatis也有了个深刻的见解。当然有很多问题,后面也会进一步去完善,如果你对这个开源项目感兴趣,不妨star一下呀!

  • 相关阅读:
    247. Strobogrammatic Number II 倒过来看也一样的数
    性能测试结果分析
    性能测试文档
    性能测试技术
    性能测试流程
    JMeter(7)插件
    Java同步工具类
    线程池
    死锁
    Lock显示锁
  • 原文地址:https://www.cnblogs.com/xiaobingblog/p/14149977.html
Copyright © 2011-2022 走看看