Jfinal 源码分析

Jfinal 基本使用与源码分析

jfinal 使用

jfinal Config

1
2
3
4
5
6
7
8
public class DemoConfig extends JFinalConfig {
public void configConstant(Constants me) {}//配置JFinal常量值
public void configRoute(Routes me) {}//配置访问路由
public void configEngine(Engine me) {}//配置Template Engine
public void configPlugin(Plugins me) {}//配置JFinal的Plugin,比如druid、ActiveRecord
public void configInterceptor(Interceptors me) {}//全局拦截器,配置粒度分为 Global、Inject、Class、Method四个层次
public void configHandler(Handlers me) {}//Handler可以接管所有web请求
}

提供回调方法,读取配置工具类

ActiveRecord

基础用法

  • Tx 拦截器是通过捕捉到异常以后才回滚事务的,所以上述代码中的 doIt() 方法中如果有 try catch 块捕获到异常,必须再次抛出,才能让 Tx 拦截器感知并回滚事务。
  • JDBC 默认的事务级别为:Connection.TRANSACTION_READ_COMMITTED
  • 通过调用 ActiveRecordPlugin.setCache(…) 便可切换 cache 实现
  • Dialect配置
    1
    2
    3
    4
    5
    6
    7
    8
    public class DemoConfig extends JFinalConfig {
    public void configPlugin(Plugins me) {
    ActiveRecordPlugin arp = new ActiveRecordPlugin(…);
    me.add(arp);
    // 配置Postgresql方言
    arp.setDialect(new PostgresqlDialect());
    }
    }

多数据源

  • 当使用多数据源时,只需要对每个 ActiveRecordPlugin指定一个 configName即可:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public void configPlugin(Plugins me) {
    // mysql 数据源
    DruidPlugin dsMysql = new DruidPlugin(…);
    me.add(dsMysql);

    // mysql ActiveRecrodPlugin 实例,并指定configName为 mysql
    ActiveRecordPlugin arpMysql = new ActiveRecordPlugin("mysql", dsMysql);
    me.add(arpMysql);
    arpMysql.addMapping("user", User.class);

    // oracle 数据源
    DruidPlugin dsOracle = new DruidPlugin(…);
    me.add(dsOracle);

    // oracle ActiveRecrodPlugin 实例,并指定configName为 oracle
    ActiveRecordPlugin arpOracle = new ActiveRecordPlugin("oracle", dsOracle);
    me.add(arpOracle);
    arpOracle.setDialect(new OracleDialect());
    arpOracle.addMapping("blog", Blog.class);
    }
  • 切换需要使用Db.use(configName)方法得到数据库操作对象
    1
    2
    3
    4
    // 查询 dsMysql数据源中的 user
    List<Record> users = Db.use("mysql").find("select * from user");
    // 查询 dsOracle数据源中的 blog
    List<Record> blogs = Db.use("oracle").find("select * from blog");

独立使用

ActiveRecordPlugin可以独立于java web 环境运行在任何普通的java程序中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ActiveRecordTest {
public static void main(String[] args) {
DruidPlugin dp = new DruidPlugin("localhost", "userName", "password");
ActiveRecordPlugin arp = new ActiveRecordPlugin(dp);
arp.addMapping("blog", Blog.class);

// 与 jfinal web 环境唯一的不同是要手动调用一次相关插件的start()方法
dp.start();
arp.start();

// 通过上面简单的几行代码,即可立即开始使用
new Blog().set("title", "title").set("content", "cxt text").save();
Blog.dao.findById(123);
}
}

Enjoy 模板引擎

简单易用,以扩展,可以独立和spring、spring boot整合,很不错的设计

提供扩展

  • EhCachePlugin
  • RdisPlugin
  • Cron4jPlugin 调用规则需要注意
  • Validator
  • 国际化
  • Json转换
  • 扩展

jfinal 源码分析

官方demo

配置与启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package vip.infotech.jfinal.helloworld.config;

import com.jfinal.config.*;
import com.jfinal.server.undertow.UndertowServer;
import com.jfinal.template.Engine;
import vip.infotech.jfinal.helloworld.controller.HelloWorldController;

public class ProjectConfig extends JFinalConfig {

public static void main(String[] args) {
UndertowServer.start(ProjectConfig.class);
}

public void configRoute(Routes me) {
me.add("/", HelloWorldController.class);
}

public void configEngine(Engine me) {
me.setBaseTemplatePath("webapp").setToClassPathSourceFactory();
}

public void configConstant(Constants me) {}
public void configPlugin(Plugins me) {
System.out.println("test configPlugin");
}
public void configInterceptor(Interceptors me) {}
public void configHandler(Handlers me) {}
}

controler

1
2
3
4
5
6
7
8
9
10
package vip.infotech.jfinal.helloworld.controller;

import com.jfinal.core.Controller;
import com.jfinal.core.paragetter.Para;

public class HelloWorldController extends Controller {
public void index(@Para(value="who", defaultValue="World")String who) {
keepPara();
}
}

