Java Servlet基础

本章将使用javax.servlet.http包中的类。该包不包含在JDK的核心类库中,因此需将Tomcat安装目录lib子目录中的servlet-api.jar文件复制到JDK根目录下jre/lib/ext中。
为了便于理解,我们先简单提一下MVC模式,这个模式在JAVA里也有提过,是一种被广泛使用的一种设计模式。

MVC模式的核心思想是有效组合“模型(model)”、“视图(view)”、“控制器(controller)”。是一种先进的设计模式。
model:用于存储数据的对象。
view:向控制器提交所需数据、显示模型中的数据。
controller:负责具体的业务逻辑操作,即控制器根据视图提出的要求对数据做出处理,将有关结果存储到模型中,并负责让模型和视图进行必要的交互,当模型中的数据变化时,让视图更新显示。
从面向对象的角度看,MVC结构可以使程序更具有对象化特征,也更容易维护(低耦合)。
 

我们上一章学的bean和这章即将学的servlet,一个是model部分,一个是controller部分,虽然某些功能有重叠,但是这是需要分层所必须的,我们需要清楚,我们这章使用servlet完成的很多任务,大部分都是上一章类似的,并且可以用上一章的方法实现,但是bean实际上是用来做数据存储的,其他操作只是他的“副产品”,而且servlet的功能会比bean强大得多(也麻烦的多emmm)。
 
java Servlet的核心思想是在服务器端创建能响应用户请求的对象。

1.Servlet类

就是编写一个特殊的子类,这个特殊的类就是javax.servlet.http包中的HttpServlet类。HttpServlet类实现了Servlet接口,实现了响应用户的方法。HttpServlet类的子类被习惯地称为作一个Servlet类,这样的类创建的对象习惯上被称作一个servlet。

2.保存字节码文件

Servlet类的字节码文件和bean的字节码文件一样,都应该保存到以classes为根目录下的对应目录中。

3.编写web.xml文件

和bean不同的是,我们需要编写部署文件,这样Tomcat服务器才会按客户的请求使用Servlet字节码文件创建对象。
web.xml文件在WEB-INF文件下,先来简单学习一些xml标记,比如下面这个?。

  <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>JavaServlet.ServletTest</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/lookHello</url-pattern>
</servlet-mapping>

<servlet>标记及其子标记
该标记 需要两个子标记:<servlet-name>和<servlet-class>,其中<servlet-name>标记的内容是服务器创建的servlet的名字,<servlet-class>标记的内容指定服务器用那个Servlet类创建servlet。
<servlet-mapping>标记及其子标记
<servlet-mapping>标记需要两个子标记:<servlet-name>和<url-pattern>,其中<servlet-name>标记的内容是服务器创建的servlet名字(和上面的名字相同),<url-pattern>标记用来指定用户用怎样的url格式来请求servlet。
比如上栗中,名字是hello,字节码文件存放在./classes/JavaServlet/ServletTest.class中,访问./lookHello 即可访问该Servlet类。
 
 
example5_1.jsp

<%@ page contentType="text/html;charset=GB2312" %>
<HTML><BODY ><Font size=3>
<FORM action="lookHello" method=post>
  <BR>输入圆的半径:
  <Input type="text" name="radius">
     <Input type="submit" value="提交">
</FORM>
</Font></BODY></HTML>

 
ServletTest.java

package JavaServlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ServletTest extends HttpServlet
{  public void init(ServletConfig config) throws ServletException
    { super.init(config);
    }
   public void service(HttpServletRequest request,HttpServletResponse response)
                       throws IOException
    {  response.setContentType("text/html;charset=GB2312");
       PrintWriter out=response.getWriter();
       out.println("<html><body>");
       String str=request.getParameter("radius");  //获取客户提交的信息
       double r=0;
       try{  r=Double.parseDouble(str);
             if(r>=0)
              { out.print("<BR>半径是 "+r+" 的圆的面积:");
                out.print("<BR>"+Math.PI*r*r);
              }
             else
              {  out.print("<BR>圆的半径不可以是负数:"+r);
              }
          }
        catch(NumberFormatException e)
          { out.print("<H1>请输入数字字符! </H1>");
          }
       out.println("</body></html>");
    }
}

 
我们右键编辑web.xml(不要双击,否则会打开xml文件)。
web.xml(添加)

  <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>JavaServlet.ServletTest</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/lookHello</url-pattern>
