Servlet介绍

JSP的本质就是Servlet,开发者把编写好的JSP也main部署在Web容器中后,Web容器会将JSP编译成对应的Servlet。但直接使用Servlet的坏处是:Servlet的开发效率很低,特别是当使用Servlet生成表现层页面时,页面中所有的HTML标签,都需采用Servlet输出流来输出页面的开发。这一系列的问题,都阻碍了Servlet作为表现层的使用。
自MVC规范出现后,Servlet的责任开始明确下来,仅仅作为控制器使用,不再需要生成页面标签,也不再作为视图层角色使用。

1.Servlet的开发

Servlet通常被称为服务器端小程序,是运行在服务器端的程序,用于处理及响应客户端的请求。
Servlet是个特殊的Java类,这个Java类必须继承HttpServlet。每个Servlet可以响应哭都断的请求。Servlet提供不同的方法用于相应客户端请求。

  • doGet:用于响应客户端的GET请求
  • doPost:用于响应客户端的POST请求
  • doPut:用于响应客户端的PUT请求
  • doDelete:用于响应客户端的DELETE请求

事实上,客户端的请求通常只有GET和POST两种,Servlet为了响应这两种请求,必须重写doGet()和doPost()方法。如果为了响应4个方式的请求,则四个方法都要重写。
大部分时候,Servlet对于所有请求的响应都是一样的。此时,可以采用重写一个方法来代替上面的几个方法:只需重写service()方法即可响应客户端的所有请求。
另外,HttpServlet还包含两个方法。

  • init(ServletConfig config):创建Servlet实例时,调用该方法的初始化Servlet资源。
  • destroy():销毁Servlet实例时,自动调用该方法回收资源。
    通常无须重写init()和destroy()两个方法,除非需要在初始化Servlet时,完成某些资源初始化的方法,才考虑重写init方法。如果需要在销毁Servlet之前,先完成某些资源的回收,比如关闭数据库连接等,才需要重写destroy方法。

    注意
    不用为Servlet类编写构造器,如需要对Servlet执行初始化操作,应将初始化操作放在Servlet的init()方法中定义。而且,如果重写了init(ServletConfig config)方法,则应在重写该方法的第一行调用super.init(config)。即调用HttpServlet的init方法。


下面提供一个Servlet示例,该Servlet将获取表单请求参数,并将请求参数显示给客户端。
//Servlet必须继承HttpServlet类
@WebServlet(name="firstServlet" 
    , urlPatterns={"/firstServlet"})
public class FirstServlet extends HttpServlet
{
    //客户端的响应方法,使用该方法可以响应客户端所有类型的请求
    public void service(HttpServletRequest request,
        HttpServletResponse response) 
        throws ServletException,java.io.IOException
    {
        //设置解码方式
        request.setCharacterEncoding("GBK");
        response.setContentType("text/html;charSet=GBK");
        //获取name的请求参数值
        String name = request.getParameter("name");
        //获取gender的请求参数值
        String gender = request.getParameter("gender");
        //获取color的请求参数值
        String[] color = request.getParameterValues("color");
        //获取country的请求参数值
        String national = request.getParameter("country"); 
        //获取页面输出流
        PrintStream out = new PrintStream(response.getOutputStream());
        //输出HTML页面标签
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Servlet测试</title>");
        out.println("</head>");
        out.println("<body>");
        //输出请求参数的值:name
        out.println("您的名字:" + name + "<hr/>");
        //输出请求参数的值:gender
        out.println("您的性别:" + gender + "<hr/>");
        //输出请求参数的值:color
        out.println("您喜欢的颜色:");
        for(String c : color)
        {
            out.println(c + " ");
        }
        out.println("<hr/>");
        out.println("您喜欢的颜色:");
        //输出请求参数的值:national
        out.println("您来自的国家:" + national + "<hr/>");
        out.println("</body>");
        out.println("</html>");
    }
}
上面的Servlet类继承了HttpServlet类,表明他可以作为一个Servlet使用。其中的service方法用来响应用户请求。该Servlet和之前的九个内置对象那篇中的request1.jsp页面的效果完全相同,都通过HttpServletRequest获取客户端的form请求参数,并显示请求参数的值。