启动过程,通过IDE调试工具调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// 1.执行UndertowServer.start(),根据配置信息,创建UndertowServer执行启动服务方法,默认80端口
public static void start(Class<? extends JFinalConfig> jfinalConfigClass) {
create(jfinalConfigClass).start();
}
……
protected void doStart() {
if (started) {
return ;
}

/**
* jfinal-undertow 中一切依赖 devMode 的动作都会切到生产模式,性能发挥到极致
*/
if (UndertowKit.isDeployMode()) {
UndertowConfig.devMode = false;
}

init();// 1.1初始化

/* 挪到 init() 之中,让 config 中的值更早生效
if (configConsumer != null) {
configConsumer.accept(config);
configConsumer = null; // 配置在整个生命周期只能调用一次
}*/

if (onDeployConsumer != null) {
onDeployConsumer.accept(config.getClassLoader(), deploymentInfo);
}

if (UndertowKit.notAvailablePort(config.getPort())) {
throw new IllegalStateException("port: " + config.getPort() + " not available!\n");
}


deploymentManager = Servlets.defaultContainer().addDeployment(deploymentInfo);
deploymentManager.deploy();// 部署

configHttp();// 1.2配置http-重点
// configSsl();

// 在 start 前进行更多配置
if (onStartConsumer != null) {
onStartConsumer.accept(builder);
}

undertow = builder.build();
undertow.start();// 启动

if (isDevMode() && hotSwapWatcher == null) {
hotSwapWatcher = new HotSwapWatcher(this);
hotSwapWatcher.start();
}

started = true;// 完成最终启动
}

// 1.1初始化
protected void init() {
builder = Undertow.builder();// 构造Undertow
/*
if (configConsumer != null) {
configConsumer.accept(config);
configConsumer = null; // 配置在整个生命周期只能调用一次
}*/
configJFinalPathKit(); // 为 com.jfinal.kit.PathKit 注入值,以便支持inal-undertow 的部署方式 该配置要兼顾开发与部署两种场景 部署模式下才需要为 jfinal 的 PathKit 注入值
configUndertow(); // 配置Undertow
// configListener();
// configWebSocket();
// configServlet();
// configFilter();
configWeb();// 配置web
configJFinalFilter();// 1.1.1配置过滤器
}
// 1.2配置http
protected void configHttp() {
HttpHandler httpHandler = null;
try {
httpHandler = deploymentManager.start();// 1.2.1部署启动,启动后会执行filter过滤器
} catch (ServletException e) {
stopSilently();
throw new RuntimeException(e);
}
HttpHandler pathHandler = Handlers.path().addPrefixPath(config.getContextPath(), httpHandler);
pathHandler = configHandler(pathHandler);
pathHandler = configGzip(pathHandler);
pathHandler = new SetHeaderHandler(pathHandler, "Server", "JFinal");

pathHandler = configSsl(pathHandler);

builder
.addHttpListener(config.getPort(), config.getHost())
// .setServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, false)
.setHandler(pathHandler);
}

// 1.1.1配置过滤器方法
protected void configJFinalFilter() {
deploymentInfo.addFilter(
Servlets.filter("jfinal", getJFinalFilter()).addInitParam("configClass", config.getJFinalConfig())
).addFilterUrlMapping("jfinal", "/*", DispatcherType.REQUEST);
}

// 1.2.1 deploymentManager.start();执行后会调用JFinalFilter下的init
public void init(FilterConfig filterConfig) throws ServletException {
if (this.jfinalConfig == null) {
this.createJFinalConfig(filterConfig.getInitParameter("configClass"));
}

jfinal.init(this.jfinalConfig, filterConfig.getServletContext());// 1.2.1.1 然后调用JFinal下的init
String contextPath = filterConfig.getServletContext().getContextPath();
this.contextPathLength = contextPath != null && !"/".equals(contextPath) ? contextPath.length() : 0;
this.constants = Config.getConstants();
this.encoding = this.constants.getEncoding();
this.jfinalConfig.afterJFinalStart();
this.handler = jfinal.getHandler();
}
// 1.2.1.1 然后调用JFinal下的init
void init(JFinalConfig jfinalConfig, ServletContext servletContext) {
this.servletContext = servletContext;
this.contextPath = servletContext.getContextPath();
this.initPathKit();
Config.configJFinal(jfinalConfig);// 1.2.1.1.1 然后调用Config.configJFinal
this.constants = Config.getConstants();
this.initActionMapping();// 初始化
this.initHandler();// 初始化
this.initRender();// 初始化
this.initOreillyCos();// 初始化
this.initTokenManager();// 初始化
}
// 1.2.1.1.1 然后调用Config.configJFinal
static void configJFinal(JFinalConfig jfinalConfig) {
jfinalConfig.configConstant(constants);
initLogFactory();
initEngine();
configPluginWithOrder(1, jfinalConfig);
jfinalConfig.configRoute(routes);// 1.2.1.1.1.1配置路由
configPluginWithOrder(2, jfinalConfig);// 1.2.1.1.1.2这一步会对用户配置的插件进行配置然后输出"test configPlugin"
jfinalConfig.configEngine(engine);// 配置引擎
configPluginWithOrder(3, jfinalConfig);
jfinalConfig.configInterceptor(interceptors);// 配置拦截器
configPluginWithOrder(4, jfinalConfig);
jfinalConfig.configHandler(handlers);// 配置Handler
configPluginWithOrder(5, jfinalConfig);
}
// 1.2.1.1.1.1 调用工程用户定义的ProjectConfig.configRoute
public void configRoute(Routes me) {
me.add("/", HelloWorldController.class); //1.2.1.1.1.1.1
}
// 1.2.1.1.1.1.1 调用Routes.add 添加路由
public Routes add(String controllerKey, Class<? extends Controller> controllerClass, String viewPath) {
this.routeItemList.add(new Routes.Route(controllerKey, controllerClass, viewPath));
return this;
}
// 1.2.1.1.1.2 插件配置
private static void configPluginWithOrder(int order, JFinalConfig jfinalConfig) {
if (order == constants.getConfigPluginOrder()) {
jfinalConfig.configPlugin(plugins);// 执行该语句调用ProjectConfig.configPlugin输出"test configPlugin"
startPlugins();
}

}

参考