美文网首页
10、Servlet

10、Servlet

作者: sunhaiyu | 来源:发表于2017-03-28 23:33 被阅读36次

servlet

注意: 现在默认是在类上写注解@WebServlet("/Aservlet")来代替web.xml的配置

servlet就是在服务器端接受请求并完成响应的java类。

第一个例子 -- Hello World

使用到了MyEclipse与Tomcat7。新建一个Web Project。

新建一个Java Class,如下

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;


public class Aservlet implements Servlet {

    @Override
    public void destroy() {
        System.out.println("servelet调用了destroy()");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void init(ServletConfig arg0) throws ServletException {
        System.out.println("servelet服务器init");
        
    }

    @Override
    public void service(ServletRequest req, ServletResponse resp)
            throws ServletException, IOException {
        
        System.out.println("servelet服务器接收到请求");
        resp.getWriter().write("Hello World");
        
    }
    
}

在web.xml里面注册。

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <display-name></display-name> 
  <!--  注册servlet到服务器 -->
  <servlet> 
  <!-- 配置servlet的名字,保证唯一,一般用类名 -->
    <servlet-name>Aservlet</servlet-name>
    <!-- 完整路径,若没有package,就是如下写法 -->
    <servlet-class>Aservlet</servlet-class>
  </servlet>
  <!-- 配置servlet的访问路径 -->
  <servlet-mapping>
  <!-- 这个名字和上面的name一致 -->
    <servlet-name>Aservlet</servlet-name>
    <url-pattern>/Aservlet</url-pattern>
  </servlet-mapping>
  <!-- 默认使用index.jsp作为欢迎文件 -->
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

在浏览器输入http://localhost:8080/hello/Aservlet就可以看到显示Hello World.

servlet生命周期

  • Servlet对象创建时机? 第一次访问servlet时。
  • Servlet对象创建的特点? 只在第一次访问时调用init一个servlet实例在服务器中只有一个。
  • 当请求访问servlet时,service方法会处理请求.
  • 当服务器将要关闭,服务器会销毁服务器中的Servlet对象,在真正销毁之前调用destory方法。

其他方法

  • getServletInfo => 基本用不到,可以返回servlet作者信息、版权等。
  • getServletConfig => 获得启动信息对象,如下面的例子
    private ServletConfig config;
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
    }
    // 这里获得的config是从init里面得到的,需要用一个成员变量存储。扩大其生命周期
    @Override
    public ServletConfig getServletConfig() {
        return config;
    }

ServletConfig详解

  • String getInitParameter(String name) 获得配置信息 根据键获得值
  • Enumeration getInitParameterNames()获得配置信息 获得所有键
  • String getServletName() 获得servlet的名称 <servlet-name>AServlet</servlet-name>
  • ServletContext getServletContext() 该方法返回ServletContext对象.

在目录下新建一个Bservlet

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;


public class Bservlet implements Servlet {
    
    private ServletConfig config;
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
    }

    @Override
    public ServletConfig getServletConfig() {
        return config;
    }
    
    @Override
    public void destroy() {
        System.out.println("servelet调用了destroy()");
    }
    
    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void service(ServletRequest req, ServletResponse resp)
            throws ServletException, IOException {
        // 获得所有参数建
        Enumeration<String> en = getServletConfig().getInitParameterNames();
        while (en.hasMoreElements()) {
            // 获得键
            String key = en.nextElement();
            // 获得值
            String value = getServletConfig().getInitParameter(key);
            resp.getWriter().write(key + " -> " + value + "\n");
        }
        // 获得servlet名字
        String servletName = getServletConfig().getServletName();
        resp.getWriter().write(servletName+ "\n");
    }
}

web.xml里新增

<!-- 其他内容 -->
<servlet> 
  <!-- 配置servlet的名字,保证唯一,一般用类名 -->
    <servlet-name>Bservlet</servlet-name>
    <!-- 完整路径,若没有package,就是如下写法 -->
    <servlet-class>Bservlet</servlet-class>
    <!-- 配置初始化参数,键值对的形式 
    getInitParameterNames()获取的就是这里所有的参数键值对-->
    <init-param>
        <param-name>name</param-name>
        <param-value>Tom</param-value>
    </init-param>
    <init-param>
        <param-name>age</param-name>
        <param-value>18</param-value>
    </init-param>
  </servlet>
  
  <!-- 配置servlet的访问路径 -->
  <servlet-mapping>
  <!-- 这个名字和上面的name一致 -->
    <servlet-name>Bservlet</servlet-name>
    <url-pattern>/Bservlet</url-pattern>
  </servlet-mapping>