</servlet-mapping>

 
运行结果:


我们发现,上面地址栏是lookHello,并且执行了ServletTest.java的字节码的执行结果。
同样,我们可以在URL里直接输入参数访问lookHello

所以我们这里既可以向lookHello提交表单(form),也可以直接<a href=”lookHello??radius=10> 超链接到该类,效果都是一样的。
注意:对所有的字节码文件进行更改、对web.xml文件进行更改都需要重启服务器。
(如果你在跟我做,较新版本的Tomcat在web.xml做出更改之后可以重新加载,所以节省操作不用重启,但是书上仍然要求重启。)
 

servlet的创建与运行

用户可以根据web.xml部署文件来请求服务器创建并运行一个servlet。如果服务器没有名字为hello的servlet,服务器就会根据web.xml文件中的<servlet>的class指定的servlet类创建一个名字为hello的servlet对象。因此对web.xml的更改或是对字节码的更改必须重启服务器。
servlet类可以使用getServletName()方法返回配置文件中的<servlet-name>标记给出的servlet的名字。
当用户请求服务器运行一个servlet时,必须根据web.xml文件中的<servlet-mapping>标记的子标记<url-pattern>指定的格式输入请求。
 

servlet对象的生命周期

一个servlet的生命周期主要有下列3个过程组成:
(1)初始化servlet,servlet第一次被请求加载时,服务器初始化这个servlet,即创建一个servlet,这个servlet调用init()方法完成必要的初始化工作。
(2)新诞生的servlet再调用service【servlet类中的方法】方法相应用户的请求。
init方法只被调用一次,即在servlet第一次被请求加载时调用该方法。当后续的用户请求servlet服务时,Web服务将启动一个新的线程,在该线程中,servlet调用service方法响应用户的请求。
 
init方法:首次请求时被调用。
service方法:当servlet成功创建和初始化后,调用servlet服务器会将两个参数传递给该方法:一个是HttpServletRequest类型的对象,该对象封装了用户的请求信息;另一个是HttpServletResponse类型的对象,该对象用来响应用户的请求。
destroy方法:当服务器终止服务时,该方法会被执行,用来销毁servlet。
init方法只会被调用一次(当然destroy也是),但是service可以被调用很多次,每当有新用户访问servlet时,服务器都会为用户分配一个全新的线程并调用该方法。

4.共享变量

因为每个用户都调用Servlet的service方法,所以
example5_4.jsp

<%@ page contentType="text/html;charset=GB2312" %>
<HTML><BODY ><Font size=5>
<A Href="lookPI" >参与计算PI的值<A>
</BODY></HTML>

computePI.java

package JavaServlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class computePI extends HttpServlet
{ double sum=0,i=1,j=1;
  int number=0;
  public void init(ServletConfig config) throws ServletException
    {super.init(config);
    }
  public synchronized void service(HttpServletRequest request,
HttpServletResponse response) throws IOException
    {   response.setContentType("text/html;charset=GB2312");
        PrintWriter out=response.getWriter();
        out.println("<html><body>");
        number++;
        sum=sum+i/j;
        j=j+2;
        i=-i;
        out.println("servlet:"+getServletName()+"已经被请求了"+number+"次");
        out.println("<BR>现在PI的值是:");
        out.println(4*sum);
        out.println("</body></html>");
    }
}

web.xml(增加)

    <servlet>
    <servlet-name>PI</servlet-name>
    <servlet-class>JavaServlet.computePI</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>PI</servlet-name>
    <url-pattern>/lookPI</url-pattern>
</servlet-mapping>

我们看到如果你把变量放到整个Servlet类的成员变量可以被所有访问该页面的用户所共享。


这个值随着访问次数的增加会越来越接近PI。

5.doGet和doPost方法

