`
rongxh2010
  • 浏览: 48929 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

探讨Non-Struts的Java Web开发

阅读更多

      开发Java Web项目时,如果不采用Struts、WebWork、SpringMVC等MVC框架,而使用原始的Servlet API时,该怎么开发呢?

      Struts 1.x采用了一个有“总控制器”作用的Servlet处理所有的请求,而Struts 2.x则采用了Filter。两者各有优缺点。我个人比较喜欢Struts2的一种URI约定形式,类似于“.../member!add.action ”、“.../abc/articles!list.action”的形式。在这里,我们本例也采取了Struts2的这种URI约定形式。即:

目录/目录/.../类名!方法名.后缀

      我们也采用Struts2的Filter主控方式,开发一个javax.servlet.Filter的子类,用来处理所有请求,再在这个Filter里获取访问的URI,从URI字符串中提取业务实体类和访问方法,然后根据Java的反射API,动态地创建类对象,动态地执行相应的业务方法。这个Filter我命名为"org.mvcgo.filter.ControllerFilter"。

在web.xml文件中,配置这个Filter,代码如下:

<filter>
	<filter-name>ControllerFilter</filter-name>
	<filter-class>org.mvcgo.filter.ControllerFilter</filter-class>
	<init-param>
		<param-name>suffix</param-name>
		<param-value>go</param-value>
	</init-param>
	<init-param>
		<param-name>encoding</param-name>
		<param-value>utf-8</param-value>
	</init-param>
	<init-param>
		<param-name>package</param-name>
		<param-value>org.mvcgo.action</param-value>
	</init-param>
</filter>
<filter-mapping>
	<filter-name>ControllerFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

 

      上面的代码中,ControllerFilter内置参数suffix、encoding分别表示URI的后缀和编码,package表示“根包”,所谓“根包”,这是学习了Struts2的URI命名规则,如上面配置的"org.mvcgo.action",若有个Action类名为"org.mvcgo.action.infos.MemberGo.java",则相应的访问路径为.../PROJECT_NAME/infos/member.go。这个参数都会在我写的"org.mvcgo.filter.ControllerFilter"类中用到。

      下面,我们来分析一下“org.mvcgo.filter.ControllerFilter”类,类的头部代码如下:

package org.mvcgo.filter;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mvcgo.action.BaseGo;
import org.mvcgo.utils.StringUtil;

public class ControllerFilter implements Filter{...}

       在ControllerFilter里定义的成员属性如下:

	private FilterConfig filterConfig;
	
	//配置ControllerFilter时,对应于init-param中的param-name
	private static final String SUFFIX_NAME = "suffix";			//前缀的"param-name"
	private static final String ENCODING_NAME = "encoding";		//编码的"param-name"
	private static final String PACKAGE_NAME = "package";		//Go的基包的"param-name"
	
	//init-param的默认初始化值
	private String SUFFIX_VALUE_DEFAULT = "go";			//默认前缀
	private String ENCODING_VALUE_DEFAULT = "UTF-8";		//默认编码
	private String PACKAGE_VALUE_DEFAULT;

 

 重写Filter的init方法,执行初始化:

    /**
     * 重写Filter的init方法,执行初始化操作
     */
	public void init(FilterConfig filterConfig) throws ServletException {
		this.filterConfig = filterConfig;
		
		//初始化Go访问的后缀
		String suffix = filterConfig.getInitParameter(SUFFIX_NAME);
		if(null != suffix && !"".equals(suffix)) {
			this.SUFFIX_VALUE_DEFAULT = suffix;
		}
		
		//初始化编码
		String encoding = filterConfig.getInitParameter(ENCODING_NAME);
		if(null != encoding && !"".equals(encoding)){
			this.ENCODING_VALUE_DEFAULT = encoding;
		}
		
		//初始化Go类的基包
		String basePackage = filterConfig.getInitParameter(PACKAGE_NAME);
		if(null != basePackage && !"".equals(basePackage)) {
			this.PACKAGE_VALUE_DEFAULT = basePackage;
		} else {
			throw new ControllerDeployException("读取web.xml中ControllerFilter的初始化参数失败,请检查" + PACKAGE_NAME + "是否配置正确...");
		}
	}

 

初始化完后,会自动执行doFilter方法,代码如下:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
	
	//强制转换request和response
	HttpServletRequest httpRequest = (HttpServletRequest) request;
	HttpServletResponse httpResponse = (HttpServletResponse) response;
	
	//设置HttpServletRequest的编码格式
	httpRequest.setCharacterEncoding(ENCODING_VALUE_DEFAULT);
	
	//获取请求的URI,如请求http://127.0.0.1:8080/mvcgo/info/user!save.go?id=7,会得到"/mvcgo/info/user!save.go"
	String requestURI = httpRequest.getRequestURI();
	System.out.println("url = " + requestURI);
	
	if(requestURI.endsWith("." + SUFFIX_VALUE_DEFAULT)) {
		String packageClassName = PACKAGE_VALUE_DEFAULT;		//包类名,即类的全名
		String[] segs = requestURI.split("/");
		for(int i = 2 ; i < segs.length - 1 ; i++) {
			packageClassName = packageClassName + "." + segs[i];
		}
		String classMethodSuffix = segs[segs.length - 1];
		String classMethod = classMethodSuffix.substring(0, classMethodSuffix.lastIndexOf("." + SUFFIX_VALUE_DEFAULT));
		String className = null;		//业务类名
		String methodName = null;		//方法名
		if(classMethod.indexOf("!") != -1) {
			String[] methodNamesArray = classMethod.split("!");
			className = methodNamesArray[0];
			methodName = methodNamesArray[1];
		} else {
			className = classMethod;
		}
		//格式化并构建Go类的类名
		className = StringUtil.upFirstChar(className) + StringUtil.upFirstChar(SUFFIX_VALUE_DEFAULT.toLowerCase());		//首字母大写
		packageClassName = packageClassName + "." + className;
		
		try {
			Class c = Class.forName(packageClassName);
			Method[] methods = c.getMethods();
			Object target = c.newInstance();
			
			//业务的Go类,支持继承BaseGo和为不继承BaseDao的两种方式
			if(target instanceof BaseGo) {		//如果该类继承了BaseGo父类
				Method method = c.getMethod(methodName);
				Method initMethod = c.getSuperclass().getMethod("initHttpServlet", HttpServletRequest.class, HttpServletResponse.class);
				//执行BaseGo初始化
				initMethod.invoke(target, httpRequest, httpResponse);
				//执行Go类的业务方法
				method.invoke(target);
			} else {		//不继承BaseGo父类的情况
				Method method = c.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
				//执行Go类的业务方法
				method.invoke(target, httpRequest, httpResponse);
			}
			
		} catch (ClassNotFoundException e) {
			throw new GoNotFoundException("Go类:"+ packageClassName +"找不到", e);
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	} else {
		chain.doFilter(request, response);
	}
	
}

 

从上面的代码可看到,我们的业务BaseGo类支持两种形式,有不继承Go类的情况,如:

package org.mvcgo.action.school;

import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class StudentGo {
	 
	public void add(HttpServletRequest request, HttpServletResponse response){
		System.out.println("student " + request.getQueryString());
	}
	
	public void save(HttpServletRequest request, HttpServletResponse response){
		String category = request.getParameter("category");
		System.out.println(category);
		response.sendRedirect("../test01.jsp");
	}

}

      上面的代码中,每个业务方法,都在ControllerFilter类中注入了HttpServletRequest和HttpServletResponse,这样,我们就可以方便地利用Servlet的API来处理我们网页请求了。

      为了实现更方便的Go编码,于是开发一个可重用的BaseGo类,代码如下:

package org.mvcgo.action;

import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mvcgo.utils.ParameterFilter;

public class BaseGo {
	 
	protected HttpServletRequest request;
	protected HttpServletResponse response;

	public void initHttpServlet(HttpServletRequest request, HttpServletResponse response, Class clazz, Method method) {
		this.request = request;
		this.response = response;
	}
	
}

 

 这时,我们的StudentGo类,继承这个BaseGo,那些业务方法,可直接写成如下形式,而无须带参数。

public void add() throws IOException{
	System.out.println("student " + request.getQueryString());
}

public void save() throws IOException{
	String category = request.getParameter("category");
	System.out.println(category);
	response.sendRedirect("../test01.jsp");
}

 本例,运行服务器,在浏览器中访问时路径为

.../PROJECT_NAME(项目名)/school/student!add.go

.../PROJECT_NAME(项目名)/school/student!save.go

 

      本例小小的代码,无以跟Struts、WebWork待这些大框架比美,仅演示了在脱离大框架的情况下,用Servlet、Filter等原始的J2EE API开发Java Web项目时的一种方案,其中ControllerFilter、BaseGo等类都可继续开发扩充更多功能,以方便Web开发者的使用。

分享到:
评论
8 楼 抛出异常的爱 2010-05-05  
REST风格+google= Gadget
7 楼 plusir 2010-05-05  
后面是hibernate+spring+cxf,前面纯xhtml+css+javascript。把你的业务类上的方法restfull出来供前面通过httpxmlrequest调用。

只是一种想法,还没在具体项目中用过,个人觉得只要注意逻辑划分(业务逻辑由业务方法完成,渲染逻辑由前端脚本完成)这种方式还是值得尝试的。

另外还在考虑更极端的,把存储过程通过ws暴露出来,js来调用。。。。。。。。这种模式很适合业务规则不复杂的项目,类似于福勒在企业级应用架构模式中的定义的事务脚本模式。
6 楼 Zahir 2010-05-04  
这样弄和struts有什么本质的区别吗?
5 楼 gongji 2010-05-04  
Nutz貌似是一个很好的轻量级替代品,或许你用的上
4 楼 heqishan 2010-03-14  
顶。。。期待楼主的后续文章。。。
3 楼 xiaolongfeixiang 2010-03-10  

看完你的帖子,很想知道有没有后续?

你的这个开发有2个问题:

1、和Servlet的API耦合程度过高。

2、每次访问都要Reflect一次,效率不高。可以考虑缓存Class Method。
2 楼 xiaolongfeixiang 2010-03-10  
<p><span style="font-size: small;">和楼主有同样的想法。</span></p>
<p><span style="font-size: small;"><br></span></p>
<p><span style="font-size: small;">我本来想采用SSH2的架构,但是觉得架构太复杂,而自己有只是用最表面的功能。所以,很快放弃了。</span></p>
<p><span style="font-size: small;"><br></span></p>
<p><span style="font-size: small;">最近也在想采用基于<strong> Filter转发+Reflection动态调用</strong> 的方法开发应用。</span></p>
<p><span style="font-size: small;"><br></span></p>
<p><span style="font-size: small;">继续关注!!!</span></p>
1 楼 rongxh2010 2010-03-10  
补充:StringUtil类中的方法:
/**
 * 将字符串首字母转成大写字母
 * @param oldStr
 * @return
 */
public static String upFirstChar(String oldStr) {
	char[] strArray = oldStr.toCharArray();
	char firstChar = strArray[0]; 
	int asciiCode = (int)firstChar;
	if(asciiCode >= 97 && asciiCode <= 122) {
		asciiCode -= 32;
	}
	char upperCase = (char)asciiCode;
	strArray[0] = upperCase;
	String newName = new String(strArray);
	return newName;
}

相关推荐

    天线阻抗匹配 Non-Foster_Reactance_Matching

    本主题主要围绕这一先进匹配技术展开,结合学位论文的研究深度,我们将深入探讨Non-Foster阻抗匹配的基本原理、应用场景以及设计方法。 传统的Foster匹配网络基于电容和电感元件,遵循无源电路的因果关系,适用于正...

    高通平台整个签名NON-HLOS.bin脚本.zip

    "高通平台整个签名NON-HLOS.bin脚本.zip"这个文件包涉及到的是高通平台对特定二进制文件(NON-HLOS.bin)进行安全签名的过程。这个过程对于确保系统的安全性和完整性至关重要,因为只有经过正确签名的文件才能被系统...

    Algorithms for Non-negative Matrix 论文概要

    Algorithms for Non-negative Matrix论文描述希望帮助到大家

    ESP8266 Non-OS SDK API参考

    ESP8266 Non-OS SDK是一个适用于ESP8266芯片的操作系统SDK,它为开发者提供了丰富的API接口,用于开发各种硬件应用。SDK是非操作系统版本,这意味着它不依赖于像FreeRTOS这样的实时操作系统,而是提供一个较为简单的...

    android studio finished with non-zero exit value 1, value 2解决办法

    在使用 Android Studio 进行应用开发的过程中,有时会遇到 `finished with non-zero exit value` 的错误提示。这类错误通常表明构建过程中出现了异常,导致构建流程未能顺利完成。本篇文章将详细介绍这一问题的成因...

    HAP-Specification-Non-Commercial-Version.zip

    HAP-Specification-Non-Commercial-Version.zip是一个包含HAP规范非商业版本详细文档的压缩文件,其中的HAP-Specification-Non-Commercial-Version.pdf是该规范的详细说明。 一、HAP协议概述 HAP协议是Apple公司...

    Science Research Writing for Non-Native Speakers of English

    Science Research Writing for Non-Native Speakers of English第一版英文版pdf

    no-debug-non-zts-20090626

    PHP(Hypertext Preprocessor)是一种广泛使用的开源脚本语言,主要用于Web开发,但也可作为通用编程语言使用。在构建PHP时,开发人员可以选择不同的配置选项来定制解释器的行为。 1. **no-debug**: 这个前缀表明这...

    Non-Convex Mesh Collider Automatic Generator v1.2.unitypackage

    Non-Convex Mesh Collider Automatic Generator v1

    Unfortunately you can’t have non-Gradle Java modules and Android-Gradle modules in one project.

    you can’t have non-Gradle Java modules and Android-Gradle modules in one project. 既然gradle的问题,那就同步一下吧。 结果发现左上角的Sync Project with Gradle Files按钮竟然是灰色的

    NMS通用算法_论文《Efficient Non-Maximum Suppression》(中文)

    非极大值抑制(Non-Maximum Suppression,简称NMS)是一种广泛应用于计算机视觉任务中的低层次处理技术,尤其在边缘检测、目标检测等领域扮演着重要的角色。NMS的目的在于保留图像中的局部最大值点,同时抑制其他...

    Blockchain Basics A Non-Technical Introduction in 25 Steps epub

    Blockchain Basics A Non-Technical Introduction in 25 Steps 英文epub 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者或csdn删除

    第二版Science Research Writing for Non-Native Speakers of English

    "第二版Science Research Writing for Non-Native Speakers of English" 这本书是为非英语母语者的科学研究写作指南,旨在帮助他们提高英语写作能力,特别是在科学研究领域中。下面是这本书中的一些重要知识点: 1...

    N32905 Non-OS 源码

    总之,"N32905 Non-OS 源码"是一份宝贵的教育资源,它揭示了嵌入式系统开发的基础和精髓。结合KEIL工具,开发者可以深入探究N32905芯片的特性和潜力,为各种应用场景提供定制化的解决方案。通过学习和实践,我们不仅...

    Science research writing for non-native speakers of English.pdf

    《科学研究写作》可能还探讨了如何在保持学术严谨性的同时,让文章的表达更具吸引力。同时,书中还可能包括了如何进行同行评审和如何正确回应审稿人反馈的内容。这些是科研工作者在论文发表过程中不可避免的环节,...

    monte carlo filter and smoother for non-gaussian nonlinear state space models

    本文档的标题“Monte Carlo Filter and Smoother for Non-Gaussian Nonlinear State Space Models”指出了其核心研究内容:利用蒙特卡洛方法(Monte Carlo method)来处理非高斯(Non-Gaussian)、非线性(Nonlinear...

    Learning Non-Local Range Markov Random Field for Image Restoration

    Learning Non-Local Range Markov Random Field for Image Restoration, http://gr.xjtu.edu.cn/web/jiansun/4

    Laravel开发-laravel-non-www

    "Laravel开发-laravel-non-www"项目就是针对这种情况提供的一种解决方案,它是一个微型的Laravel5中间件,旨在处理WWW前缀的URL请求,确保所有流量都正确地导向无WWW的版本。这在统一域名配置、SEO优化以及保持网站...

    NSSM - the Non-Sucking Service Manager

    NSSM (Non-Sucking Service Manager) 是一个开源工具,用于将任何Windows程序打包成服务,并在启动时自动运行。NSSM是一款轻量级且易于使用的应用程序,适用于系统管理员和开发人员。 NSSM的主要功能包括: 1.将任何...

    Science research writing for non-native and native speakers

    5. **讨论**:讨论部分是对结果的解读,比较与已有研究的异同,解释观察到的现象,并探讨可能的原因。同时,此处也应提出未来研究的方向。 6. **结论**:总结研究的主要发现,强调其对领域的影响,并回应引言中的...

Global site tag (gtag.js) - Google Analytics