Servlet和JSP的区别在于
1.Servlet中没有内置对象,原来JSP中的内置对象都必须由程序显示创建
2.对于静态的HTML标签,Servlet都必须使用页面输出流逐行输出。

JSP是Servlet的一种简化,使用JSP只需要输出到客户端的内容,至于JSP脚本如何嵌入一个类中,有JSP容器完成。而Servlet则是一个完整的Java类,这个类的service()方法用于响应客户端的请求。 普通Servlet类里的service()方法的作用,等同于JSP生成Servlet类的_jspService()方法。因此原JSP页面的JSP脚本、静态HTML内容,在普通Servlet里都应该转换成service()方法的代码或输出语句。原JSP声明中的内容,对应为在Servlet中定义的成员变量或成员方法。

提示
上面Servlet类中的@WebServlet属于Servlet3.0的Annotation

2.Servlet的配置

编辑好的Servlet源文件并不能响应应用请求,还必须将其编译成class文件。江边以后的FirstServlet.class文件放到WEB-INF/classes路径下,如果Servlet有包,还应该将class文件放在对应的包路径下(如:WEB-INF/classes/com.lee)。

注意
如果需要直接采用javac命令来编译Servlet类,则必须将Servlet API接口和类添加到系统的CLASSPATH环境变量里。即Tomcat安装目录下lib目录中的servlet-api.jar和jsp-api.jar添加到CLASSPATH环境变量中。

为了让Servlet能相应用户请求,还必须将Servlet配置在Web应用中。配置Servlet时,需要修改web.xml文件。

从Servlet3.0开始,配置Servlet有两种方式:
- 在Servlet类中使用WebServlet Annotation进行配置
- 通过在web.xml文件中进行配置

上面开发Servlet类时使用了@WebServlet Annotation修饰该Servlet类,使用@WebServlet时可指定以下属性:
属性是否必需说明
asyncSupported指定该Servlet是否支持异步操作模式
displayName指定该Servlet的显示名
initParams用于为该Servlet配置参数
loadOnStartup用于将该Servlet配置成load-on-startup的Servlet
name指定该Servlet的名称
urlPatterns/value这两个属性的作用完全相同,douzhidinggaiServlet处理的URL

