Struts 2 的动作模型

一直以来都没有很好地考究 Struts 2 用的是怎样一个模型。多数人认为 Struts 2 的精髓在于拦截器,如果单从 Struts 2 的包来看似乎的确这样,因为很多 Action 部分的包都由 xwork 提供了。
 
但我觉得,这个 interceptor (拦截器)的概念实际上和 Servlet 规范中的 Filter 十分像,Struts 1 是对 Servlet 规范进行一封封装,实际上架构还是依循 Servlet 而建立的。而 Struts 2 的架构和 Struts 1 类似吗?
 
就我们所知道的,Struts 2 是 Struts 1 和 WebWork 的结合,实际上更偏向于 WebWork 框架(具体历史不考究了,只看软件包结构可以看出)。Struts2 框架实际上并不直接封装请求的信息传递给 Java 类,而是通过配置文件和参数约定把逻辑类的参数和请求参数绑定起来,使用统一的控制器(请求分发器)作为边界。因此,这个模型和 Struts 1 的架构来说差异还是很大的。
 
Strus 1 的 Action 控制器实际上只是对 Servlet 进行了封装,分离了控制和视图(实际上用 Servlet 控制,转发给 JSP 同样可以做到),外加上 ActionForm 对标单提交和校验作了封装。
 
而 Struts 2 采用的实际上是另一套模型。大家知道为了复用而让请求和 Action 解耦,但是却没想过这时候 Action 应该算是什么。我觉得这个改变促使其对 Struts 1 下的 MVC 模型进行了补充,把 Struts 1 中的 Action 整合为组件,既可以模仿 Struts 1 的方式写 Action ,又可以把服务类直接当作 ction 。
 
Struts 2 的组件化实际上可以使得边界类可以不再出现(理想情况下),达到代码复用的最好情况。

Enterprise JavaBean 笔记:JBoss安装与配置

记得刚上大学不久,学 Java 的时候,就曾经听说过 Enterprise JavaBean 了,但一直没有试用过,因为,从这个名字看来,很容易和 JavaBean 混淆。而在 Enterprise JavaBean 介绍中提到的分布式、可伸缩的网络模块的时候,又显然和作为对象数据存取规范的 JavaBean 规范不同。Enterprise JavaBean 更像是一个遵循 JavaBean 规范的网络服务提供者。一直以来用PHP来开发网站,对 Java 这边规范的 MVC 开发模式还是颇为羡慕。而这个学期就开始 EJB 的旅程了~

这个系列博客大概会持续讨论关于 Enterprise JavaBean 的问题,欢迎大家提出意见。 :-)

环境选择:

这里选择 NetBeans + JBoss AS 5.1 作为开发环境。选择 JBoss AS 5.1 是因为课程需要,其实 NetBeans + Glassfish 的组合可能更好。

  1. JBoss AS 5.1:可以在http://sourceforge.net/projects/jboss/files/JBoss/JBoss-5.1.0.GA找到,下载后解压即可。
  2. Netbeans 7.2:可以在http://www.netbeans.org下载NetBeans 7.2 for Java EE(按提示安装即可,其中可以选择安装安装包带上的Glassfish 3.2.2,Java EE 6兼容服务器)。

配置:

NetBeans安装成功后,如果开始时选择了Glassfish,那么已经有一个Java EE 6兼容服务器。不过,这里选择课堂要求的JBoss Application Server 5.1。

  1. 在NetBeans的服务器平台上,点击“工具”->“服务器”。
  2. 在服务器窗口点击“添加服务器”,按提示添加“JBoss Application Server”,选择JBoss的安装根目录即可。
  3. 启动JBoss AS 5.1,如果出现“java.lang.IllegalArgumentException: Wrong arguments. new for target java.lang.reflect.Constructor expected=[java.net.URI] actual=[java.io.File]”的提示,大多是因为注入AttachmentStore类的时候没有指定注入类型导致。
    编辑%JBOSS_HOME%/server/default/conf/bootstrap/profile.xml文件:
    ……
    
    <bean name="AttachmentStore" class="org.jboss.system.server.profileservice.repository.AbstractAttachmentStore">
        <constructor>
            <parameter>
                <inject bean="BootstrapProfileFactory" property="attachmentStoreRoot" />
            </parameter>
        </constructor>
    ……

    修改<parameter>标签为:

    ……
            <parameter class="java.io.File">
    ……

    即可。