这两个方法是用来处理用户的请求并作出响应的方法。
我们知道,当用户访问Servlet类时,服务器会为该用户分配一个全新的线程来调用service方法,但是实际上这里service方法的功能是检查HTTP的请求类型(get或者post),并在service方法中根据请求方式的不同调用上述两种方法,所以我们可以直接复写doGet和doPost两个方法完成对两种请求的不同响应。
当然这种方法可以增加响应的灵活性并降低服务器的负担,如果对两种请求方式作出的响应相同,只需要在doGet方法中调用doPost方法或者在doPost方法中调用doGet方法即可。
 
example5_5.jsp

<%@ page contentType="text/html;charset=GB2312" %>
<HTML><BODY ><Font size=2>
<P>输入一个数,提交给servlet(Get方式):
<FORM action="lookSquare" method=get>
  <Input Type=text name=number>
  <Input Type=submit value="提交">
</FORM>
<P>输入一个数,提交给servlet(Post方式):
<FORM action="lookSquare" method=post>
  <Input Type=text name=number>
  <Input Type=submit value="提交">
</FORM>
</BODY></HTML>

 
GetSquareOrcubin.java

package JavaServlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class GetSquareOrCubic extends HttpServlet
{  public void init(ServletConfig config) throws ServletException
    {super.init(config);
    }
   public  void  doPost(HttpServletRequest request,HttpServletResponse response)
                        throws ServletException,IOException
    {   response.setContentType("text/html;charset=GB2312");
        PrintWriter out=response.getWriter();
        out.println("<html><body>");
        String number=request.getParameter("number");     //获取客户提交的信息
        double n=0;
        try{  n=Double.parseDouble(number);
             out.print("<BR>"+number+"的平方是:");
             out.print("<BR>"+n*n);
          }
        catch(NumberFormatException e)
          { out.print("<H1>请输入数字字符! </H1>");
          }
        out.println("</body></html>");
    }
   public  void  doGet(HttpServletRequest request,HttpServletResponse response)
                        throws ServletException,IOException
    {   response.setContentType("text/html;charset=GB2312");
        PrintWriter out=response.getWriter();
        out.println("<html><body>");
        String number=request.getParameter("number");     //获取客户提交的信息
        double n=0;
        try{  n=Double.parseDouble(number);
             out.print("<BR>"+number+"的立方是:");
             out.print("<BR>"+n*n*n);
          }
        catch(NumberFormatException e)
          { out.print("<H1>请输入数字字符! </H1>");
          }
        out.println("</body></html>");
    }
}

 
web.xml(添加)

<servlet> <servlet-name>showSquareOrCubic</servlet-name>
<servlet-class>JavaServlet.GetSquareOrCubic</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>showSquareOrCubic</servlet-name>
<url-pattern>/lookSquare</url-pattern>
</servlet-mapping>

 
我们可以直观的看出访问方式的不同导致服务器响应的不同。

 
 

6.重定向与转发

重定向的功能是将用户从当前页面或servlet定向到另一个JSP页面或Servlet;(让你到另一个页面)
转发的功能是将用用户对当前JSP页面或Servlet的请求转发给另一个JSP页面或Servlet。(把你提交的表单给别人一份)
1.sendRedirect方法
重定向方法仅仅是将用户从当前页面或Servlet定向到另一个JSP页面或者Servlet,但不能将用户对当前页面或Servlet的请求转发给所定向的资源,也就是说,重定向的目标或者页面无法使用request获取用户提交的数据。
2.RequestDispatcher对象
RequestDispatcher对象可以把用户对当前JSP页面或Servlet的请求转发给另一个JSP页面或Servlet,而将用户对当前JSP页面或Servlet的请求和响应传递给转发到的JSP页面或Servlet。也就是说,当前页面所要转发到的目标页面或Servlet可以使用request获取用户提交的数据。
方法:
1.得到RequestDispatcher对象。
在当前页面调用getRequestDispatcher(String path);该方法返回一个RequestDispatcher对象,其中参数path是目的页面或Servlet。
2.转发
在1中获取的对象下调用.forward(request,response);
 
example5_6.jsp

