PS:cnxieyang@163.com/xieyang@e6yun.com

本文就Jfinal-plugin的源码进行分析和解读

  • Plugin继承及实现关系类图如下,常用的是Iplugin的三个集成类:DruidPlugin、RedisPlugin、ActiveRecordPlugin,下文就父类及3个实现类做解读
  • IPlugin详解
    IPlugin为Jfinal插件实现类的接口,提供了两个抽象的方法:boolean start(); boolean stop();供子类实现,Jfinal在加载时,调用实现类的start()方法启动插件,所谓插件是将某个业务模块进行了封装,如redis、dbcp等。定义方法如下
    	boolean start();
    	boolean stop();  
  • IDataSourceProvider详解
    IDataSourceProvider提供获取DataSource的方法,其本身为接口,凡是与数据库相关的插件都可以实现IDataSourceProvider接口,接口中的方法如下图
    DataSource getDataSource();
  • 如何加载自定义插件
    1.在JFinalConfig实现类的configPlugin(Plugins me)方法中创建对象,将启动逻辑写到插件继承类的start方法中
    2.在JFinal的init-->Config.configJFinal(jfinalConfig)-->startPlugins()中完成对插件的start方法的调用,加载插件成功
  • RedisPlugin源码解读(不深入到redis,仅读取到Jredis对象的获取,其余部分在后续的redis章节讲解)
    PS:此处基本上没有难度,自行研究即可
    包括:主缓存、redis缓存的缓存、缓存方法的封装
  • DruidPlugin源码解读(仅读取到数据库连接的获取,其余部分在后续得Druid高级使用模块讲解)
    DruidPlugin主要是多Druid的DruidDataSource注入了相关的参数变量,不做分析
  • ActiveRecordPlugin源码
    ActiveRecordPlugin是Jfinal引入的持久层的新思路,下文重点讲解什么是ActiveRecordPlugin、用途、高级使用等
    ActiveRecordPlugin实现的是DB+ActiveRecord模式
    /**
         * 配置插件
         */
        public void configPlugin(Plugins me) {
            // 配置C3p0数据库连接池插件
            DruidPlugin druidPlugin = createDruidPlugin();
            me.add(druidPlugin);
    
            // 配置ActiveRecord插件,需要传入IDataSourceProvider dataSourceProvider,druidPlugin刚刚好是IDataSourceProvider的子类        //A1:初始化ActiveRecordPlugin 
             ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin);        //A2:所有映射在中自动化搞定,加载映射关系        arp.addMapping("blog", "id", Blog.class);        me.add(arp);    }
  • ActiveRecordPlugin如何初始化1.调用下方构造
    public ActiveRecordPlugin(IDataSourceProvider dataSourceProvider) {
            this(DbKit.MAIN_CONFIG_NAME, dataSourceProvider);
        }

    2.继续调用ActiveRecordPlugin(String configName, IDataSourceProvider dataSourceProvider)构造,如下

    public ActiveRecordPlugin(String configName, IDataSourceProvider dataSourceProvider) {
            this(configName, dataSourceProvider, DbKit.DEFAULT_TRANSACTION_LEVEL);
        }

    3.传入事务的级别后再调用构造,如下

    public ActiveRecordPlugin(String configName, IDataSourceProvider dataSourceProvider, int transactionLevel) {
            if (StrKit.isBlank(configName)) {
                throw new IllegalArgumentException("configName can not be blank");
            }
            if (dataSourceProvider == null) {
                throw new IllegalArgumentException("dataSourceProvider can not be null");
            }        //IDataSourceProvider 的对象赋值
           this.dataSourceProvider = dataSourceProvider;       //赋值当前对象的config属性的属性值,PS代码省略       this.config = new Config(configName, null, transactionLevel); }
  • 模型与表明如何映射
    JFinal为我们提供重载的两个方法,实现对数据表与实体模型的映射,如下放所示
      //带有主键,主键以逗号隔开   public ActiveRecordPlugin addMapping(String tableName, String primaryKey, Class<? extends Model<?>> modelClass) {
            tableList.add(new Table(tableName, primaryKey, modelClass));
            return this;
        }
        //不带主键
        public ActiveRecordPlugin addMapping(String tableName, Class<? extends Model<?>> modelClass) {
            tableList.add(new Table(tableName, modelClass));
            return this;
        }

    1.有主键的Table的初始化

    public Table(String name, String primaryKey, Class<? extends Model<?>> modelClass) {
            if (StrKit.isBlank(name))
                throw new IllegalArgumentException("Table name can not be blank.");
            if (StrKit.isBlank(primaryKey))
                throw new IllegalArgumentException("Primary key can not be blank.");
            if (modelClass == null)
                throw new IllegalArgumentException("Model class can not be null.");
    
            this.name = name.trim();
            setPrimaryKey(primaryKey.trim());
            this.modelClass = modelClass;
        }
    //将以逗号隔开的主键拆分数数组并赋值给primaryKey对象void setPrimaryKey(String primaryKey) {
    String[] arr = primaryKey.split(",");
    for (int i=0; i<arr.length; i++)
    arr[i] = arr[i].trim();
    this.primaryKey = arr;
    }

    2.无主键的Table的初始化

        public Table(String name, Class<? extends Model<?>> modelClass) {
            if (StrKit.isBlank(name))
                throw new IllegalArgumentException("Table name can not be blank.");
            if (modelClass == null)
                throw new IllegalArgumentException("Model class can not be null.");
    
            this.name = name.trim();
            this.modelClass = modelClass;
        }

    3.存储Table关系到List中
    ps:ActiveRecordPlugin类中提供了私有集合private List<Table> tableList = new ArrayList<Table>();用来存储table映射到List中

  • ActiveRecordPlugin加载并初始化映射关系
    PS:插件的启动在JFinal启动源码解读博客章节做了详细介绍<http://www.cnblogs.com/cnxieyang/p/7236734.html>请点击参照。
        循坏调用所有插件的Start方法,完成插件的启动,ActiveRecordPlugin的start启动方法如下,下方对所有实现做详细介绍
    public boolean start() {
    		if (isStarted) {
    			return true;
    		}
    		if (config.dataSource == null && dataSourceProvider != null) {
    			config.dataSource = dataSourceProvider.getDataSource();
    		}
    		if (config.dataSource == null) {
    			throw new RuntimeException("ActiveRecord start error: ActiveRecordPlugin need DataSource or DataSourceProvider");
    		}
    
    		config.sqlKit.parseSqlTemplate();
    		//C1:构建映射
    		new TableBuilder().build(tableList, config);                //添加配置
                   	DbKit.addConfig(config);
    		isStarted = true;
    		return true;
    	}
    

    1.C1:构建映射调用build方法

    void build(List<Table> tableList, Config config) {
            // 支持 useAsDataTransfer(...) 中的 arp.start() 正常运作
            if (config.dataSource instanceof NullDataSource) {
                return ;
            }
    
            Table temp = null;
            Connection conn = null;
            try {
                conn = config.dataSource.getConnection();
                TableMapping tableMapping = TableMapping.me();
                for (Table table : tableList) {
                    temp = table;                //C11:构建
                    doBuild(table, conn, config);                //Map集合中存储映射关系
                    tableMapping.putTable(table);
                    DbKit.addModelToConfigMapping(table.getModelClass(), config);
                }
            } catch (Exception e) {
                if (temp != null) {
                    System.err.println("Can not create Table object, maybe the table " + temp.getName() + " is not exists.");
                }
                throw new ActiveRecordException(e);
            }
            finally {
                config.close(conn);
            }
        }

    C11:实现如下

    @SuppressWarnings("unchecked")
        private void doBuild(Table table, Connection conn, Config config) throws SQLException {
            table.setColumnTypeMap(config.containerFactory.getAttrsMap());
            if (table.getPrimaryKey() == null) {
                table.setPrimaryKey(config.dialect.getDefaultPrimaryKey());
            }
            //构建查询数据库字段的sql
            String sql = config.dialect.forTableBuilderDoBuild(table.getName());
            Statement stm = conn.createStatement();
            ResultSet rs = stm.executeQuery(sql);
            ResultSetMetaData rsmd = rs.getMetaData();
            //遍历数据表字段,将数据表字段类型映射成JAVA类型,并存储到Table对象中
            for (int i=1; i<=rsmd.getColumnCount(); i++) {
                String colName = rsmd.getColumnName(i);
                String colClassName = rsmd.getColumnClassName(i);
    
                Class<?> clazz = javaType.getType(colClassName);
                if (clazz != null) {
                    table.setColumnType(colName, clazz);
                }
                else {
                    int type = rsmd.getColumnType(i);
                    if (type == Types.BINARY || type == Types.VARBINARY || type == Types.BLOB) {
                        table.setColumnType(colName, byte[].class);
                    }
                    else if (type == Types.CLOB || type == Types.NCLOB) {
                        table.setColumnType(colName, String.class);
                    }
                    else {
                        table.setColumnType(colName, String.class);
                    }
                    // core.TypeConverter
                    // throw new RuntimeException("You've got new type to mapping. Please add code in " + TableBuilder.class.getName() + ". The ColumnClassName can't be mapped: " + colClassName);
                }
            }
    
            rs.close();
            stm.close();
        }

    备注:以上完成了ActiveRecordPlugin的初始化,后续详解如何使用Db+ActiveRecord的方式加载或者写入数据
    PS:时间不早了,休息了。。。