测试:

  1. 新建 EJB 模块。新建 Stateless Session Bean(无状态会话Bean),新建向导创建Local(本地)接口的无状态会话 Bean,添加一个 Bussiness Method (在编辑区域内,按 alt + Insert,即弹出生成代码框,选择 NetBeans 的原因一部分就是因为 NetBeans 挺方便的代码生成功能)。
    代码看起来像:
    package hello.greeting;
    
    import javax.ejb.Stateless;
    
    @Stateless
    public class Greeting implements GreetingLocal {
    
        public String greet(String name) {
            return "Hello, " + name + "!";
        }
    }

    接口如下:

    package hello.greeting;
    
    import javax.ejb.Local;
    
    @Local
    public interface GreetingLocal {
    
        public String greet(String name);
    }
  2. 新建 Web 应用程序项目,编辑项目属性,添加 上面创建的 EJB 模块到 Libraries (库)中,不需要包括在编译包中。这里只是为了让这个 Web 应用程序知道会话 Bean 的接口而已。
    编辑 index.jsp,在空白处添加对 EJB 的引用:
    <%@page contentType="text/html" pageEncoding="UTF-8"%>
    <%@page import="javax.naming.*" %>
    <%
    InitialContext context = new InitialContext();
    GreetingLocal bean = (GreetingLocal) context.lookup("Greeting/local");
    out.println(bean.greet("Jack"));
    %>
  3. 发布 EJB 模块和 Web 应用程序来测试吧~

离开R-Tank

9 月 22 日了,记得去年年末的时候,我们开始了 R-tank 项目,经过两次的重构,并尝试了三种不同的实现方式,即使这个项目最终未能吸引很多人的注意,但也积累了不少的经验。

由于一直以来都是独立开发,所以并没有在博客上写起关于 R-tank 项目的细节。R-Tank 项目其实是由 Lu 学长发起的以 WIFI 远程控制为基础的智能玩具项目。想法其实在国外已经有实现了,不过在去年的国内,还是一个很新奇的想法。于是,我们(LuDa、WeiJianwen、GuoZiyi等)开始了这个项目的构建。

看着以前简陋的草图还觉得挺好笑的:

架构草图

不过可以体现出我还是很努力地尝试把这个项目构建好。上图中的所有线表示一个 TCP 连接或者内部调用,可惜当时没分清,所以都用实线了。这幅图应该是最后一次维护的时候画的,已经从 UDP 转移到 TCP 来管理连接。

简要地说明一下,NetBase 类是一个抽象的基类,负责提供一个消息传达者的作用,维护一个。里面有 3 个方法,分别是发送往第几个客户端的。然后交给子类来实现不同的网络操作。这样看上去的架构还是挺完善的,不过,可惜的是,不一致的网络消息发送导致维护成本的提高,以及用 TCP 导致广播消息的困难,都是这个项目的挑战;另一方面,消息协议的确定也是一个难题,如何同步游戏中各种数据也是一个难题。