<!-- 其他内容 -->

在浏览器输入http://localhost:8080/hello/Bsevlet,显示如下数据

name -> Tom
age -> 18
Bservlet

GenericServlet

自己写一个MyGenericServlet

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;


public abstract class MyGenericServlet implements Servlet, ServletConfig {
    private ServletConfig config;
    
    @Override
    public void destroy() {
        
    }

    @Override
    public ServletConfig getServletConfig() {
        return config;
    }

    @Override
    public String getServletInfo() {
        return "";
    }
    // 在连接服务器的时候,init(config)肯定会被调用,如果想在初始化动作里面完成其他初始化,再写一个空参init被有参的调用
    // 子类重写空参init即可
    // 这么做把默认初始化和用户自定义初始化绑定起来了
    @Override
    public void init(ServletConfig config) throws ServletException {
      // init方法 妥善的保存config对象
        this.config = config;
        System.out.println("有参init");
        this.init();
    }
    // 自定义初始化
    // 空参init方法,为了防止开发人员重写 原生init方法
    public void init() throws ServletException {
        
    }
    // 将service函数设为抽象的,强迫用户去实现。因为没有service函数的servlet是没有意义的
    @Override
    public abstract void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException;

    @Override
    public String getServletName() {
        return config.getServletName();
    }

    @Override
    public ServletContext getServletContext() {
        return config.getServletContext();
    }

    @Override
    public String getInitParameter(String key) {
        return config.getInitParameter(key);
    }

    @Override
    public Enumeration<String> getInitParameterNames() {
        return config.getInitParameterNames();
    }
}

新建一个类继承刚写的MyGenericServlet

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

// 只是重写了init()和service方法
public class Cservlet extends MyGenericServlet {
    // 连接到服务器会自动调用有参的init,打印有参init和无参init
    @Override
    public void init() {
        System.out.println("无参init");
    }

    @Override
    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {
        String value = getInitParameter("name");
        String servletName = getServletName();
        res.getWriter().write(value + "\n");
        res.getWriter().write(servletName + "\n");
    }
}

GenericServlet有如下好处

1. init方法 妥善的保存config对象
2. 空参init方法,为了防止开发人员重写 原生init方法
3. service方法空实现=> 声明为抽象
4. destory方法空实现 
5. 实现getServletInfo,getServletConfig
6. 实现了servletConfig接口. 接口中的方法直接调用config实现类实现.

HttpServlet

自己实现一个简单的MyHttpservlet

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// 这里声明为abstract主要是为了防止用户直接使用该类(new一个实例)。而必须通过继承
public abstract class MyHttpServlet extends MyGenericServlet {

    @Override
    public void service(ServletRequest request, ServletResponse response)
            throws ServletException, IOException {
        // 帮用户强转
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        // 函数重载, 用户自定义的
        service(req, res);
    }
    
    public void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {  
        // 获得请求方式,根据请求方式的不同调用不同的doXXX()方法
        String method = request.getMethod();
        if ("GET".equals(method)) {
            doGet(request, response);
        } else if ("POST".equals(method)) {
            doPost(request, response);
        }
    }
    
    // 处理GET请求
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 具体的处理逻辑
    }
    
    // 处理POST请求
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       // 具体的处理逻辑
    }
}

子类继承MyHttpServlet后,可以选择实现doGet()或者doPost() 。也可以重写自定义的service(HttpServletRequest req, HttpServletResponse res),这样不会太注重到底是什么请求方法。

servlet细节

servlet线程安全

Servlet的实例在服务器运行期间只有一个实例存在,所以线程不安全。

线程不安全: 如果使用成员变量来接受线程参数,如果发生并发,那么会出现线程问题(覆盖,实例只有一个,成员变量也只有一个,后面来的会把前面的覆盖)

解决办法: 将装载线程参数的变量放置到方法中,写成局部变量。

servlet的创建实例时机

默认情况: 第一次访问该servlet时候。

让servlet实例随着服务器的启动而创建,在servlert标签中 添加一个配置即可:<load-on-startup></load-on-startup>在该配置中填入一个整数可实现。

