Jfinal-Plugin源码解读
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:初始化ActiveRecordPluginActiveRecordPlugin 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源码解读的更多相关文章
- Jfinal启动源码解读
本文对Jfinal的启动源码做解释说明. PS:Jfinal启动容器可基于Tomcat/Jetty等web容器启动,本文基于Jetty的启动方式做启动源码的解读和分析,tomcat类似. 入口 JF ...
- Jfinal控制器源码解读
本文对Jfinal的控制器源码做以下分析. PS:控制器是所有请求跳转的基础,本文就Jfinal控制器的继承关系及初始化的方法做出解释说明. 啰嗦下:所有的请求和响应都是都是通过web容器封装,我们主 ...
- JFinal的启动源码解读
本文对Jfinal的启动源码做解释说明. PS:Jfinal启动容器可基于Tomcat/Jetty等web容器启动,本文基于Jetty的启动方式做启动源码的解读和分析,tomcat类似. 入口 JF ...
- Vue 源码解读(5)—— 全局 API
目标 深入理解以下全局 API 的实现原理. Vue.use Vue.mixin Vue.component Vue.filter Vue.directive Vue.extend Vue.set V ...
- Mybatis源码解读-插件
插件允许对Mybatis的四大对象(Executor.ParameterHandler.ResultSetHandler.StatementHandler)进行拦截 问题 Mybatis插件的注册顺序 ...
- SDWebImage源码解读之SDWebImageDownloaderOperation
第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...
- SDWebImage源码解读 之 NSData+ImageContentType
第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...
- SDWebImage源码解读 之 UIImage+GIF
第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...
- SDWebImage源码解读 之 SDWebImageCompat
第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...
随机推荐
- .NET 绝对路径的配置
有时候因为用IIS配置网站,会导致一些全局引用有路径问题无法引用到.今天就说一下,关于全局引用的绝对路径的配置,譬如,由于IIS配置的虚拟路径,一些CSS,JS的引用找不到,又或者自定义的一些跳转出现 ...
- Solr集群搭建详细教程(二)
注:欢迎大家转载,非商业用途请在醒目位置注明本文链接和作者名dijia478,商业用途请联系本人dijia478@163.com. 之前步骤:Solr集群搭建详细教程(一) 三.solr集群搭建 注意 ...
- AutoLayout的几种方法
1.XIB 2.Fram 3.屏幕比例适配(个人比较推荐) iOS屏幕适配(尺寸适配) 4.NSLayoutConstraint. 5.Masonry 概述 使用 Objective-C 纯代码编 ...
- Mybatis入门(一)之操作数据库
Whats Mybatis 持久层框架, 替代MVC层中DAO,因为DAO 层的需求就是 :能与数据库交互的对象. 能执行SQL语句. 不同于JDBC的connection,MyBatis 中有个Sq ...
- linux下增加磁盘改变指定文件路径分区挂载点和迁移数据
Centos7 系统上原有目录/data 挂载根目录下,空间有点小,我们需要把/data目录挂载到另一个磁盘,同时把数据迁移. 1.查看分区情况 fdisk -l 2.查看路径对应分区情况 df -l ...
- 创建access数据库表demo的封装
1.创建类 public void CreateBiao(ADOX.Catalog catlog,ADOX.Table table) { //StuId Column(AutoIncrement ) ...
- Jrebel简单的热部署一个web工程
前言:博主最近在做Hybris开发,漫长的启动时间大大的拖累了项目的进度,而Jrebel的出现就是为了减少项目重启的时间或者说修改了代码后直接不用重启就可以看到修改的结果,但是Hybris的部署一直没 ...
- 大白话Vue源码系列(05):运行时鸟瞰图
阅读目录 Vue 实例的生命周期 实例创建 响应的数据绑定 挂载到 DOM 节点 结论 研究 runtime 一边 Vue 一边源码 初看 Vue 是 Vue 源码是源码 再看 Vue 不是 Vue ...
- MySQL 字符集问题及安全的更新操作
一.字符集乱码 1.操作系统字符集 [root@mysql5 ~]# cat /etc/system-release /etc/sysconfig/i18n CentOS release 6.5 (F ...
- requests爬取网页的通用框架
概述 代码编写完成时间:2017.12.28 写文章时间:2017.12.29 看完中国大学MOOC上的爬虫教程后,觉得自己之前的学习完全是野蛮生长,决定把之前学的东西再梳理一遍,主要是觉得自己写的程 ...