一些小插曲:

  • 一开始的 UDP 本来也可以很好地工作,不过要耗掉很多电量用于同步。而用 UDP 实现注意的问题或许就在于同步信息的时机:不能频繁发送,也不能只发送一次。
  • TCP 与 UDP 论战,最后由于不了解 UDP 如何“不可靠”。于是,放弃 UDP 采用 TCP 。并用测试连接来确定网内的其他客户端。确实获得了更好的连接效率。
  • 有点错误地用 Java NIO 来实现 TCP 连接。而不阻塞的 IO 对于无状态网络服务来说应该是可用的,但不是所有的网络服务都是无状态的。而且,NIO 强调数据的可控制性,大量应用 Buffer 类,是否完成是不确定(因为不阻塞),所以考虑情况很多。导致这个项目在这个阶段耗费了大量时间。
  • 确定 NetBase 类的稳定 API 。采用原始 Java Socket 进行最后实现。

2012-8-23

昨天又回到沈阳了,准备新学期的学习。这个学期要学的内容真的很多,包括大学里学分最多的课都在这个学期了。三门重点课程更是我所希望了解的。不过,暂时对此还没有很多计划。不过可以肯定,必须更勤奋了。

在上个学期末,给这个假期定的计划是放松。不过,实际上真的放松不了。回家前定的两个项目,一个为视点网写的Android客户端以及学院希望我们提交的WindowsPhone程序。前一个错误地估计了HTML 5的普及,现在得用Android原生界面重写。大概在新学期的时候可以完成。

在完成Android的程序的时候,也发现了不少有趣的内容,例如,超越所有构造函数的匿名构造函数:

class TestClass {
	private int a;
	private Integer b;
	private long c;

	{
		a = 1;
		b = new Integer(a);
	}

	public TestClass(long c) {
		this.c = c;
	}
}

中间用 { ... } 括起来的就是一个匿名的构造函数,独立于所有构造函数,无论调用哪个构造函数,都会先调用这个构造函数。挺有用的吧。对于静态变量,可以用 static { ... } 来初始化。

另一些发现则是和Android的Looper、Handler、Message类框架有关的类。Looper类的构造和Thread类密切相关,而且Thread类的静态方法Thread.currentThread()可以唯一确定一个Thread然后制作一个线程池进行管理。Looper类的消息机制也建立与此。而且,利用Handler类的消息延迟发送功能,可以巧妙地构建动画,而且这个动画还在当前UI线程,可以安全地更新UI。

 

Java NIO笔记 Channel

记得第一次做计算机网络的实验课的时候,我用多线程实现了一个HTTP服务器,然后对能够同时对应多个客户端的读请求十分满意。没想过线程的开销问题。不过,后来在手机端设计一个需要同步读写的服务器的时候,才突然发现,要实现维护多个线程的同时,同步读写并不是那么容易的事情。

文中有什么问题,希望指出完善。:-)

Java NIO笔记 ByteBuffer

最近在做一个网络相关的应用,考虑到效率问题,参考了NIO库的实现。NIO库大概更接近C/C++的Socket编程,采用非阻塞的设计,以及Selector事件驱动的设计,感觉还是不错的实现。以下内容只是整理作为参考,可能有不少错误,希望对大家有帮助。如果有更好的实现方法,或者我理解上的一些问题,希望指出,共同学习。

TankII 一点小更新

发现又很久没更新博客了。

用Box2D重写了TankII,然后绘制则用了AWT和类似于Page flipping的技术,在Windows上绘制看起来是挺流畅的。至于可能有点慢,并不是画面跟不上,而是我设的速度限制而已。这次的版本,虽然地图做的还是很次,但代码管理上应该有不少进步了。眼看就开学了,不知道还有多少时间会像现在这样慢慢地开发程序。不过,有期待的就会有收获。

快速按坐标分类的方法还是没找到,不过现在的绘制速度还可以,于是也没找别的方法来提高绘制速度了。有空还是得转向OpenGL,现在用的Java2D虽然也是跨平台,但是不一定得到很好的硬件加速。而且Android的开发似乎也会是OpenGL的。用OpenGL对于平台切换也简单点吧。

下载:http://www.box.com/s/17djfttkvrovpn05ognv 源码应该会在不久后也上传的,现在没整理好,就没上传了;大概也不是很多人会看……

