快速上手JFinal
JFinal是國產的MVC框架,由 Handler、Interceptor、Controller、Render、Plugin 五大部分組成。本文通過一個例子上手JFinal,旨在熟悉JFinal中各組件的用法。
目標
- 用戶登錄/注冊
- 用戶上傳圖片
- 登錄權限驗證
訪問路徑
用戶登錄:IP/user/login
用戶注冊:IP/user/register
用戶上傳圖片:IP/user/image
登錄后能訪問: IP/user/show
依賴
- druid-1.0.5.jar
- cos-26Dec2008.jar
- jfinal-2.0-bin.jar
- log4j-1.2.16.jar
AppConfig
JFinal需要一個繼承JFinalConfig的子類,我們這里取名叫AppConfig,這個類的名字是隨便取的,我這里取AppConfig。
import com.jfinal.config.*;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.druid.DruidPlugin;
import com.jfinal.render.ViewType;
import controller.IndexController;
import controller.UserController;
import model.User;
/**
* Created by reeco_000 on 2015/7/22.
*/
public class AppConfig extends JFinalConfig{
@Override
public void configConstant(Constants constants) {
constants.setEncoding("UTF-8");
constants.setDevMode(true);
constants.setViewType(ViewType.JSP);
}
@Override
public void configRoute(Routes routes) {
routes.add("/", IndexController.class);
routes.add("/user", UserController.class);
}
@Override
public void configPlugin(Plugins plugins) {
//這里啟用Jfinal插件
PropKit.use("jdbc.properties");
final String URL =PropKit.get("jdbcUrl");
final String USERNAME = PropKit.get("user");
final String PASSWORD =PropKit.get("password");
final Integer INITIALSIZE = PropKit.getInt("initialSize");
final Integer MIDIDLE = PropKit.getInt("minIdle");
final Integer MAXACTIVEE = PropKit.getInt("maxActivee");
DruidPlugin druidPlugin = new DruidPlugin(URL,USERNAME,PASSWORD);
druidPlugin.set(INITIALSIZE,MIDIDLE,MAXACTIVEE);
druidPlugin.setFilters("stat,wall");
plugins.add(druidPlugin);
ActiveRecordPlugin activeRecordPlugin = new ActiveRecordPlugin(druidPlugin);
activeRecordPlugin.addMapping("user","userid", User.class);
plugins.add(activeRecordPlugin);
}
@Override
public void configInterceptor(Interceptors interceptors) {
//這里用于配置全局的攔截器,對所有請求進行攔截
}
@Override
public void configHandler(Handlers handlers) {
}
} 配置文件
Jdbc.properties
在根目錄下新建一個Jdbc.properties,用來保存數據庫連接信息。我們在AppConfig中需要加載它,使用了JFinal的工具類PropKit。
driverClass=com.mysql.jdbc.Driver jdbcUrl=jdbc:mysql://localhost:3306/school user=root password=root initialSize=1 minIdle=1 maxActivee=20
log4j.properties
這樣就能使用log4j進行日志統計,在需要記錄的地方獲取logger對象即可
log4j.rootLogger=INFO, stdout, file
log4j.appender.stdout.Target=System.out
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%n%-d{yyyy-MM-dd HH:mm:ss}%n[%p]-[Thread: %t]-[%C.%M()]: %m%n
# Output to the File
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=./web.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%n%-d{yyyy-MM-dd HH:mm:ss}%n[%p]-[Thread: %t]-[%C.%M()]: %m%n
# Druid
log4j.logger.druid.sql=warn,stdout
log4j.logger.druid.sql.DataSource=warn,stdout
log4j.logger.druid.sql.Connection=warn,stdout
log4j.logger.druid.sql.Statement=debug,stdout
log4j.logger.druid.sql.ResultSet=warn,stdout web.xml
然后在web.xml里配置JFinal的核心filter和我們寫好的AppConfig
<filter>
<filter-name>jfinal</filter-name>
<filter-class>com.jfinal.core.JFinalFilter</filter-class>
<init-param>
<param-name>configClass</param-name>
<param-value>AppConfig</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>jfinal</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> Model
定義用戶實體,代碼非常簡單
package model;
import com.jfinal.plugin.activerecord.Model;
/**
* Created by reeco_000 on 2015/7/22.
*/
public class User extends Model<User>{
} 繼承Model就行了,無需 getter、setter 方法,無需Annotation,無需xml
這里數據庫中user設計也很簡單,就三個字段,userid,username,password,貼下sql:
DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `userid` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, PRIMARY KEY (`userid`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
Service
service層,封裝業務邏輯。這里用到了ActiveRecord插件,需要在AppConfig中注冊。
public void configPlugin(Plugins plugins) {
//這里啟用JFinal插件
DruidPlugin druidPlugin = new DruidPlugin(URL,USERNAME,PASSWORD);
druidPlugin.set(INITIALSIZE,MIDIDLE,MAXACTIVEE);
druidPlugin.setFilters("stat,wall");
plugins.add(druidPlugin);
ActiveRecordPlugin activeRecordPlugin = new ActiveRecordPlugin(druidPlugin);
//添加Model類和數據庫表的映射。user指的是表名,userid指的是主鍵
activeRecordPlugin.addMapping("user","userid", User.class);
plugins.add(activeRecordPlugin);
} 數據庫連接池使用的是Druid,JFinal還支持C3P0。JFinal使用的是Db+Record 完成ORM。Db類提供了大量查詢的方法,這種模式類似于ruby on rails。
package service;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;
/**
* Created by reeco_000 on 2015/7/22.
*/
public class UserService {
public boolean add(String username,String password){
String SQL = "SELECT userid FROM user WHERE username =?";
Integer result = Db.queryFirst(SQL, username);
if(result==null){
Record user = new Record().set("username", username).set("password", password);
Db.save("user", user);
return true;
}
return false;
}
public boolean login(String username,String password){
String SQL = "SELECT userid FROM user WHERE username =? and password=?";
Integer result = Db.queryFirst(SQL, username, password);
if(result!=null)
return true;
else return false;
}
} Interceptor
JFinal里的攔截器有三個作用域,全局,類和方法。利用JFinal的AOP,使用起來也非常方便。攔截器定義也非常簡單,只需繼承Interceptor接口重寫intercept方法。全局的攔截器是在AppConfig里配置:
public void configInterceptor(Interceptors interceptors) {
//這里用于配置全局的攔截器,對所有請求進行攔截
// 添加控制層全局攔截器
interceptors.addGlobalActionInterceptor(new GlobalActionInterceptor());
// 添加業務層全局攔截器
interceptors.addGlobalServiceInterceptor(new GlobalServiceInterceptor());
} 類和方法使用@Before(Class.class)即能使用,具體可以看下面Controller的例子。在JFinal2還提供了Inject攔截器,不過我們的例子里沒這需求,所以沒用到,具體各位還是看官網文檔吧。
AuthInterceptor
簡易的權限攔截器,這里的作用是只有登錄的才能執行。在session里設置了一個變量flag,當是true時就執行。
package interceptor;
import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.core.Controller;
/**
* Created by reeco_000 on 2015/7/22.
*/
public class AuthInterceptor implements Interceptor {
@Override
public void intercept(Invocation invocation) {
Controller controller = invocation.getController();
Boolean loginUser = controller.getSessionAttr("flag");
if (loginUser ==true )
invocation.invoke();
else
controller.redirect("/");
}
} LoginValidator
Validator是JFinal提供的校驗組件。其核心是Interceptor,所以用法和Interceptor類似,繼承Validator類即可。
-
validate提供了一系列的validateXXX方法
-
handleError處理錯誤,常見的用法是
controller.keepPara(); controller.render("register.html");意思是返回原頁面,保持傳入的參數。
package validator;
import com.jfinal.core.Controller;
import com.jfinal.validate.Validator;
/**
* Created by reeco_000 on 2015/7/22.
*/
public class LoginValidator extends Validator {
@Override
protected void validate(Controller c) {
validateRequiredString("username","nameError","username is null");
validateRequiredString("password","passError","username is null");
}
@Override
protected void handleError(Controller c) {
}
} Controller
JFinal的Controller需要繼承Controller類,類的映射是在AppConfig中配置:
public void configRoute(Routes routes) {
routes.add("/", IndexController.class);
routes.add("/user", UserController.class);
} 我們這里配置意思是/訪問到IndexController這個類,/user 訪問到UserController。/user/login 默認訪問UserController.login()方法,這點類似于struct2,如果之前有基礎,上手會非常快。
UserController
映射 /user,在login()上使用了校驗攔截器LoginValidator,show()使用了權限攔截器AuthInterceptor。
package controller;
import com.jfinal.aop.Before;
import com.jfinal.core.Controller;
import interceptor.AuthInterceptor;
import service.UserService;
import validator.LoginValidator;
/**
* Created by reeco_000 on 2015/7/22.
*/
public class UserController extends Controller{
private UserService userService = new UserService();
public void index(){}
@Before(LoginValidator.class)
public void login(){
String username = getPara("username");
String password = getPara("password");
boolean loginCheck = userService.login(username,password);
if(loginCheck){
renderJson("10000");
getSession().setAttribute("flag",true);
}
else
renderJson("10001");
}
public void register(){
String username = getPara("username");
String password = getPara("password");
boolean result = userService.add(username,password);
if(result)
renderJson("10010");
else
renderJson("10011");
}
@Before(AuthInterceptor.class)
public void show(){
renderJsp("user.jsp");
}
public void image(){
try{
getFile(getPara("img"),"UTF-8");
renderJson("20010");
} catch (Exception e){
renderJson("20012");
}
}
} - getPara() 從請求中獲取參數
- getFile() 支持文件上傳
- renderJson() 返回JSON,JFinal提供了一系列的render方法,目前支持的視圖類型有:
FreeMarker、JSP、Velocity、JSON、File、Text、Html 等等。
IndexController
package controller;
import com.jfinal.core.Controller;
/**
* Created by reeco_000 on 2015/7/22.
*/
public class IndexController extends Controller{
public void index(){
renderJsp("index.jsp");
}
} index是Controller默認調用的方法
總結
最后整個工程的文件如上圖所示,代碼非常簡單,我也就不上傳獻丑了。主要介紹了JFinal的Interceptor、Controller、Render、Plugin,Handler是對Controller和Interceptor的補充。ORM使用的是Db+ActiveRecord。JFinal還有很多組件沒有提到,各位請到官網查看詳細文檔。