Jfinal-Plugin源码解读的更多相关文章

  1. Jfinal启动源码解读

    本文对Jfinal的启动源码做解释说明. PS:Jfinal启动容器可基于Tomcat/Jetty等web容器启动,本文基于Jetty的启动方式做启动源码的解读和分析,tomcat类似. 入口  JF ...

  2. Jfinal控制器源码解读

    本文对Jfinal的控制器源码做以下分析. PS:控制器是所有请求跳转的基础,本文就Jfinal控制器的继承关系及初始化的方法做出解释说明. 啰嗦下:所有的请求和响应都是都是通过web容器封装,我们主 ...

  3. JFinal的启动源码解读

    本文对Jfinal的启动源码做解释说明. PS:Jfinal启动容器可基于Tomcat/Jetty等web容器启动,本文基于Jetty的启动方式做启动源码的解读和分析,tomcat类似. 入口  JF ...

  4. Vue 源码解读(5)—— 全局 API

    目标 深入理解以下全局 API 的实现原理. Vue.use Vue.mixin Vue.component Vue.filter Vue.directive Vue.extend Vue.set V ...

  5. Mybatis源码解读-插件

    插件允许对Mybatis的四大对象(Executor.ParameterHandler.ResultSetHandler.StatementHandler)进行拦截 问题 Mybatis插件的注册顺序 ...

  6. SDWebImage源码解读之SDWebImageDownloaderOperation

    第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...

  7. SDWebImage源码解读 之 NSData+ImageContentType

    第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...

  8. SDWebImage源码解读 之 UIImage+GIF

    第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...

  9. SDWebImage源码解读 之 SDWebImageCompat

    第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...

