文档章节

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

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

#程序员薪资揭榜#你做程序员几年了?月薪多少?发量还在么?>>>

最近在用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 波总指导。





© 著作权归作者所有

玛雅牛

玛雅牛

粉丝 487
博文 115
码字总数 28117
作品 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
119
1
JFinal的ActiveRecord有更详细的文档吗?

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

redraiment
2014/04/25
345
5
关于jfinal在idea中的部署问题

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

hikevin
2013/12/10
5.6K
10
OSC上关于Jfinal的提问整理(一)

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

木川瓦兹
2013/04/23
7.9K
21
关于写handler自定义routes

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

cl293
2015/02/12
137
1

没有更多内容

加载失败,请刷新页面

加载更多

kafka重要概念与集群重点配置详解

重要概念 broker 一个broker就是一个kafka实例,负责接收、转发、存储消息,kafka集群就是由多个broker组成。 topic kafka的topic是一个逻辑概念,就是对消息分组、分类,便于区分处理不同业...

trayvon
今天
44
0
在树莓派里搭建 Lighttpd 服务器

Lighttpd 像 Ngnix 一样,是被设计运行在低内存,低 CPU 负载的设备上,它们都非常适合在树莓派上运行。 本文将介绍如何在树莓派上运行基本配置的 Lighttpd ,以及如何与 PHP-FRM 一起使用。...

良许Linux
今天
21
0
Service Mesh 高可用在企业级生产中的实践 | 线上直播回顾

Service Mesh Virtual Meetup 是 ServiceMesher 社区和 CNCF 联合主办的线上系列直播。本期为 Service Mesh Virtual Meetup#1 ,邀请了四位来自不同公司的嘉宾,从不同角度展开了 Service Me...

SOFAStack
今天
37
0
word转pdf软件有哪些?word转pdf软件怎么操作?

虽说日常生活中,很多人写报告写策划都依然会使用word程序,但是严格来说,word却并非是唯一常用的办公软件,就比如说pdf,就越来越受年轻人的欢迎了,那么经常用电脑办公的你是否知道,其实...

开源86
今天
48
0
Java创建对象的过程(类实例化)

1.检查类是否被加载。 当虚拟机遇到new指令后,会先去常量池检查有没有该类的符号引用,并且检查这个类有没有进行加载、解析、初始化过,没有就先执行类加载过程。 2.为对象分配内存空间*。 ...

曦鱼violet
今天
26
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部