<%@ page contentType="text/html;charset=GB2312" %>
<HTML><BODY ><Font size=2>
<FORM action="verifyYourMessage" method=post>
     输入姓名:<Input Type=text name=name>
 <BR>输入年龄:<Input Type=text name=age>
 <BR><Input Type=submit value="提交">
</FORM></BODY></HTML>

 
example3_6.jsp(名字打错了emmm就不改了)

<%@ page contentType="text/html;charset=GB2312" %>
<HTML><BODY ><Font size=2>
<%
    String name=request.getParameter("name");
    String age=request.getParameter("age");
    out.print(name+"  "+age);
%>
</FORM></BODY></HTML>

 
Verify.java

package JavaServlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class Verify extends HttpServlet
{  public void init(ServletConfig config) throws ServletException
    {super.init(config);
    }
   public  void  doPost(HttpServletRequest request,HttpServletResponse response)
                        throws ServletException,IOException
    {   String name=request.getParameter("name");      //获取客户提交的信息
        String age=request.getParameter("age");        //获取客户提交的信息
        if(name.length()==0||name==null)
         { response.sendRedirect("example5_6.jsp");          //重定向
         }
        else if(age.length()==0||name==null)
         { response.sendRedirect("example5_6.jsp");          //重定向
         }
        else if(age.length()>0)
         { try { int numberAge=Integer.parseInt(age);
                 if(numberAge<=0||numberAge>=150)
                  { response.sendRedirect("example5_6.jsp");
                  }
                 else
                 {  RequestDispatcher dispatcher=
                    request.getRequestDispatcher("example3_6.jsp");
                    dispatcher.forward(request, response);      //转发
                 }
               }
            catch(NumberFormatException e)
              {  response.sendRedirect("example5_6.jsp");
              }
         }
    }
   public  void  doGet(HttpServletRequest request,HttpServletResponse response)
                        throws ServletException,IOException
    {   doPost(request,response);
    }
}

 
web.xml(添加)

<servlet>
 <servlet-name>verify</servlet-name>
 <servlet-class>JavaServlet.Verify</servlet-class>
 </servlet>
 <servlet-mapping>
 <servlet-name>verify</servlet-name>
 <url-pattern>/verifyYourMessage</url-pattern>
 </servlet-mapping>

 
我们看到,如过是又一个文本框没有填写,则会被重定向回当前页面,如果两个都填写了,则请求会被转发到example3_6.jsp,并显示传过去的数字(尽管此时URL显示的是当前Servlet)。

课后题:

1.Servlet对象是在服务器端还是客户端被创建?
·    服务器端。
2.Servlet对象被创建后将先调用init方法还是service方法?
·    先调用init完成相应的初始化操作,然后调用service方法相应操作。
3.“Servlet第一次被请求加载时调用init方法。当后续的客户请求Servlet对象时, Servlet对象不再调用init方法”,这样的说法是否正确?
·    正确。
4.假设创建Servlet的类是tom.jiafei.Dalian,创建的Servlet对象的名字是myservlet,应当怎么配置web.xml文件?

<servlet>
<servlet-name>myservlet</servlet-name>
<servlet-class>tom.jiafei.Dalian</servlet-class>
 </servlet>
<servlet-mapping>
<servlet-name>myservlet</servlet-name>
 <url-pattern>/Dalian</url-pattern>
 </servlet-mapping>

5.如果Servlet类不重写service方法,那么应当重写哪两个方法?
·    doPost()和doGet()
6.HttpServletResponse类的sendRedirect方法和RequestDispatcher类的forward方法有何不同?
·    一个是重定向,一个是转发。重定向的功能是将用户从当前页面或servlet定向到另一个JSP页面或Servlet;转发的功能是将用用户对当前JSP页面或Servlet的请求转发给另一个JSP页面或Servlet。
7.Servlet对象怎么获得用户的会话对象?
·  在doGet()或doPost()方法中使用获取当前会话对象.HttpSession session=request.getSession(true);
一个用户在不同的Servlet中获取的session对象是完全相同的,不同的用户的session对象互不相同。

分类: JSP

0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注