随机推荐

  1. Unity优化之GC——合理优化Unity的GC

      转载请标明出处http://www.cnblogs.com/zblade/ 最近有点繁忙,白天干活晚上抽空写点翻译,还要运动,所以翻译工作进行的有点缓慢 =.= PS: 最近重新回来更新了一遍,文 ...

  2. 在Windows上运行Spark程序

    一.下载Saprk程序 https://d3kbcqa49mib13.cloudfront.net/spark-2.1.1-bin-hadoop2.7.tgz 解压到d:\spark-2.1.1-bi ...

  3. 《python参考手册(第四版)》【PDF】下载

    <python参考手册(第四版)>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382222 内容介绍 本书是权威的Python语 ...

  4. 'boost/iterator/iterator_adaptor.hpp' file not found之xcode生成时报错的解决方案

    xcode生成rn(0.49.3)项目的时候出现“'boost/iterator/iterator_adaptor.hpp' file not found之xcode”报错. 原因: /Users/x ...

  5. Python 项目实践二(下载数据)第四篇

    接着上节继续学习,在本节中,你将下载JSON格式的人口数据,并使用json模块来处理它们.Pygal提供了一个适合初学者使用的地图创建工具,你将使用它来对人口数据进行可视化,以探索全球人口的分布情况. ...

  6. C#中&与&&的区别

    c#&是什么意思? 看过一些文章,关于这个的简单而容易被忽略的语法,说的总有点瑕疵. 贴代码15秒之内应该能知道c#中一个&和两个&&的区别,开始计数了........ ...

  7. UVA 11600 Masud Rana

    题目大意:有一个n个点的完全图,有些路上有妖怪.现在一个人从一号点出发,每天随机走向另一个点,消灭路上的妖怪,问平均几天后所有点之间存在没有妖怪的路径.点数≤30. 看到点这么少肯定状压,看见存不下肯 ...

  8. [知了堂学习笔记]_用JS制作《飞机大作战》游戏_第4讲(创建敌方飞机、敌方飞机发射子弹、玩家子弹击中敌方小飞机,小飞机死亡)

    一.创建敌方飞机 1.思考创建思路: 创建敌方飞机思路与创建玩家飞机思路一样: (1)思考敌方飞机具备什么属性: 敌方飞机的图片.坐标.飞行速度.状态(是否被击中) 设置小飞机被击中时消失时间.飞机可 ...

  9. lesson - 11 正则表达式

    正则就是有一定规律的字符串,有几个特殊符号很关键(. * + ? | ),我们平时不仅可以用命令行工具grep/sed/awk去引用正则,而且还可以把正则嵌入在nginx.apache.甚至php.p ...

  10. jquery图片延迟加载 及 serializeArray、serialize用法记录

    1.使用jquery实现 图片延迟加载 由于用户访问页面需要加载很多的图片,延迟加载技术在电子商务网站领域越来越普及,淘宝商城,京东商城,凡客等访问量巨大的电子商务站点为了增加用户用户体验,访问速度以 ...