文档章节

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

玛雅牛
 玛雅牛
发布于 2013/06/04 12:39
字数 1410
阅读 755
收藏 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 波总指导。





© 著作权归作者所有

共有 人打赏支持
玛雅牛

玛雅牛

粉丝 485
博文 113
码字总数 27360
作品 4
高级程序员
私信 提问
加载中

评论(7)

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

引用来自“JFinal”的评论

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

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

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

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

@JFinal 你好,想跟你请教个问题: 波总,请问有没有计划把ROR的flash功能加入到jfinal里? Thanks.

Jacky_D_H
2015/05/04
115
1
JFinal的ActiveRecord有更详细的文档吗?

因为也在尝试用Java实现类似RoR的ActiveRecord,就想看看现有框架都实现了哪些特性。 看了JFinal 1.5的手册,感觉写得太简短,例如Dialect的使用只提供了如何创建Dialect的方法,没指出Diale...

redraiment
2014/04/25
329
5
关于写handler自定义routes

大家好, 我是从ror那边新转过来jfinal开发的, 有一些关于多级routes的问题想问问, 正如之前曾经有人问过的关于自定义routes的问题, 现在最新版本貌似已经完全支持了restful了. 在路由上面, 想...

cl293
2015/02/12
129
1
关于jfinal在idea中的部署问题

@JFinal 你好,想跟你请教个问题: 请问在intellij idea 里怎样使配置的路由立即生效? 我使用的是 idea + tomcat + jrebel,发现每次注册新的contrller都必须重启tomcat啊。 ps:刚刚看了一下...

hikevin
2013/12/10
5.2K
10
JFinal + swfupload Session失败的问题

@JFinal 你好,想跟你请教个问题: JFinal + swfupload 上传文件时,Session失败的问题不知JFinal如何解决的? 以前用Struts的时候,只要在请求地址后加上jsessionid=xxx就可以方便解决不同会...

车开源
2014/04/10
313
2

没有更多内容

加载失败,请刷新页面

加载更多

微服务分布式事务实现

https://www.processon.com/view/link/5b2144d7e4b001a14d3d2d30

WALK_MAN
今天
2
0
《大漠烟尘》读书笔记及读后感文章3700字

《大漠烟尘》读书笔记及读后感文章3700字: 在这个浮躁的社会里,你有多久没有好好读完一本书了? 我们总觉得自己和别人不一样,所以当看到别人身上的问题时,很少有“反求诸己”,反思自己。...

原创小博客
今天
4
0
大数据教程(9.5)用MR实现sql中的jion逻辑

上一篇博客讲解了使用jar -jar的方式来运行提交MR程序,以及通过修改YarnRunner的源码来实现MR的windows开发环境提交到集群的方式。本篇博主将分享sql中常见的join操作。 一、需求 订单数据表...

em_aaron
今天
3
0
十万个为什么之什么是resultful规范

起源 越来越多的人开始意识到,网站即软件,而且是一种新型的软件。这种"互联网软件"采用客户端/服务器模式,建立在分布式体系上,通过互联网通信,具有高延时(high latency)、高并发等特点...

尾生
今天
3
0
Terraform配置文件(Terraform configuration)

Terraform配置文件 翻译自Terraform Configuration Terraform用文本文件来描述设备、设置变量。这些文件被称为Terraform配置文件,以.tf结尾。这一部分将讲述Terraform配置文件的加载与格式。...

buddie
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部