数字的数值,在有多个servlet需要随着服务器启动而启动时,决定启动顺序。数字越小优先级越高。最小就是0。一般0~5。取3就行。

如果数字一样,谁先配置谁先创建。

servlet的路径配置

<url-pattern></url-pattern>该配置,配置方式有两种:

  1. 路径匹配: 一定以"/"开头

/AServlet -> http://localhost:8080/project-name/Aservlet
/ABC/AServlet -> http://localhost:8080/project-name/ABC/Aservlet
/ABC/* -> http://localhost:8080/project-name/ABC/any-word*匹配任意内容太

  1. 后缀名匹配: 以开头*

*.do
*.action
*.html

后缀名匹配和路径匹配不能同一配置中混合使用. 例如: /.do
一个servlet可以配置多个路径. 直接在<servlet-mapping>元素中添加多个<url-pattern>配置即可。优先级: /AServlet > /abc/
> *.do > /*

注意:匹配范围越大,优先级越低。

ServletContext

我们一个Web项目有且只有一个ServletContext .

  • 创建:随着项目的启动而创建
  • 销毁:随着项目的关闭而销毁
  • 获得:通过ServletConfig对象的 getServletContext方法获得。

功能

  1. 可以获得项目参数
  2. 是Servlet技术中的3个域对象之一
  3. 获得项目内的资源
<!-- 项目级的启动参数 -->
<context-param>
    <param-name>name</param-name>
    <param-value>Rose</param-value>
 </context-param>
   <context-param>
    <param-name>age</param-name>
    <param-value>15</param-value>
 </context-param>
  <context-param>
    <param-name>gender</param-name>
    <param-value>female</param-value>
 </context-param>
import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class Fservlet extends HttpServlet {
    // 获得并输出所有项目启动参数
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 获得ServletContext对象
        ServletContext servletContext = getServletContext();
        Enumeration<String> en = servletContext.getInitParameterNames();
        
        while (en.hasMoreElements()) {
            String key = en.nextElement();
            String value = servletContext.getInitParameter(key);
            
            response.getWriter().write("<h2>"+key + " -> " + value+"</h2>");
            
        }
    }

}

servlet之间通讯

先写一个简单的例子,用另外一个类作为中间媒介,通过static对象在类之间共享,实现通讯。

import java.util.HashMap;
import java.util.Map;


public class Constant {
    public static String word;
    public static Map<String, Object> map = new HashMap<>(); 
}

Gservlet向Hservlet发送消息

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class Gservlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 通过static对象在类之间共享
        Constant.word = "hahaha";
        Constant.map.put("money", "1000");
        Constant.map.put("iphone", "Apple"); 
    }
}

Hservlet接收消息

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class Hservlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String word = Constant.word;
        String money = Constant.map.get("money");
        String phone = Constant.map.get("iphone");
        
        System.out.println(word);
        System.out.println(money);
        System.out.println(phone);
    }
}

其实ServletContext已经帮我们想到了这一点

使用servletContext.setAttribute(key, value)设置属性键值对。

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class Gservlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        ServletContext sc = getServletContext();
        
        sc.setAttribute("money", "100");
        sc.setAttribute("iphone", "Apple");
        System.out.println("已发送");
        
//      Constant.word = "hahaha";
//      Constant.map.put("money", "1000");
//      Constant.map.put("iphone", "Apple"); 
    }
}

使用servletContext.setAttribute(key, value)设置属性键值对。

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class Hservlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
//      String word = Constant.word;
//      String money = Constant.map.get("money");
//      String phone = Constant.map.get("iphone");
        
        ServletContext sc = getServletContext();
        
        String money = (String) sc.getAttribute("money");
        String phone = (String) sc.getAttribute("iphone");
        System.out.println(money);
        System.out.println(phone);
    }
}

其他方法

// 删除键值对
        sc.removeAttribute("money");
        // 遍历所有属性
        Enumeration<String> en = sc.getAttributeNames();
        while (en.hasMoreElements()) {
            String key = en.nextElement();
            System.out.println(key);
        }

上面的ServletContext介绍的是application域。

Servlet三大域

  • application
  • request
  • session

域用于服务器组件之间的通讯(例如:两个servlet之间通讯)。域的实质就是map。application域 就是在整个项目内共享数据的map,所以两个servlet之间通信,其他servlet也会知道。且application域不是线程安全的,不能知道接受到的信息是否就是来自某一个具体的servlet,例如上例中Hservlet不知道它接收到信息就是Gservlet传来的。

操作域的方法:

  • void setAttribute(String key,Object value);
  • Object getAttribute(String key);
  • Enumeration<String> getAttributeNames();
  • void removeAttribute(String key);

获得内部资源

package servlet;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URL;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Iservlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
         ServletContext sc = getServletContext();
            // 1、 获得WebRoot下的user.xml这样写就行
            InputStream is =  sc.getResourceAsStream("/user.xml");
            System.out.println(is); 
            String realPath = sc.getRealPath("/user.xml");
            System.out.println(realPath); // //\webapps\cookie_session\user.xml
            // 2、Class也有获得资源的方法,/表示根目录是src或者说classes目录,ServletContext的根目录是项目的根目录(WebRoot)
            // 这样写是获取classes目录下的xml,WEB-INF/classes/user.xml
            InputStream is3 = Fservlet.class.getResourceAsStream("/user.xml");
            System.out.println(is3);
            // 这样写是获取工程包(package)下的目录下的xml,注意不加"/"
            InputStream is4 = Fservlet.class.getResourceAsStream("user.xml");
            System.out.println(is4);
            // 获得classes下具体某个包下xml的绝对路径
            URL url = Fservlet.class.getResource("user.xml");
            System.out.println(url.getPath()); // /webapps/cookie_session/WEB-INF/classes/cookie/user.xml
           // 获得src(或者说classes)下xml的绝对路径
            URL url2 = Fservlet.class.getResource("/user.xml");
            System.out.println(url2.getPath()); // /webapps/cookie_session/WEB-INF/classes/user.xml
        }
}

总结

Servlet Server Applet -> 运行在服务器端的小程序

功能:接收请求并完成响应。

本质:就是一个Java类

规则

  • 实现Servlet
  • 继承GenericServlet
  • 继承HttpServlet

生命周期

  1. 第一次访问时,服务器会创建servlet的实例,创建完后才调用init ()方法进行初始化
  2. 讲请求交给service(req, res)处理。
  3. 服务器关闭时调用destroy()方法。

servlet具体过程

小任务--统计访问人数

package servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Lservlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
            //1 获得Application域中存放的统计数字
            Integer count = (Integer) getServletContext().getAttribute("count");
            //2 判断是否获得到统计数字
            if(count == null){
                //没获得到=> 将数字初始化为1
                count = 1;
            }else{
                //获得到了=> 将数字加1
                count += 1;
            }
            //3 输出,放回到Application域中
            response.getWriter().write("you are the "+ count+" vistors");
            getServletContext().setAttribute("count",count);
    }

}

by @sunhaiyu

2017.3.25

相关文章

  • Servlet知识点

    servlet 知识点 主要分为 10 个点 : ① Tomcat ; ②Servlet 与HTTP介绍 ;③ S...

  • 10、Servlet

    servlet 注意: 现在默认是在类上写注解@WebServlet("/Aservlet")来代替web.xml...

  • servlet 笔记

    servlet servlet 声明周期 Servlet 的线程安全 Servlet 获取配置信息 Servlet...

  • Servlet

    Servlet介绍 Servlet开发步骤 创建Servlet 配置Servlet 配置Servlet中设置初始化...

  • 深入理解Servlet技术

    1. 什么是servlet servlet Servlet是javax.servlet.Servlet包中定义的一...

  • Servlet与Jsp(1)

    Servlet 实现原理Servlet接口使Servlet容器能将Servlet类载入内存,并在Servlet实例...

  • web.xml不同版本之间头信息的区别

    一、Servlet 2.3 二、Servlet 2.4 三、Servlet 2.5 四、Servlet 3.0 五...

  • Servlet入门

    Servlet简介 Servlet(Servlet Applet)是Java Servlet的简称,称为小服务程序...

  • Servlet 实例

    Servlet 实例 Servlet 是服务 HTTP 请求并实现 javax.servlet.Servlet 接...

  • Java Servlet API

    Servlet 支持的版本 Servlet 3.0 新特性详解 Servlet 3.0 新特性概述Servlet ...

网友评论

      本文标题:10、Servlet

      本文链接:https://www.haomeiwen.com/subject/ybwsottx.html