**如果打算使用Annotation来配置,注意以下两点:** - 不要在web.xml文件的根元素`
<!-- 配置Servlet的名字 -->
    <servlet>
        <!-- 指定Servlet的名字,
            相当于指定@WebServlet的name属性 -->
        <servlet-name>firstServlet</servlet-name>
        <!-- 指定Servlet的实现类 -->
        <servlet-class>lee.FirstServlet</servlet-class>
    </servlet>
    <!-- 配置Servlet的URL -->
    <servlet-mapping>
        <!-- 指定Servlet的名字 -->
        <servlet-name>firstServlet</servlet-name>
        <!-- 指定Servlet映射的URL地址,
            相当于指定@WebServlet的urlPatterns属性-->
        <url-pattern>/aa</url-pattern>
    </servlet-mapping>
上面指定了该Servlet的URL为/aa。如果在Servlet类上使用WEbServlet Annotation照样起作用。以下是运行结果图: ![这里写图片描述](https://img-blog.csdn.net/20170922002943841?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxNDIxMjk4MQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) form.jsp请求后,URL显示的是**aa**。在这种情况下,Servlet与JSP的作用效果完全相同。

3.JSP/Servlet的生命周期

JSP的本质就是Servlet,开发者编写的JSP页面将由Web容器编译成对应的Servlet,当Servlet在容器中运行时,其实例的创建和销毁等都不是由程序员决定的,而是由Web容器进行控制的。

创建Servlet实例有两个时机:
1. 客户端第一次请求某个Servlet时,系统创建该Servlet的实例:大部分的Servlet都是这种方式创建。
2. Web应用启动时立即创建Servlet实例,即使用load-on-startup配置


每个Servlet的运行都遵循如下生命周期:
1. 创建Servlet实例。
2. Web容器调用Servlet的init方法,对Servlet进行初始化
3. Servlet初始化后,将一直存在于容器中,用于响应客户端请求。如果客户端发送GET请求,容器调用Servlet的doGet方法处理并响应请求;如果客户端发送POST请求,容器调用Servlet的doPost方法处理并响应请求。或者统一使用service()放啊处理来响应用户请求。
4. Web容器决定销毁Servlet时,先调用Servlet的destroy方法,通常在关闭Web应用之时销毁Servlet。

Created with Raphaël 2.1.0 start start 创建实例成功 创建实例成功 完成初始化 完成初始化 响应客户请求 响应客户请求 资源回收完成 资源回收完成 实例被销毁 实例被销毁 init() destroy() 等待进入下一次生命周期

4.load-on-startup Servlet

创建Servlet实例有两个时机:用户请求之时或应用启动之时。应用启动时就创建Servlet,通常是用于某些后台服务的Servlet,或者需要拦截很多请求的Servlet:这种Servlet通常作为应用的基础Servlet使用,提供重要的后台服务。 配置load-on-starup的Servlet有两种方式:
  • 在web.xml文件中通过<servlet.../>元素的<load-on-startup.../>子元素进行配置。
  • 通过@WebServlet Annotation的loadOnStartup属性 指定。

<load-on-startup.../>元素或loadOnStartup属性都只接受一个整型值,该整型值越小,优先级别就越高,Servlet就越优先实例化。
下面是一个简单的Servlet,该Servlet不响应用户请求,仅仅实现计时器功能,每隔一段时间在控制台打印出当前时间。

@WebServlet(loadOnStartup=1)
public class TimerServlet extends HttpServlet
{
    public void init(ServletConfig config)throws ServletException
    {
        super.init(config);
        Timer t = new Timer(1000,new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                System.out.println(new Date());
            }
        });
        t.start();
    }
}

这个Servlet没有提供service()方法,这表明它不能响应用户请求,所以无须为它配置URL映射。由于他不能接收用户请求,所以只能在应用启动时实例化。
以上程序中Annotation即可将该Servlet配置了load-on-startup Servlet。除此之外,还可以在web.xm这里写代码片段如下:

<servlet>
<!-- Servlet名 -->
    <servlet-name>timerServlet</servlet-name>
<!-- Servlet的实现类 -->
    <servlet-class>lee.TimerServlet</servlet-class>
<!-- 配置应用启动时,创建Servlet实例,相当于指定@WebServlet的loadOnStartup属性-->
<load-on-startup>1</load-on-startup>
</servlet>

以上配置片段中,制定了Web应用启动时,Web容器将会实例化该Servlet,且该Servlet不能响应用户请求,将一直作为后台如无执行:每隔1秒输出一次系统时间。

5.访问Servlet的配置参数

配置Servlet时,还可以增加额外的配置参数。通过使用配置参数,可以实现提供更好的可移植性,避免将参数以硬编码方式写在代码中。
为Servlet配置参数有两种方式:

  • 通过@WebServlet的initParams属性来指定。
  • 通过在web.xml文件的<servlet.../>元素中添加<init-param.../>子元素来指定。

第二种方式与为JSP配置初始化参数极其相似,因为JSP的实质就是Servlet,而且配置JSP的实质就是把JSP当Servlet使用。
访问Servlet配置参数通过ServletConfig对象完成,ServletConfig提供如下方法。
- java.lang.String getInitParameter(java.lang.String name):用于获取初始化参数。

注意
JSP的内置对象config就是此处的ServletConfig

下面的Servlet将会连接数据库,并执行SQL查询,通过Annotation和配置web.xml两种方式来配置连接数据库的信息。

ServletConfig获取配置参数的方法和ServletContext获取配置参数的方法完全一样,只是ServletConfig是取得当前Servlet的配置参数,而ServletContext是获取整个Web应用的配置参数。

@WebServlet(name="testServlet"
    , urlPatterns={"/testServlet"}
    , initParams={
        @WebInitParam(name="driver", value="com.mysql.jdbc.Driver"),
        @WebInitParam(name="url", value="jdbc:mysql://localhost:3306/javaee"),
        @WebInitParam(name="user", value="root"),
        @WebInitParam(name="pass", value="1234")})
public class TestServlet extends HttpServlet
{
}

以上程序中粗体字@WebServlet中的initParams属性用于为该Servlet配置参数,initParams属性值的每个@WebInitParam配置一个初始化参数,每个@WebInitParam可指定如下两个属性。

  1. param-name : 指定配置参数名
  2. param-value : 指定配置参数值

下面是Servlet在web.xml文件中的配置片段

<servlet>
        <!-- 配置Servlet名 -->
        <servlet-name>testServlet</servlet-name>
        <!-- 指定Servlet的实现类 -->
        <servlet-class>lee.TestServlet</servlet-class>
        <!-- 配置Servlet的初始化参数:driver -->
        <init-param>
            <param-name>driver</param-name>
            <param-value>com.mysql.jdbc.Driver</param-value>
        </init-param>
        <!-- 配置Servlet的初始化参数:url -->
        <init-param>
            <param-name>url</param-name>
            <param-value>jdbc:mysql://localhost:3306/javaee</param-value>
        </init-param>
        <!-- 配置Servlet的初始化参数:user -->
        <init-param>
            <param-name>user</param-name>
            <param-value>root</param-value>
        </init-param>
        <!-- 配置Servlet的初始化参数:pass -->
        <init-param>
            <param-name>pass</param-name>
            <param-value>lzc717LZC</param-value>
        </init-param>
    </servlet>

运行效果如下:
这里写图片描述

6.使用Servlet作为控制器

使用Servlet作为表现层的工作量太大,所有的HTML标签都需要使用页面输出流生成。因此,使用Servlet作为表现层有如下劣势:

  1. 开发效率地,所有的HTML标签都需要使用页面输出流完成。
  2. 不利于团队协作开发,美工人员无法参与Servlet界面的开发。
  3. 程序可维护性查,及时修改一个按钮的标题,都必须重新编辑代码,并重新编译。

在标准的MVC模式中,Servlet仅作为控制器使用。Java EE应用架构正式遵循MVC模式的,对于遵循MVC模式的Java EE应用而言,JSP仅作为表现层(View)技术,其作用有以下两点:

  1. 负责收集用户请求参数
  2. 将应用的处理结果、状态数据呈现给用户

Servlet则仅充当控制器(Controller)角色,所有业务逻辑、数据访问逻辑都在Model中实现。实际上Model下还可以有很多丰富的组件,例如DAO组件、领域对象等。
下面介绍一个使用Servlet作为控制器的MVC应用,来演示一个简单的登陆验证。
下面是登陆页面代码:

<%
if (request.getAttribute("err") != null)
{
    out.println(request.getAttribute("err") + "<br/>");
}
%>
</span>
请输入用户名和密码:
<!-- 登录表单,该表单提交到一个Servlet -->
<form id="login" method="post" action="login">
用户名:<input type="text" name="username"/><br/>
密&nbsp;&nbsp码:<input type="password" name="pass"/><br/>
<input type="submit" value="登录"/><br/>
</form>

运行效果如下:
这里写图片描述
上面的if判断是用来响应request输出错误提示,该页面其实是一个简单的表单页,用于收集用户名及密码,并将请求提交到制定Servlet,该Servlet充当控制器角色。

注意
根据严格的MVC规范,上面的login.jsp页面也不应该被客户端直接访问,客户的请求应先发送到指定Servlet,然后由Servlet将请求forward到该JSP页面。

控制器Servlet的代码如下:

@WebServlet(name="login"
    , urlPatterns={"/login"})
public class LoginServlet extends HttpServlet
{
    //响应客户端请求的方法
    public void service(HttpServletRequest request,
        HttpServletResponse response)
        throws ServletException,java.io.IOException
    {
        String errMsg = "";
        //Servlet本身并不输出响应到客户端,因此必须将请求转发
        RequestDispatcher rd;
        //获取请求参数
        String username = request.getParameter("username");
        String pass = request.getParameter("pass");
        try
        {
            //Servlet本身,并不执行任何的业务逻辑处理,它调用JavaBean处理用户请求
            DbDao dd = new DbDao("com.mysql.jdbc.Driver",
                "jdbc:mysql://localhost:3306/javaee","root","1234");
            //查询结果集
            ResultSet rs = dd.query("select pass from user "
                + "where name = ?", username);
            if (rs.next())
            {
                //用户名和密码匹配
                if (rs.getString("pass").equals(pass))
                {
                    //获取session对象
                    HttpSession session = request.getSession(true);
                    //设置session属性,跟踪用户会话状态
                    session.setAttribute("name" , username);
                    //获取转发对象
                    rd = request.getRequestDispatcher("/welcome.jsp");
                    //转发请求
                    rd.forward(request,response);
                }
                else
                {
                    //用户名和密码不匹配时
                    errMsg += "您的用户名密码不符合,请重新输入";
                }
            }
            else
            {
                //用户名不存在时
                errMsg += "您的用户名不存在,请先注册";
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();            
        }
        //如果出错,转发到重新登录
        if (errMsg != null && !errMsg.equals(""))
        {
            rd = request.getRequestDispatcher("/login.jsp");        
            request.setAttribute("err" , errMsg);
            rd.forward(request,response);
        }
    }
}

控制器负责接收客户端的请求,它既不直接对客户端输出响应,也不处理用户请求,值调用JavaBean来处理用户请求;JavaBean处理及术后,Servlet根据处理结果,调用不同的JSP页面向浏览器呈现处理结果。
上面Servlet使用@WebServlet Annotation为该Servlet配置了URL为/login,因此向/login发送的请求将会交给该Servlet处理。
下面是DbDao主要代码:

public class DbDao 
{
    private Connection conn;
    private String driver;
    private String url;
    private String username;
    private String pass;
    public DbDao()
    {
    }
    public DbDao(String driver , String url 
        , String username , String pass)
    {
        this.driver = driver;
        this.url = url;
        this.username = username;
        this.pass = pass; 
    }
    //省略成员属性的setter和getter方法...

    //获取数据库连接
    public Connection getConnection() throws Exception
    {
        if (conn == null)
        {
            Class.forName(this.driver);
            conn = DriverManager.getConnection(url,username,
                this.pass);
        }
        return conn;
    }
    //插入记录
    public boolean insert(String sql , Object... args)
        throws Exception
    {
        PreparedStatement pstmt = getConnection().prepareStatement(sql);
        for (int i = 0; i < args.length ; i++ )
        {
            pstmt.setObject( i + 1 , args[i]);
        }
        if (pstmt.executeUpdate() != 1)
        {
            return false;
        }
        pstmt.close();
        return true;
    }
    //执行查询
    public ResultSet query(String sql , Object... args)
        throws Exception
    {
        PreparedStatement pstmt = getConnection().prepareStatement(sql);
        for (int i = 0; i < args.length ; i++ )
        {
            pstmt.setObject( i + 1 , args[i]);
        }
        return pstmt.executeQuery();
    }
    //执行修改
    public void modify(String sql , Object... args)
        throws Exception
    {
        PreparedStatement pstmt = getConnection().prepareStatement(sql);
        for (int i = 0; i < args.length ; i++ )
        {
            pstmt.setObject( i + 1 , args[i]);
        }
        pstmt.executeUpdate();
        pstmt.close();
    }
    //关闭数据库连接的方法
    public void closeConn()
        throws Exception
    {
        if (conn != null && !conn.isClosed())
        {
            conn.close();
        }
    }
}

下面是MVC中各个角色的对应组件
M: Model,即模型,对应JavaBean
V:View,即视图,对应JSP页面
C:Controller,即控制器,对应Servlet

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值