0
点赞
收藏
分享

微信扫一扫

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 条评论