文档章节

给JFinal添加了类似ROR的Flash功能。

玛雅牛
 玛雅牛
发布于 2013/06/04 12:39
字数 1410
阅读 751
收藏 5

最近在用JFinal开发一个小型的项目,使用的是个人比较认可的HTTL模板引擎。过几天不忙了把相关集成的东西发出了。

今天先发一个:给JFinal添加了类似ROR的Flash功能。

在使用JFinal做开发时,经常会用到如下代码:


if(webUser.update()){
        this.setSessionAttr("msg", "更新成功");
	this.redirect("detail?id="+webUser.getLong("id"));
	return;
}
this.setAttr("msg", "更新失败");
render("edit.html");
使用redirect后,会重定向到另一个页面,要传送一些信息,要么使用Session,要么拼装重定向的url地址来做。使用Session的话,需要考虑何时清除的问题,还有就是性能问题,Session中应该少放东西,少放大东西。使用拼装url地址的方法比较麻烦,容易出现错误,另外负责对象难以通过这种方式传输。当然如果系统设计成后端全是服务,返回json,这种问题就不会有了。只是个人习惯传统的页面跳转方式所以需要解决这个问题。


如何解决这个问题,想到RoR中的Flash操作,通过Fash保存数据,这些数据仅能在下次请求时获取,获取后随即清楚。最终通过Jfina的ehcache插件解决了此问题。

思路如下:

基于ehCache构建了一个FlashManager,负责将Flash保存在cache中。这个FlashManager是全局的,为了区分每个用户,使用了sessionKey(这个其实是SessionId);


package com.jfinal.core;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

import com.jfinal.plugin.ehcache.CacheKit;

/**
 *
 * Flash管理器
 *
 * @author dafei
 *
 */
public class FlashManager {
	/**
	 * 全局静态flash管理器
	 */
	private static FlashManager flashManager = new FlashManager();
	/**
	 * falsh 存放在cache中的值。
	 */
	private final String flashCacheName = "flashCache";

	/**
	 * 读写锁
	 */
	private ReentrantLock lock = new ReentrantLock();

	/**
	 * 构造函数,不允许初始化
	 */
	private FlashManager() {
	}

	/**
	 * 单例方法
	 *
	 * @return flash管理器
	 */
	public static FlashManager getInstance() {
		return flashManager;
	}

	/**
	 * 添加flash信息到缓存中。
	 *
	 * @param sessionKey
	 *            session路径
	 * @param curAction
	 *            当前ActionPath
	 * @param key
	 *            键
	 * @param value
	 *            值
	 */
	@SuppressWarnings("unchecked")
	public void setFlash(String sessionKey, String curAction, String key,
			Object value) {
		sessionKey = sessionKey + curAction.replace("/", "_");
		lock.lock();
		Object obj = CacheKit.get(flashCacheName, sessionKey);
		Map<String, Object> map = null;
		if (obj != null) {
			map = (Map<String, Object>) obj;
		} else {
			map = new HashMap<String, Object>();
			CacheKit.put(flashCacheName, sessionKey, map);
		}
		map.put(key, value);
		lock.unlock();
	}

	/***
	 * 在调用redirect forwardAction
	 * 时调用此接口,将以当前actionPath为key更替为下一个请求actionPath作为key。
	 *
	 * @param sessionKey
	 *            session的Id值
	 * @param curAction
	 *            当前ActionPath
	 * @param nextAction
	 *            下一个ActionPath
	 */
	public void updateFlash(String sessionKey, String curAction,
			String nextAction) {
		String oldKey = sessionKey + curAction.replace("/", "_");
		String newkey = sessionKey + nextAction.replace("/", "_");
		lock.lock();
		Object obj = CacheKit.get(flashCacheName, oldKey);
		if (obj != null) {
			CacheKit.remove(flashCacheName, oldKey);
			CacheKit.put(flashCacheName, newkey, obj);
		}
		lock.unlock();
	}

	/**
	 * 从cache中取得Flash的Map
	 *
	 * @param sessionKey
	 *            session路径
	 * @param curAction
	 *            当前ActionPath
	 * @return Flash的Map
	 */
	@SuppressWarnings("unchecked")
	public Map<String, Object> getFlash(String sessionKey, String curAction) {
		String sessionActionKey = sessionKey + curAction.replace("/", "_");
		Map<String, Object> map = null;
		lock.lock();
		Object obj = CacheKit.get(flashCacheName, sessionActionKey);
		if (obj != null) {
			map = (Map<String, Object>) obj;
			CacheKit.remove(flashCacheName, sessionActionKey);
		}
		lock.unlock();
		return map;
	}
}

修改Controler中相关代码:

增加了一个变量,用来记录是否设定了Flash信息

增加了两个方法:


/**
	 * 根据当前路径构造将要跳转的路径的完整Action
	 * @param currentActionPath 当前路径,类似 /sau/index
	 * @param url 下一个路径,类似/au/login, detail?,admin/detail.
	 * @return 下一个Action的完整路径()
	 */
	public String parsePath(String currentActionPath, String url){
		if(url.startsWith("/")){//完整路径
			return url.split("\\?")[0];
		}else if(!url.contains("/")){//类似于detail的路径。
			return "/"+ currentActionPath.split("/")[1] + "/" + url.split("\\?")[0];
		}else if(url.contains("http:")|| url.contains("https:")){
			return null;
		}
		///abc/def","bcd/efg?abc
		return currentActionPath + "/" + url.split("\\?")[0];
	}

	/**
	 * 设定Flash,该flash中的所有信息将会出现在下一个请求中。
	 * 该操作一般用在forwardAction 及redirect操作前。
	 * 在设定Falsh拦截器后,拦截器会自动注入所有当前Action中设定的Flash信息到request中。
	 * 且仅注入一次。
	 * @param key 键
	 * @param value 值
	 */
	public void setFlash(String key, Object value){
		String sessionKey = this.getSession(false).getId();
		String actionPath = this.request.getRequestURI();
		FlashManager.getInstance().setFlash(sessionKey,actionPath, key, value);
		setFlashFalg = true;
	}


修改了三个方法:

public void forwardAction(String actionUrl) {
		if(setFlashFalg){//若有新加入的Flash。更换key。
			String sessionKey = this.getSession(false).getId();
			String actionPath = this.request.getRequestURI();
			//将以当前actionPath为key更替为下一个请求actionPath作为key
			FlashManager.getInstance().updateFlash(sessionKey, actionPath, actionUrl);
			setFlashFalg =false;
		}
		render = new ActionRender(actionUrl);
	}



public void redirect(String url) {
		if(setFlashFalg){
			String sessionKey = this.getSession(false).getId();
			String actionPath = this.request.getRequestURI();
			String newActionPath = parsePath(actionPath, url);
			FlashManager.getInstance().updateFlash(sessionKey, actionPath, newActionPath);
			setFlashFalg = false;
		}
		render = renderFactory.getRedirectRender(url);
	}
public void redirect(String url, boolean withQueryString) {
		if(setFlashFalg){
			String sessionKey = this.getSession(false).getId();
			String actionPath = this.request.getRequestURI();
			String newActionPath = parsePath(actionPath, url);
			FlashManager.getInstance().updateFlash(sessionKey, actionPath, newActionPath);
			setFlashFalg = false;
		}
		render = renderFactory.getRedirectRender(url, withQueryString);
	}


这些解决了Flash的记录问题,怎样将数据在适当的时候可以让程序访问到呢?想到了拦截器。实现一个。


package com.jfinal.ext.interceptor;

import java.util.Map;
import java.util.Map.Entry;

import com.jfinal.aop.Interceptor;
import com.jfinal.core.ActionInvocation;
import com.jfinal.core.Controller;
import com.jfinal.core.FlashManager;

/**
 * Flash拦截器。
 * @author dafei
 *
 */
public class Flash implements Interceptor{
	@Override
	/**
	 * 该拦截器取得当前ActionPath,从Cache中检查是否有传送给当前Action的Flash对象Map
     * 若有,则遍历Map,并将所有key,value注入到当前的request请求中。
	 */
	public void intercept(ActionInvocation ai) {
		String sessionKey = ai.getController().getSession(false).getId();
		String curAction = ai.getViewPath()+ai.getMethodName();
		Map<String, Object> flashMap = FlashManager.getInstance().getFlash(sessionKey, curAction);
		if(flashMap != null){
			Controller c = ai.getController();
			for(Entry<String,Object> flashEntry: flashMap.entrySet()){
				c.setAttr(flashEntry.getKey(), flashEntry.getValue());
			}
		}
		ai.invoke();
	}
}


实现完毕。

如何使用:

在ehcache.xml中添加一个


<cache name="flashCache"
           maxElementsInMemory="1024"
           eternal="false"
           timeToIdleSeconds="180"
           timeToLiveSeconds="180"
           overflowToDisk="true"
           diskSpoolBufferSizeMB="10" />


Controller上或者需要传入数据的Action上启动Flash拦截器。

@Before({SessionInterceptor.class,Flash.class})
public class SmsApiUserController extends Controller 

@Before(Flash.class)
public void detail(){
  。。。
}
如何调用


if(webUser.update()){
			
			this.setFlash("msg", "更新成功");
			this.redirect("detail?id="+webUser.getLong("id"));
			return;
		}
		this.setAttr("msg", "更新失败");
		render("edit.html");


总结一下:这个Flash实现主要解决 两个请求间的仅且一次的数据传递问题,当然这两个请求是有要求的:一个请求和其发起的另一个请求(redirect,forwardAction操作)。

