本系列打算从最基础开始,详细介绍j2ee开发技术的原理及相应框架;
本文主要介绍了3个servlet的入门例子:
例子1:最简单的servlet例子,也就是hello world;
例子2:介绍了一个动作框架,这个是其他更复杂框架的基础思路;
例子3:加入了数据库存储数据,同时提供了一个最基础的事务解决方案;
最后,本文根据以上3个例子,提炼出j2ee web开发过程中可能会碰到的问题;实际上这也是大多数web开发框架需要解决的问题;后续我会带着这些问题去解析ssh框架。其中重点在spring和hibernate。
1 最简单的例子——servlet接口
1.1 项目代码
public class HelloWorldServlet extends HttpServlet{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
//super.service(req, resp);
PrintWriter writer = resp.getWriter();
writer.println("Hello, World!");
writer.close();
}
}
3、web.xml配置
<!DOCTYPE web-app PUBLIC '-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN' 'http://java.sun.com/dtd/web-app_2_3.dtd'>
<web-app>
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.xuxm.demo.servlet.HelloWorldServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
1.2 分析
HttpServlet 里的三个方法:service(HttpServletRequest req, HttpServletResponse resp) ,doGet(HttpServletRequest req, HttpServletResponse resp), doPost(HttpServletRequest req, HttpServletResponse res)的区别和联系:注意,sun只是定义了servlet接口,而实现servlet接口的就是类似于tomcat的服务器,所以我是在tomcat的安装目录下找到实现的类。
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals("GET")) {
long lastModified = getLastModified(req);
if (lastModified == -1L)
{
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader("If-Modified-Since");
if (ifModifiedSince < lastModified / 1000L * 1000L)
{
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(304);
}
}
}
else if (method.equals("HEAD")) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
}
else if (method.equals("POST")) {
doPost(req, resp);
}
else if (method.equals("PUT")) {
doPut(req, resp);
}
else if (method.equals("DELETE")) {
doDelete(req, resp);
}
else if (method.equals("OPTIONS")) {
doOptions(req, resp);
}
else if (method.equals("TRACE")) {
doTrace(req, resp);
}
else
{
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
2 动作框架
2.1 简介
在 Web 开发初期,许多专业编程人员都不得不弄清当他们继续时,如何较好地使用 servlet。最普遍的结果之一就是在服务器上暴露 servlet。每种类型的请求都有一个。这很快就变得令人头痛,因此,编程人员开始在其 servlet 中包含条件逻辑使之更具适应性,以便处理多种类型的请求。一段时间后,这也产生了一些糟糕的代码。有一种更好的方式,称作动作 servlet(action servlet),它实现了名为模型 2 的概念。据我了解,该思想是由 David M. Geary首次写到的,但是它已经较好的用于流行的 servlet 库中了,例如 Jakarta Struts 项目。
在动作 servlet 中,并没有指示 servlet 行为的条件逻辑,而是具有动作(编程人员定义的类),servlet 授权这些类来处理不同类型的请求。大多数情况下,这个面向对象(OO)的方法要优于拥有多个 servlet,或在一个 servlet 中有多个 if 条件。
我们的示例动作 servlet 将是一个极简单的、基于浏览器的应用程序的网守(gatekeeper),该应用程序将允许我们创建、存储、查看以及删除合同列表项。这些记录项的格式都非常良好。最后,为了使用该应用程序,用户将必须登录它,但是,我们稍后将在 用户和数据 中添加这项功能。
2.2 项目代码
3、servlet
public class ContactsServlet extends HttpServlet{
protected ActionFactory factory = new ActionFactory();
public ContactsServlet() {
super();
}
protected String getActionName(HttpServletRequest request) {
String path = request.getServletPath();
//System.out.print("test:"+request.getServletPath());
//System.out.print("test:"+path.substring(1, path.lastIndexOf(".")));
return path.substring(1, path.lastIndexOf("."));
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
ContactsAction action = factory.create(getActionName(req));
String url = action.perform(req, resp);
if (url != null)
getServletContext().getRequestDispatcher(url).forward(req, resp);
}
}
4、动作框架
1)ContactsAction
public interface ContactsAction {
public String perform(HttpServletRequest request, HttpServletResponse response);
}
2)BootstrapAction、AddContactAction、RemoveContactAction这三个具体动作都实现了 ContactsAction接口
public class BootstrapAction implements ContactsAction{
@Override
public String perform(HttpServletRequest request,
HttpServletResponse response) {
// TODO Auto-generated method stub
return "/" + "contactList.jsp";
}
}
public class AddContactAction implements ContactsAction{
@Override
public String perform(HttpServletRequest request,
HttpServletResponse response) {
Contact newContact = createContact(request);
HttpSession session = request.getSession();
ContactList contacts = (ContactList) session.getAttribute("contacts");
contacts.addContact(newContact);
session.setAttribute("contacts", contacts);
return "/contactList.jsp";
}
protected Contact createContact(HttpServletRequest request) {
Contact contact = new Contact();
contact.setFirstname(request.getParameter(RequestParameters.FIRSTNAME));
contact.setLastname(request.getParameter(RequestParameters.LASTNAME));
contact.setStreet(request.getParameter(RequestParameters.STREET));
contact.setCity(request.getParameter(RequestParameters.CITY));
contact.setState(request.getParameter(RequestParameters.STATE));
contact.setZip(request.getParameter(RequestParameters.ZIP));
contact.setType(request.getParameter(RequestParameters.TYPE));
return contact;
}
}
public class RemoveContactAction implements ContactsAction{
@Override
public String perform(HttpServletRequest request, HttpServletResponse response) {
int contactId = Integer.parseInt(request.getParameter("id"));
HttpSession session = request.getSession();
ContactList contacts = (ContactList) session.getAttribute("contacts");
contacts.removeContact(contactId);
session.setAttribute("contacts", contacts);
return "/contactList.jsp";
}
}
3)ActionFactory
public class ActionFactory {
protected Map map = defaultMap();
public ActionFactory() {
super();
}
public ContactsAction create(String actionName) {
Class klass = (Class) map.get(actionName);
if (klass == null)
throw new RuntimeException(getClass() + " was unable to find " +
"an action named '" + actionName + "'.");
ContactsAction actionInstance = null;
try {
actionInstance = (ContactsAction) klass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return actionInstance;
}
protected Map defaultMap() {
Map map = new HashMap();
map.put("index", BootstrapAction.class);
map.put("addContactAction", AddContactAction.class);
map.put("removeContactAction", RemoveContactAction.class);
return map;
}
}
4)jsp页面contactList.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%@ page import="java.util.*" %>
<%@ page import="com.xuxm.demo.model.*" %>
<html>
<head>
<title>Contacts List 1.0</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<style type="text/css">
body, table, hr {
color: black;
background: silver;
font-family: Verdana, sans-serif;
font-size: x-small;
}
</style>
</head>
<body>
<jsp:useBean id="contacts" scope="session"
class="com.xuxm.demo.model.ContactList"/>
<h2>Contact List 1.0</h2>
<hr size="2"/>
<table frame="below" width="100%">
<tr>
<th align="left"></th>
<th align="left">Name</th>
<th align="left">Street</th>
<th align="left">City</th>
<th align="left">State</th>
<th align="left">Zip</th>
<th align="left">Type</th>
</tr>
<%
List list = contacts.getContacts();
for (Iterator i = list.iterator(); i.hasNext();) {
Contact contact = (Contact)i.next();
%>
<tr>
<td width="100"><a href="removeContactAction.perform?id=<%= contact.getId()%>"
>Delete</a></td>
<td width="200"><%=contact.getFirstname()%> <%=contact.getLastname()%></td>
<td width="150"><%=contact.getStreet()%></td>
<td width="100"><%=contact.getCity()%></td>
<td width="100"><%=contact.getState()%></td>
<td width="100"><%=contact.getZip()%></td>
<td width="100"><%=contact.getType()%></td>
</tr>
<%
}
%>
</table>
<br/>
<br/>
<br/>
<fieldset>
<legend><b>Add Contact</b></legend>
<form method="post" action="addContactAction.perform">
<table>
<tr>
<td>First Name:<td>
<td><input type="text" size="30" name="firstname"></td>
</tr>
<tr>
<td>Last Name:<td>
<td><input type="text" size="30" name="lastname"></td>
</tr>
<tr>
<td>Street:<td>
<td><input type="text" size="30" name="street"></td>
</tr>
<tr>
<td>City:<td>
<td><input type="text" size="30" name="city"></td>
</tr>
<tr>
<td>State:<td>
<td><input type="text" size="30" name="state"></td>
</tr>
<tr>
<td>Zip:<td>
<td><input type="text" size="30" name="zip"></td>
</tr>
<tr>
<td>Type:<td>
<td><input type="radio" size="30" name="type" value="family">
Family <input type="radio" size="30" name="type"
value="acquaintance"
checked> Acquaintance</td>
</tr>
</table>
<br/>
<input type="submit" name="addContact" value=" Add ">
</form>
</fieldset>
</body>
</html>
2.3 分析
- 从导致调用 servlet 的 URL 中派生动作名。
- 基于该名称实例化正确的动作。
- 告诉该动作开始执行。
- 将响应发送给动作所指向的 URL。
这样做很好,但是我们需要一些附加类来完成该任务。这就是动作框架要做的事。
我们的简单动作框架有 4 个主要组件:
ActionFactory。该工厂将请求中的动作名转换成 servlet 可以用来完成其工作的动作类。
Action 接口。该接口定义所有动作的极其简单的公共接口。
名为 ContactsAction 的抽象类。该类实现了所有动作共用的一个方法,并强制子类实现另一个方法(perform())。
ContactsAction 的三个子类。这些子类使 servlet 能够进行自我引导、添加新合同和删除合同。
在 servlet 的 service() 方法中,该过程以 ActionFactory 开始。
3 总结
根据以上的例子,我们可以猜测(其实是肯定)得出如下结论:
1、所有请求的入口都是servlet;于是你会发现,不管是struct还是spring等,实际上在web.xml中配置的入口都是servlet;
2、请求参数传递:用求某个地址时,参数是如何传入servlet的?从例子中我们可以看到参数是被包装在HttpServletRequest中了。这里会涉及到两个问题:1)验证参数,比如输入的数字不能是小数等;2)参数与对象之间的绑定;
1)参数验证:方法主要有3种:1、前端js验证;2、获取HttpServletRequest参数时验证;3、转为类对象时验证;
2)参与与类对象绑定:在大多数框架,会将传入参数绑定到类对象中,程序实际上处理的是类对象;
3、动作框架:如果每个请求都配置一个servlet,过于混乱;如本文中所述设计一个动作框架,这个是比较初级的;通常框架会提供一个基础类来做这个事情,不需要用户设计;
4、请求或者是结果处理:如果我想对请求做一些处理,比如设置编码方式,或者验证用户权限;这时候该如何处理?总不可能对每个请求处理的代码里面都加入相同的代码吧?对于这个处理,一般是用servlet的filter、或者是spring中的interceptor、或者利用aop来实现;
5、表现层:对于用户看到的部分,使用哪种技术来实现,是jsp还是其他?对于选定的技术,实际上还需要解决,返回参数如何显示在jsp(或其他文件)上;
6、数据存储:1)数据存储在数据库时,需要调用对应的程序,一般是通过调用jdbc来与数据交互;这里就涉及到一个重要问题,那就是事务;
2)数据源切换:通常的理论设计框架,都在鼓吹是否支持切换数据库,比如从oracle切换到mysql,也就是做到数据库无关性;(个人感觉这个需求似乎没那么强烈)
3)缓存:对实时性要求比较高的系统,所有数据都从数据库读取,显然效率太差;于是需要引入数据缓存机制,比如hibernate中ehance,或者redis等开源组件;
7、监听:通常我们可能考虑在系统初始化时做一些事情,或者在某些事件发生时做一些事情。那么这个就需要用到监听了。实际上在web.xml中是可以配置listener的。
8、上下文:一般的应用都要求做一些国际化等之类的事情,这个可能就需要维护上下文,用于支持国际化等;
9、session和cookie:web开发这两个东西是免不了的了。
10、日志:日志本质上不应该在这里提,因为它是属于实现的一部分,但是考虑到在系统bug分析、运维、用户行为分析(数据挖掘)等方面都有大用,因此对于系统日志需要做针对性考虑,到底该记哪些日志、怎么记?
以上基本覆盖了一个web框架需要做的事情,在分析各个框架时以这个思路去考虑其对应的配置到底是做什么的,就可以一目了然了,而不是被各种框架五花八门的实现给迷惑了。当然,在一些框架中可能还用到一些比较方便的功能:
1、bean自动装配:如果系统有很多类,这些类都要在配置文件中进行配置显然是一个很繁琐的事情,于是自动装配bean就被提出来了。这个主要是利用java的注解和反射。
2、分布式(多线程):由于web网站有可能访问量特别大,因此需要进行扩容,这里就设计分布式(多线程);
3、多访问方式:这里用多访问方式似乎不大妥当,但是也找不到其他词来描述了。比如一个系统,部分用户使用普通的网页方式访问而部分用户使用web service访问,这就有可能导致其基于事务处理出现问题(如果它的事务只是针对web配置的话);
《servlet中service() doGet() doPost() 方法》