Struts2 与 OGNL ( 投影 过滤 ) 以及pojo依赖注入的方法

ORM(这里我们以hibernate为例子)的对象关联使得对象之间的导航变得十分清晰与明了。对开发员而言,感觉到的是优雅与实用。
想下通过Orm的关联,我们不用再和sql打交道(这个也不一定,我的看法是绝不勉强用哪一个,哪一个合适,就用哪一个),
我们可以相当的方便拿到与之相关的对象。当然取出来的数据还是要显示在页面上给用户看的。这里便是Ognl显神通的地方了。
Ognl是个脚本语言,弱类型,可以运行时判断对象类型。和Struts2标签结合相当不错。之前 javaeye有个struts2系列教程,作者对Ognl的介绍就很不错。有空大家可以看看。
今天我们研究一个案例。
有个站内信类,我们叫它Message,如下:
   public class Message implements java.io.Serializable
{

// Fields

private Integer id;
private User user;//收件人
private Integer fromId;//发信人Id
}


由于信件Message和User类有两个关联,但是当时设计的话只是用了一次一对多关联,发信人则直接使用其Id,这就丧失了导航的优势
但我在页面显示相关信息的时候 比如我想显示发件人的头像 headIcon,姓名等,但Message并没有这个字段,同时也无法通过 getUser()之类的导航来得到,,也就是我们必须通过显示连接查询获得fromId对象。
于是我想到了另外一个解决方案
也就是在页面通过Message中的fromId取得会员对象,通过加载的User对象取得headIcon.
这时我写了一个新的类,我们称之为PageHelper 代码如下:
package com.snail.commons.util;
import org.apache.log4j.Logger;
import com.snail.commons.basedao.IBaseDAO;