今年回来,还是有点无奈。不过走到现在,其实有很多人帮助过我了,要知道感恩生活,才知道快乐。

Smile

上学期末的时候写的一个小程序,用于练习 Java2D 编程。我觉得图形编程还是挺有趣的。

记得学期末的时候,某人发短信过来说对编程还是没很多兴趣。嘿嘿,其实我觉得其实编程就不过是实现生活中的事情而已。于是写了这个小程序。希望见到这个小程序也会开心一下吧。

也应该说一下。版权不在我这里,我只是用 Java 参考 Xorg 中的 Xeye 示例程序、加入一些鼠标事件处理写的。

大家可以参考一下源码吧。虽然现在看回原来写的代码,我也觉得写得不是很好(思路不清晰,但代码不长)……需要注意的是,我使用了 JRE 7 的新特性,所以,必须在 JDK 7 下编译。
实际上是因为加入了这些语句:

frame.setShape(new  
	java.awt.geom.Ellipse2D.Double(
		0, 0, 
		frame.getWidth(), 
		frame.getHeight()));
try {
	frame.setOpacity(0.8f);
} catch(Exception ex) {
}

其实还有一些动态效果的。截图就不多发了。

嗯,其实我希望大家见到这个程序都会开心。

下载:http://www.boxcn.net/shared/il58j4fm96mkj6nj0s69

源码:http://www.boxcn.net/shared/umi549k2vjsir2lzc25a

TankII update 1.2.1

考虑到以前同学说过的按键问题,现在修正了一下。不过,要更准确地控制坦克的炮筒位置,就需要两副方向键。而现在 ASDW 和 方向键 (功能和ASDW一样)都用了,于是想到 VI 的键盘模式 HJKL 。

修改记录:

  • 更改键设定,H、J、K、L 控制坦克炮筒角度。

所以,现在是,举例来说,无论现在炮筒在什么方向,只要按了 H 那么就会沿最短的路径向左( J 对应“向下”、K 对应“向上”、L 对应“向右”)。

这个修改太小了。所以就只是更新一个小版本号吧。基本上就修改了 TankKeyListener 类。

下载地址:http://www.boxcn.net/shared/1v7lhfdfumj08sxoktsl (其实下载地址没怎么变,用了 box.net 的文件版本功能)

Log 2011.8.11

昨天在回顾 POJ 3349 的时候,想起了散列表(Hash Table)的数据结构。提醒了我在 TankII 的对象碰撞的时候应该处理的一些事情,对需要检测的对象先进行粗略的分类然后再继续判断,这样的话,对 TankII 设计里可以减少实际碰撞检测的时候。这需要的就是一个散列函数了。这个散列函数应该满足的是把几个顶点坐标处理成一个数,如果所有坦克都可以通过这个数判断粗略分出可能造成碰撞的坦克的话,游戏的刷新间隔应该可以缩减到更短,有利于其他额外的绘制处理。但这个散列函数也不是容易找的。也只是我的一个很原始的想法而已。而且,散列表应该是对应静态分类(储存的对象基本是确定的)的,而我所需要的是动态分类,也是一个难题,还在探索中……

现在总算是对数据结构有些初始的理解了。

还有就是对 Java2D 的一些不满,似乎 Java2D 中的结构都是静止的结构,没有考虑到这个对象用作动画的一部分?如 Rectangle2D 这样的对象,虽然提供了 getCenterX() getCenterY() 这样的方法,但对 Rectangle2D 内顶点的修改却很少涉及。但看 OpenJDK 中 Rectangle2D 的实现,其实是可以增加额外的修改图像的方法的。而且碰撞检测也是基于变量的,这样修改也不会造成性能的问题吧?只有一个不方便的 setRect(doulbe x, double y, double w, double h) 来进行整体修改。当然,也是一个解决办法。相比起 Pygame 的 Rect 类就差远了。