闲人笔记1:HelloServlet

从一个servlet开始吧
建立一个页面做一个简单的输入,比如用户登录
[code]
<form action="./XXXX " method="post">
<table>
<tr><td><input name="name" type="text" size="10"/></td></tr>
<tr><td><input name="pwd" type="password" size="10"/></td></tr>
<tr><td><input type="submit" value="submit"/></td></tr>
</table>
</form>
[/code]
然后我们的业务逻辑方法,在Business类中实现这样一个方法
[code]
public String goSomePageOrDoSomeThing(String name,String pwd){
if(name.equals("user")&&pwd.equals("pwd"))
return "sucess";
else
return "error";
}
[/code]
最后新建一个Action类继承HttpServlet复写其中的service方法
[code]
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String name = req.getParameter("name");
String pwd = req.getParameter("pwd");
Business business = new Business();
//调用我们的业务逻辑,最终判断是否成功
if(business.goSomePageOrDoSomeThing(name,pwd).equals("Sucess")) {
req.getRequestDispatcher("/sucess.jsp").forward(req, resp);
}else{
req.getRequestDispatcher("/error.jsp").forward(req, resp);
}
}
[/code]
这是一个很简单的servlet的使用,在web.xml中配置了servlet的相关参数便可以实现这样的一个业务逻辑。之后根据返回结果,我们判断是到哪一个页面。但,在一个J2EE应用中有成百上千个业务逻辑,我们不可能为每一个方法都配置一个servlet。当然我们可以用一个特定的参数,比如method在每一个url后缀中添加要执行的Business类的方法名,比如”/XXXX?method=add”. 可如果每一个servlet中都要对应一个模型的crud操作的话,最后的代码可能会变成这样
[code]
if(method.equals("add")){
//business do some add op, business.addSomeThing();
....
}else if(method.equals("delete")){
....
}else if(method.equals("update")){
...
}else if(method.equals("detail")){
...
}.....

[/code]
在doPost或是doGET方法中会有很多if else语句的出现,这样使得代码变得很糟,难于维护。
有没有更好的方法?虽然这比把所有的代码都写在JSP中清晰了一些了,可依然难于构建一个满意的程序。

我们想要做的是:隔离变化的部分

试想千百个servlet都做这些工作,他们的名字不同,逻辑不同,但他们都重复的做着三件事。

1 从页面采集数据
2 唤醒相应的业务逻辑层的方法
3 将返回的参数放入到request中,转发或重定向请求.

首先,我们把Action的web.xml映射配置做一下修改,比如
[code]
<servlet-mapping>
<servlet-name>Action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
[/code]
我们把url-pattern 中的过滤规则改为 *.do,即所有以.do结尾的请求,都会由我们的Action管理,这里Action类作为前端控制器。
同样在service方法中,我们可以通过
req.getRequestURI();得到它的url路径比如:/test/login.do
截取字符串
[code]
String name = req.getRequestURI();
int from = req.getRequestURI().lastIndexOf("/");
int end = req.getRequestURI().lastIndexOf(".do");
name = name.substring(from, end);
[/code]
这里我们最后得到的name是“/login”在这里我们需要一个配置文件能够和业务逻辑相对应。比如一个XML文件,用它来描述业务逻辑所在的类和执行之后的页面
[code]
<action-config>
<action id="/login" class="com.hello.servlet.Business">
<vo class="com.hello.servlet.valobj.VO"/>
<forward name="success" path="/sucess.jsp"/>
<forward name="error" path="/error.jsp"/>
</action>
</action-config>
[/code]
假设我们有一个Map在servlet初始化得时候,我们写一段程序,去读这个xml文件,然后把key(id) value(class)放置入这个Map中,而现在我们所需要做的就是在这个Map中找寻是否存在这样的一个action
用dom4j实现一个很粗陋的程序,仅为演示这样一个过程
[code]
HashMap<String,String> actionConfig = new HashMap<String,String>();

