10、Servlet

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里面注册。



   
  
   
  
    Aservlet
    
    Aservlet
  
  
  
  
    Aservlet
    /Aservlet
  
  
  
    index.jsp
  

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

servlet生命周期

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

其他方法

  • 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的名称 AServlet
  • 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 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里新增


 
  
    Bservlet
    
    Bservlet
    
    
        name
        Tom
    
    
        age
        18
    
  
  
  
  
  
    Bservlet
    /Bservlet
  

在浏览器输入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 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标签中 添加一个配置即可:在该配置中填入一个整数可实现。

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

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

servlet的路径配置

该配置,配置方式有两种:

  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可以配置多个路径. 直接在元素中添加多个配置即可。优先级: /AServlet > /abc/
> *.do > /*

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

ServletContext

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

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

功能

  1. 可以获得项目参数
  2. 是Servlet技术中的3个域对象之一
  3. 获得项目内的资源


    name
    Rose
 
   
    age
    15
 
  
    gender
    female
 
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 en = servletContext.getInitParameterNames();
        
        while (en.hasMoreElements()) {
            String key = en.nextElement();
            String value = servletContext.getInitParameter(key);
            
            response.getWriter().write("

"+key + " -> " + value+"

"); } } }

servlet之间通讯

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

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


public class Constant {
    public static String word;
    public static Map 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 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 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具体过程

10、Servlet_第2张图片

小任务--统计访问人数

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

你可能感兴趣的:(10、Servlet)