public class PageHelper
{
private IBaseDAO baseDAO;//一个负责持久层的对象
public PageHelper(IBaseDAO baseDAO)
{
this.baseDAO=baseDAO;
}
private final static Logger logger=Logger.getLogger(PageHelper.class);
/*
clz:String 类的全名(包括包名,例如import com.snail.commons.beans.User)
id :integer 需要加载的id
*/
public Object loadEntity(String clz,Integer id)
{
Class temClz=null;
try {
temClz=Class.forName(clz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Object obj=baseDAO.findEntity(temClz, id);
return obj;
}
}

通过这个类,我们就能动态加载需要的类,并且取得相应的持久化对象。
我们看下在页面如何调用:
	<s:property value="(new com.snail.commons.util.PageHelper(baseDAO)).loadEntity('com.snail.component.beans.User',fromId).headPic"/>


我们首先实例化PageHelper类,需要注意的是,在Ognl表达式中,必须使用全类名,接着直接调用其方法。
当然也可以将loadEntity定义为静态方法,相应的,你应该改成
<s:property value="@com.snail.commons.util.PageHelper@loadEntity('com.snail.component.beans.User',fromId).headPic"/>

当然对于上面的案例来说,我们可以通过构造方法传入baseDAO对象(第一种调用方法),也可以通过(参数传递baseDAO,不过这里的例子没有体现)我这里只是作为Ognl静态方法调用的一个示例。
(个人感觉上面的方案破坏了MVC的封层,这相当于数据库经过Service层直接到了展示层。同事效率也大了折扣,假设我要显示一百条信息,必须调用一百次Sql操作才能取出
相应的User对象。有点像n+1问题);
Rails 里面有个ApplicationHelper 帮助程序,我觉得很好,加强了页面的表现能力,我们可以在页面调用ApplicationHelper中的任何公开方法,比如实现一些截取规定长度字符等功能,当然在struts中我们可以通过标签实现,或者通过已有标签组合实现类似功能,不过都比较麻烦,哪有调用一个静态方法来得爽?
ognl就为我们提供了类似于ApplicationHelper 的实用类。
比如我们可以新建一个类假设叫PageHelper:里面有个静态方法==》

public static String substring(String str,List params)
{

if(params.size()==1)
{
Integer end=(Integer)params.get(0);
return str.substring(0,end<=str.length()?end:str.length());
}
else if(params.size()==2)
{
Integer start=(Integer)params.get(0);
Integer end=(Integer)params.get(1);
return str.substring(start,end<=str.length()?end:str.length() );
}
return str;
}


然后我们可以直接在页面上调用
<s:property value="@com.snail.commons.util.PageHelper@sustring('abc',{})"/>

这样便实现了字符截取功能。另外上面代码中的"{}",代表新建的一个List对象,很简洁吧,恩 ognl具有很多动态语言的特性。
另外我很想知道的是,如何在一个普通的Pojo类中拿到spring的bean,而且该类是我们自己直接实例化的。如果哪位大大知道的话,不妨告知一声。([color=orange]后来才理清头绪,如果解决了这个问题,也就解决了富领域模型的问题。
关于富领域模型问题,javaeye讨论了很多,到hibernate模块有很多精华好贴。我这里不讨论其优劣,只是提几句实现的方案:
第一种:
ps:也是最好的方案
在spring2.0之后,已经通过aspectj支持静态织入持久化工具类,我这里不展开,大家可以参看spring文档(或许我会另起一文详细说说步骤),不过唯一不爽的是要个什么
设置 javaagent=aspectj jar包路径
第二种:
ps:比较繁琐,但对于新新项目也是比较保险,可控性高一点的
在pojo类设置一个set方法,每次使用该pojo之前,给它set一个持久化工具类,之后pojo类里面的方法就具有了持久化功能,或者严格点,给每个Pojo类用到持久化工具类的方法全部以持久化工具类为其中之一的参数
第三种:
ps:很有技巧性
不管你pojo是谁产生的,你的静态成员总是所有实例共享的吧,我如果给你的静态成员设置上持久化工具类,那所有的pojo实例都可以共享吧。
这种实现方案有两个:
no1 :通过spring拦截pojo在加载时的静态方法,赋值。这样省事,不过
no2 :有三个类,A, B,C, A用spring加载,实现ApplicatioContextAware接口,注入context.类B有一个静态域,该静态域通过set属性注入。这个可能不是很好理解,我们举个例子,

@Component
public class B
{
private static ApplicationContext context;


@Autowired(required = true)
public void setUserAccessor(A a) {
B.context = a.context;
}
}


这样应该容易理解一些
接着在C类中我们可以直接使用B中的静态域,很棒是吧
这样我们就可以将很多东东注入到不归spring管理的的自己实例化的类里面去了。
再次ps:我个人最喜欢这个方案最简洁
第四种:
ps:hibernate自己的解决方案
可以参看https://www.hibernate.org/182.html
这种方案就是通过hibernate拦截器在加载pojo类的时候注入。

当然还有很去哦其他的方案,大家可以google一下。

[/color])

Ognl的另外一个比较重要的用法是投影与过滤。网上好像讲的比较少。我在这里提一下。
第一个示例是 如何迭代一个数字,最简单的比如分页,我的pageSize=7,如何迭代出七个Option呢,如下。
<select>
<s:iterator value="pageSize.{#this}" status="sta">
<option> <s:property value="#sta.index+1"/></option>
</s:iterator>
</select>


上面有个重点: pageSize.{#this} 这是个过滤操作,得到的是一个集合。this指当前正在迭代的对象。前面的#不用说了,是ognl本省语法需要的。

假设我想迭代出大于3的几个数字,
这个时候可以这样pageSize.{#this>3}

结合ongl的过滤与投影操作,可以使得页面也有强大的逻辑操作以及过滤功能。
我们在看一个稍微复杂一点的例子:
三个类
Gequ(歌曲) ,Gequzhuanji(歌曲专辑),Gequgequzhuanji(歌曲歌曲专辑 中间表)。
Gequ=> Gequgequzhuanji 一对多关系
Gequzhuanji=> Gequgequzhuanji 一对多关系
现在我拿到了一个Gequgequzhuanji的集合,对这个集合我想显示出拥有者为master的专辑的名称,而且只要第一个。
下面的代码可以实现:

<s:property value="gequgequzhuanjis.{^#this.gequzhuanji.huiyuan.name eq 'master')}[0].zhuanJiMing" default="单曲"/>

我们来解释一下:
显然gequgequzhuanjis.{}是一个过滤操作。过滤得到的也是一个集合,这个结合需要[n]操作拿到某个元素,最终通过.拿到起属性值。
就这么简单


恩 不知道讲清楚了。大家可以再参考下ognl的官方文档。个人觉得Ognl是个强大的脚本语言。支持常用的一些操作符,比如基本类型使用==等,对象比较可以使用
eq 之类的。在传统的MVC结构中,V层有点瘦客户端的感觉,有时也可以让他胖起来!(*^__^*) 嘻嘻……
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值