web框架之jfinal
介绍
web框架除了现最流行的spring mvc,还有其他如jfinal,jersery等。现在学习jfinal的基础。
jfinal有相关的开发者社区,其中最有名是一个叫“波总”的人。 官方文档在https://www.jfinal.com/doc/2-4。
jfinal的优点:开发极简,秒速启动,拥有很多与spring类似的特性,内嵌undertow,jetty,上手快,学习成本低。
jfinal的缺点:文档不够丰富,案例不够完善,感觉功能配套不是很齐全。  
Hello World
jfinal的最重要的类是JFinalConfig,基本所有的相关的配置都是在这个类上进行。
最简单的使用: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
57package io.github.black.jfinal.demo;
import com.jfinal.config.*;
import com.jfinal.json.JacksonFactory;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.druid.DruidPlugin;
import com.jfinal.server.undertow.UndertowServer;
import com.jfinal.template.Engine;
import io.github.black.jfinal.demo.controller.HelloController;
import io.github.black.jfinal.demo.entity._MappingKit;
import io.github.black.jfinal.demo.handler.ResourcesHandler;
public class DemoConfig extends JFinalConfig {
    public static void main(String[] args) {
        UndertowServer.start(DemoConfig.class,8091,true);
    }
    
    public void configConstant(Constants constants) {
        constants.setDevMode(true);
    }
    
    public void configRoute(Routes routes) {
        routes.add("/hello", HelloController.class);
    }
    
    public void configEngine(Engine engine) {
    }
    
    public void configPlugin(Plugins plugins) {
       
    }
    
    public void configInterceptor(Interceptors interceptors) {
        
    }
    
    public void configHandler(Handlers handlers) {
     
    }
    
    public void onStart() {
    }
    