目前用起来还不错。个人对JFinal初学,实现难免考虑不周。

目前该实现没有考虑到同一个Session中多个请求同时发起同一个请求的情况。

还请@JFinal 波总指导。





© 著作权归作者所有

共有 人打赏支持
玛雅牛

玛雅牛

粉丝 483
博文 113
码字总数 27287
作品 4
高级程序员
加载中

评论(7)

走位风骚闪着腰
走位风骚闪着腰

引用来自“JFinal”的评论

谢谢分享,jfinal 未来版本考虑提供这个功能
40jfinal2.3有计划加上这个功能不
玛雅牛
玛雅牛

引用来自“谭明智”的评论

最好用session来实现,基础组件还是紧靠servlet的标准。可以提供外部插件替换

建议很不错。有空实现下。
菜根乱谭
菜根乱谭
最好用session来实现,基础组件还是紧靠servlet的标准。可以提供外部插件替换
菜根乱谭
菜根乱谭
哈哈,挺好的。我以前在struts2上实现过。
JFinal
JFinal
谢谢分享,jfinal 未来版本考虑提供这个功能
缪斯的情人
缪斯的情人
思路挺好,这个功能确实挺实用,但是使用ehCache有些重了,可以按照ror的方式通过拼接参数到重定向url方式实现,至于怎么实现,可以通过类似于django里面的local()方法避免拼接错误。另外还可以通过ajax方式在前端控制跳转同时附带参数。
紫电清霜
紫电清霜
挺好的,很有用! :)
OSC上关于Jfinal的提问整理(一)

看见Jfinal很火,就手痒痒了,想学一下,无奈入门较慢,没有找到比较全的文档。于是就经常看讨论区大家的提问与解答。后来就忽然萌生了整理下来的想法。其中的问题如果是@Jfinal 回答的,那我...

木川瓦兹
2013/04/23
0
21
JFinal 3.4 发布,将极速贯彻到 UI 层

jfinal 的终极目标是全面实现软件开发整个过程的极速开发,极大提升开发效率,极大降低学习成本,极大提升开发体验 jfinal 诞生头五年,已实现 WEB + ORM + AOP 层面的极速开发,赢得了大量开...

JFinal
04/28
0
129
JFinal Weixin 2.1 发布,微信极速 SDK

JFinal Weixin 发布四年多以来,以其简单性、稳定性,获得了大量用户的喜爱,这四年多以来 JFinal Weixin 紧跟微信官方动态,不断增加、完善功能,例如跟随本次微信官方发布的 XXE 漏洞,第一...

JFinal
07/10
0
0
Maven项目中添加jFinal包以及源文件

JFinal是国人开发的一个WEB+ORM框架,个人认为是一个简化的MVC模型,闲话不多说,首先要下载相应的lib。 找到: jfinal-1.8-bin.jar jfinal-1.8-bin-with-src.jar 下面将这两项写入Maven本地...

Barudisshu
2014/07/11
0
1
OSC上关于Jfinal的提问整理(二)

1.【问】:Db.tx(new IAtom())事务不起作用? 【jfinal答】:1:如果使用的mysql,确保引擎为 InnoDB 2:这行代码改一下Db.save(c3p0Plugin.getDataSource(), "tbtest", "PKID", record);去掉...

木川瓦兹
2013/04/25
0
2

没有更多内容

加载失败,请刷新页面

加载更多

原型模式

1、原型模式-定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象 克隆(浅度克隆->拷贝值类型或者引用,深度克隆->创建新的对象,开辟新的内存) 例如客户端知道抽象Pro...

阿元
今天
50
0
awk命令扩展使用操作

awk 中使用外部shell变量 示例1 [root@centos01 t1022]# A=888[root@centos01 t1022]# echo "" | awk -v GET_A=$A '{print GET_A}'888[root@centos01 t1022]# echo "aaaaaaaaaaaaa" | aw......

野雪球
今天
43
0
深入解析MySQL视图VIEW

Q:什么是视图?视图是干什么用的? A:视图(view)是一种虚拟存在的表,是一个逻辑表,本身并不包含数据。作为一个select语句保存在数据字典中的。   通过视图,可以展现基表的部分数据;...

IT--小哥
今天
48
0
虚拟机学习之二:垃圾收集器和内存分配策略

1.对象是否可回收 1.1引用计数算法 引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时候计数器值为0的对象就是不可能...

贾峰uk
今天
47
0
smart-doc功能使用介绍

smart-doc从8月份底开始开源发布到目前为止已经迭代了几个版本。在这里非常感谢那些敢于用smart-doc去做尝试并积极提出建议的社区用户。因此决定在本博客中重要说明下smart-doc的功能,包括使...

上官胡闹
昨天
47
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部