public void xmlRead() throws DocumentException, FileNotFoundException {
SAXReader saxReader = new SAXReader();
Document doc = saxReader.read(Thread.currentThread()
.getContextClassLoader().getResourceAsStream("action.xml"));
xmlRead(doc.getRootElement());
}

public void xmlRead(Element element) {
Iterator<Element> it = element.elementIterator();
while (it.hasNext()) {
Element e = it.next();
String key = e.getParent().attributeValue("id");
if (e.getName().equals("action")) {
actionConfig.put(e.attributeValue("id"), e.attributeValue("class")+"");
}
if (e.getName().equals("vo")) {
actionConfig.put(key+".vo", e.attributeValue("class")+"");
}
if(e.getName().equals("forward")){
actionConfig.put(key+".forward."+e.attributeValue("name"), e.attributeValue("path")+"");
}
xmlRead(e);
}
}
[/code]
在init方法中加入xmlRead();这样在servlet启动时,会自动读取我们的配置文件
key 为/login 值为com.hello.servlet.Login
forward 的key 我们取,/login + 一个”.forward.”字符串+name的值
actionform 的key 取 /login + 一个“actionform”字符串
这样我们就知道了,所有映射关系

接下来我们要接触的一个设计模式:命令设计模式
[code]
public interface Action {
public String execute();
}
[/code]
非常简洁,但这是一个非常实用的设计模式。只需要实现这个接口,所有的类便可以统一使用XX.execute()调用其所需的方法,每个类实现的内容虽然不同,但在这一步,却都在进行一个相同的过程,唤醒业务逻辑层。
恩,我们需要把参数传递给业务层的组件。
可能,你会想要这么做
[code]
public interface Action {
public String execute(HttpServletRequest req,HttpServletResponse resp);
}
[/code]
看上去不错,有了request和response我们不但可以拿取参数,还可以转向。但这样做无法避免一堆一堆类似于
[code]req.getParameter("name");[/code]如此的重复的劳动。

用一个VO做参数的传递对象,一段粗陋的程序仅仅为了演示这个过程
[code]
Class clzz = Thread.currentThread().getContextClassLoader().loadClass("com.hello.servlet.VO");
Object o = clzz.newInstance();
for(Field f :clzz.getFields()){
f.set(o, req.getParameter(f.getName()));
}
[/code]
找到配置中对应的VO,反射机制实例化它,然后注入参数。
com.hello.servlet.VO将VO从前面的配置map中取出

最后在需要的地方VO vo = (VO) o;进行转型

这样一来我们execute中的方法又变成了这样
[code]
public String execute(HttpServletRequest req,HttpServletResponse resp,Object object);
[/code]
object 为传递的参数。但这样做我们如何来做服务器端得验证?
我们不如建立一个新的类比如 ValueObject
在其中加入[code]public ActionErrors validate(HttpServletRequest request…) {}[/code]
这样一个方法,然后所有的VO都去继承该类。然后再实例化VO后总是调用该方法

同理我们也从配置文件中拿到对应的action类,实例化然后调用它的execute方法。
[code]
//init action
Class actClz = Thread.currentThread().getContextClassLoader().loadClass(actionConfig.get(name));
Object actObj = actClz.newInstance();
Class args[] = { HttpServletRequest.class,HttpServletResponse.class, ValueObject.class };
Method meth = actClz.getMethod("execute", args);
Object[] objs = { req, resp, vo };
//invoke the method get the result
Object resultObj = meth.invoke(actObj, objs);
[/code]
在业务逻辑中实现
[code]
public String execute(HttpServletRequest req, HttpServletResponse resp,ValueObject vo) {
VO userVO = (VO) vo;
if(userVO.getName().equals("user")&&userVO.getPwd().equals("pwd"))
return "success";
else
return "error";
}
[/code]
最后[code]
//从map中找到其对应的页面
req.getRequestDispatcher(actionConfig.get(name + ".forward."+resultObj.toString())).forward(req, resp);[/code]

这可能是我们拿到这个问题后,想要解决它的最本能的想法,但很显然还需要慢慢细化,然后不断的改进,甚至推翻它。
代码见附件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值