    public void onStop() {
    }
}
1  | public class HelloController extends Controller {  | 
然后打开浏览器http://localhost:8091/hello
很有意思的一点,可以很方便返回一个二维码:
首先添加maven配置:1
2
3
4
5<dependency>
           <groupId>com.google.zxing</groupId>
           <artifactId>javase</artifactId>
           <version>3.2.1</version>
       </dependency>
demo如下:1
2
3
4
5
6
7public void qrCode(){
        // 二维码携带的数据
        String data = "dengdb.github.io";
        // 渲染二维码图片,长度与宽度为 200 像素
        renderQrCode(data, 200, 200);
    }
参数注入以及拦截器
jfinal使用@Inject进行字段进行注入,只能是由jfinal管理的对象才行,如controller,Interceptor等。对于非jfinal组件,可以使用Aop.get(); 至于Aop.inject()没找到该怎么用。1
2
3
4
5
6
7
8
9
10
11
12public class LoginService {
    private LoginDao loginDao = Aop.get(LoginDao.class);
}  
在jfinalConfig中开启配置
    public void configConstant(Constants constants) {
        constants.setDevMode(true);
        // 开启对 jfinal web 项目组件 Controller、Interceptor、Validator 的注入
        constants.setInjectDependency(true);
    }
经过测试,在不同的地方使用Aop.get()获取到的是同一个对象。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public class LoginServiceT {
    private LoginDao loginDao = Aop.get(LoginDao.class);
    public String loginT(){
        System.out.println(loginDao);
        return loginDao.save();
    }
}
public class LoginService {
    private LoginDao loginDao = Aop.get(LoginDao.class);
    public String login(){
        System.out.println(loginDao);
        return loginDao.save();
    }
}
查看控制台打印日志,两个loginDao为同一个对象。
关于jfinal的入参注入问题,官文有相关文档,大致有以下几种方式:
1.在Action中直接使用,jfinal自动转换。1
2
3
4
5
6public class HelloController extends Controller {
    public void getJson(User user){
        renderJson("name",user.getName());
    }
}
我用post+json方式测试过,返回的是null,官文案例使用form表单方式,没测试过。
2.使用getPara()方式。 controller里内置了很多方法,如renderText(),renderJson,getPara()等,在controller中使用getpara()或者其重载方法,可以很方便的获取入参。
3.使用HttpKit。 jfinal内置了很多kit,如PropKit,Aop其实也算一个kit,DbKit。在controller中使用HttpKit可以获取入参:HttpKit.readData(getRequest());  
JSON的支持
jfinal支持四种json,其中包括jacsonJson和fastJson。只需在jfinalConfig中配置即可。renderJson会按照配置的json进行转换。1
2
3
4
5
6
7
8
    public void configConstant(Constants constants) {
        constants.setDevMode(true);
        //配置json
        constants.setJsonFactory(new JacksonFactory());
        // 开启对 jfinal web 项目组件 Controller、Interceptor、Validator 的注入
        constants.setInjectDependency(true);
    }
Model的使用
使用jfinal的generator生成model和配置。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
53package io.github.black.jfinal.demo;
import com.jfinal.kit.PathKit;
import com.jfinal.plugin.activerecord.generator.Generator;
import com.jfinal.plugin.druid.DruidPlugin;
import javax.sql.DataSource;
public class ModelGenerator {
    private static DruidPlugin getDP(){
        DruidPlugin dp = new DruidPlugin("jdbc:mysql://10.108.136.128/blackdb", "root", "123456");
        return dp;
    }
    public static void main(String[] args) {
        // base model 所使用的包名
        String baseModelPkg = "io.github.black.jfinal.demo.entity.base";
        // base model 文件保存路径
        String baseModelDir = PathKit.getWebRootPath() + "/src/main/java/io/github/black/jfinal/demo/entity/base";
        // model 所使用的包名
        String modelPkg = "io.github.black.jfinal.demo.entity";
        // model 文件保存路径
        String modelDir = baseModelDir + "/..";
        DruidPlugin dp = getDP();
        dp.start();
        DataSource ds = dp.getDataSource();
        Generator gernerator = new Generator(ds, baseModelPkg, baseModelDir, modelPkg, modelDir);
        // 在 getter、setter 方法上生成字段备注内容
        gernerator.setGenerateRemarks(true);
        // 设置是否生成链式 setter 方法
//        gernerator.setGenerateChainSetter(false);
        // 添加不需要生成的表名
//        gernerator.addExcludedTable("adv");
        //覆盖掉其中的 isSkipTable(String tableName) 方法,可以随心所欲控制想要的处理的 table
        ModelMetaBuilder metaBuilder = new ModelMetaBuilder(ds);
        gernerator.setMetaBuilder(metaBuilder);
        // 设置是否在 Model 中生成 dao 对象
        gernerator.setGenerateDaoInModel(true);
        // 设置是否生成链式 setter 方法
        gernerator.setGenerateChainSetter(true);
        // 设置是否生成字典文件
        gernerator.setGenerateDataDictionary(false);
        // 设置需要被移除的表名前缀用于生成modelName。例如表名 "osc_user",移除前缀 "osc_"后生成的model名为 "User"而非 OscUser
//        gernerator.setRemovedTableNamePrefixes("t_");
        // 生成
        gernerator.generate();
        dp.stop();
    }
}
小结:
生成器根据数据库配置,扫描相关的表,生成相应的model。这里最主要的是,获取DataSource,然后是配置model的包路径,baseModel一般在model的base下面;gernerator.addExcludedTable(“adv”)可以排除不需要的表,如果太多,也可以使用MetaBuilder,复写isSkipTable()方法,随心所欲的控制想要处理的table。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class ModelMetaBuilder extends MetaBuilder {
    public ModelMetaBuilder(DataSource dataSource){
        super(dataSource);
    }
    /**
     * isSkipTable 方法 return true 时将过滤掉当前 table
     * @param tableName
     * @return
     */
    protected boolean isSkipTable(String tableName) {
        if("user".equalsIgnoreCase(tableName)|| "wesd_cert".equalsIgnoreCase(tableName)){
            return false;
        }
        return true;
    }
}
上诉代码执行后,会在model下生成一个_MappingKit类(或者其他名字),该类需要在jfinalConfig中使用1
2
3
4
5
6
7
8
9
    public void configPlugin(Plugins plugins) {
        DruidPlugin dp = new DruidPlugin("jdbc:mysql://10.108.136.128/blackdb", "root", "123456");
        plugins.add(dp);
        ActiveRecordPlugin arp = new ActiveRecordPlugin(dp);
        _MappingKit.mapping(arp);
//       arp.addMapping("user", User.class);
        plugins.add(arp);
    }
Kit的使用
PropKit.use()
HttpKit.readData(),HttpKit.post(),HttpKit.get()
DbKit.getConfig()
更多参考com.jfinal.kit包  
总结
jfinal的开发和代码真的是极简,上手快。 但是实际应用的案例太少,还有很多的功能需要完善。
jfinal还有关于redis插件和其他模板渲染插件以及缓存插件,任务调度插件,以后再测试。