文档章节

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

玛雅牛
 玛雅牛
发布于 2013/06/04 12:39
字数 1410
阅读 747
收藏 5
点赞 1
评论 7

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





© 著作权归作者所有

共有 人打赏支持
玛雅牛

玛雅牛

粉丝 477
博文 108
码字总数 27153
作品 4
高级程序员
加载中

评论(7)

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

引用来自“JFinal”的评论

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

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

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

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

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

JFinal ⋅ 04/28 ⋅ 129

JAVA 极速WEB+ORM框架 - JFinal

JFinal 是基于 Java 语言的极速 WEB + ORM 框架,其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展、Restful。在拥有Java语言所有优势的同时再拥有ruby、python、p...

JFinal ⋅ 2012/03/18 ⋅ 496

kiplinglee/jfinal-ioc-plugin

jfinal-ioc-plugin JFinal 依赖注入插件 1.在JFinalConfig文件中配置IOC插件 @Overridepublic void configConstant(Constants me) { // 创建controller实现依赖注入me.setControllerFactory......

kiplinglee ⋅ 04/13 ⋅ 0

基于注释自动生成 API 文档 - Regan API

Regan API 前言 Regan API 项目是基于注释自动生成api文档,很大缩短了开始与后期维护API接口文档的时间。 Regan API 利用jdk提供的Doclet 类读取文档注释,可手动配置需要读取的文件,同时增...

Jeff_Regan ⋅ 06/15 ⋅ 0

Jboot v1.4.9 发布,核心 JFinal 升级到 3.4 最新版本

Jboot 是一个基于 JFinal 和 Undertow 开发的微服务框架。提供了 AOP、RPC、分布式缓存、限流、降级、熔断、统一配置中心、Opentracing 数据追踪、metrics 数据监控、分布式 session、代码生...

理工男海哥 ⋅ 05/03 ⋅ 0

jfinal-admin 3.2 发布,beetl 模板升级到 2.7.14

jfinal-admin 3.2 版本正式发布啦。 基于JFinal的后台管理系统,采用了简洁强大的JFinal作为web框架,模板引擎用的是beetl,数据库用mysql,前端bootstrap框架。 演示地址 http://jad.yxyun...

IT小香猪 ⋅ 04/19 ⋅ 0

JFinal 1.6可以无缝升级到最高哪个版本?

大约在JFinal 1.6的时候做了一个项目,一直运行至今。中途没升级也没做功能扩展,时至今日,需要做功能升级了,但是时隔太久远,基本不记得JFinal这个框架一路过来的升级过程了。所以想请求帮...

车开源 ⋅ 05/11 ⋅ 0

jfinal兼容oracle integer字段

为什么oracle integer字段用了number(3,0),jfinal生成的字段还是BigDecimal @jfinal

tianxia007 ⋅ 05/02 ⋅ 0

JFinal如何使用JNDI连接数据库

想用JNDI的方式连接数据库,但是JFinal中用的是DruidPlugin和ActiveRecordPlugin,没有看到用DataSource的地方?请问JFinal支持JNDI连接方式吗?怎么具体实现?...

zqq3436 ⋅ 06/11 ⋅ 0

JFinal(1.6)在Validator里执行过getFile,在Controller里取不到文件

JFinal在Validator里执行过getFile之后Validator中是可以取到文件及有关信息,但是在Controller里取不到文件,是不是我的姿势不正确?@JFinal

车开源 ⋅ 05/15 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

java软件工程师简历中项目经验怎么写?

作者:暗灭 链接:https://www.zhihu.com/question/20695310/answer/180691302 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 直接给你贴个我自己的简历...

颖伙虫 ⋅ 25分钟前 ⋅ 0

Confluence 6 恢复一个站点有关使用站点导出为备份的说明

推荐使用生产备份策略。我们推荐你针对你的生产环境中使用的 Confluence 参考 Production Backup Strategy 页面中的内容进行备份和恢复(这个需要你备份你的数据库和 home 目录)。XML 导出备...

honeymose ⋅ 今天 ⋅ 0

JavaScript零基础入门——(九)JavaScript的函数

JavaScript零基础入门——(九)JavaScript的函数 欢迎回到我们的JavaScript零基础入门,上一节课我们了解了有关JS中数组的相关知识点,不知道大家有没有自己去敲一敲,消化一下?这一节课,...

JandenMa ⋅ 今天 ⋅ 0

火狐浏览器各版本下载及插件httprequest

各版本下载地址:http://ftp.mozilla.org/pub/mozilla.org//firefox/releases/ httprequest插件截至57版本可用

xiaoge2016 ⋅ 今天 ⋅ 0

Docker系列教程28-实战:使用Docker Compose运行ELK

原文:http://www.itmuch.com/docker/28-docker-compose-in-action-elk/,转载请说明出处。 ElasticSearch【存储】 Logtash【日志聚合器】 Kibana【界面】 答案: version: '2'services: ...

周立_ITMuch ⋅ 今天 ⋅ 0

使用快嘉sdkg极速搭建接口模拟系统

在具体项目研发过程中,一旦前后端双方约定好接口,前端和app同事就会希望后台同事可以尽快提供可供对接的接口方便调试,而对后台同事来说定好接口还仅是个开始、设计流程,实现业务逻辑,编...

fastjrun ⋅ 今天 ⋅ 0

PXE/KickStart 无人值守安装

导言 作为中小公司的运维,经常会遇到一些机械式的重复工作,例如:有时公司同时上线几十甚至上百台服务器,而且需要我们在短时间内完成系统安装。 常规的办法有什么? 光盘安装系统 ===> 一...

kangvcar ⋅ 昨天 ⋅ 0

使用Puppeteer撸一个爬虫

Puppeteer是什么 puppeteer是谷歌chrome团队官方开发的一个无界面(Headless)chrome工具。Chrome Headless将成为web应用自动化测试的行业标杆。所以我们很有必要来了解一下它。所谓的无头浏...

小草先森 ⋅ 昨天 ⋅ 0

Java Done Right

* 表示难度较大或理论性较强。 ** 表示难度更大或理论性更强。 【Java语言本身】 基础语法,面向对象,顺序编程,并发编程,网络编程,泛型,注解,lambda(Java8),module(Java9),var(...

风华神使 ⋅ 昨天 ⋅ 0

Linux系统日志

linux 系统日志 /var/log/messages /etc/logrotate.conf 日志切割配置文件 https://my.oschina.net/u/2000675/blog/908189 logrotate 使用详解 dmesg 命令 /var/log/dmesg 日志 last命令,调......

Linux学习笔记 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部