转载

java Servlet

Servlet 介绍

Java Servlet 是运行在web服务器和应用程序上的程序,是一个中间层。

运行过程如图

java Servlet

即,当http请求发出以后,在容器内解析http请求,创建出servlet实例,接着,再次调用init方法,接着再次调用service方法,最后由servlet输出响应信息,对于更多的请求来说,将会调用destroy()方法,最后把响应返回给客户端。

简述一下php的过程,先请求处理,发送到apache,由apache进行处理静态资源的访问,对于动态访问,直接转发给php-fpm(其中php-fpm可以独立运行,但是不能处理静态资源)由php-fpm负责读取databases,然后把数据送给apache,再由apache返回给客户端,实际上,apache和nginx都可以作为服务器。事实上拿C++编写CGI也能完成上述的机制。

其中servlet类似于cgi,一种规范,只是一种规范。

并且,监听端口的任务由servlet服务器完成,类似的服务器由tomcat,jboss等服务器,都为实现了servlet的规则。

使用的时候,直接实现其规定的Servlet接口,当发生请求的时候,会由tomcat通过反射机制调用实现的Servlet接口的类。

生命周期

先调用init进行初始化,再次调用service方法来处理客户端的请求,再次调用destroy方法,终止,最后由jvm进行垃圾回收。

init方法

第一次创建servlet时被调用,后续每次用户请求时不在调用。即,它是一次性的初始化。

public void init() throws ServletException{
    // 初始化代码
}

service方法

为执行实际任务时的主要方法。

Servlet容器会调用service方法来处理客户端的请求,并把格式化后的响应返回给浏览器。

每次服务器接收到Servlet的时候,会产生一个新的线程来调用服务。

service方法是用来检查HTTP请求的类型。并在适当的时候,会调用适当的方法。

public void service(ServletRequest request, ServletResponse response) throws Exception{
    // service 方法
}

service方法,容器将会调用,并且是在适当的时候调用doGet和doPost等方法。

doGet方法

public void doGet(HttpServletRequest request,
                  HttpServletResponse response)
    throws ServletException, IOException {
    // Servlet 代码
}

doPost方法

public void doPost(HttpServletRequest request,
                   HttpServletResponse response)
    throws ServletException, IOException {
    // Servlet 代码
}

destroy方法

public void destroy() {
    // 终止化代码...
  }

此方法只会被调用一次,在生命周期结束的时候,会被调用。

destroy方法,用于关闭数据库连接,停止后台线程,把cookie列表写入磁盘,或者点击计数器写入磁盘,并,进行另外的清理活动

栗子

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

// 继承HttpServlet类
public class HelloWorld extends HttpServlet {
    private String message;

    @Override
    // 重写init方法,每当有请求的时候,会调用init方法
    public void init() throws ServletException{
        // 执行初始化
        this.message = "hello world";
    }

    @Override
    // 请求来的时候调用get
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 响应内容类型
        resp.setContentType("text/html");
        // 实际的逻辑处理
        // 获得输出流
        PrintWriter out = resp.getWriter();
        // 进行输出
        out.println("<h1>" + message + "</h1>");
    }

    // 进行后续处理
    public void destroy(){

    }
}

表单数据

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

// 继承HttpServlet类
public class HelloWorld extends HttpServlet {
    private static final long serialVersionUIS = 1L;

    public HelloWorld(){
        super();
    }

    protected  void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        // 响应内容类型
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        String title = "get方法获取表单数据";
        // 中文处理
        String name = new String(request.getParameter("name").getBytes("ISO8859-1"), "UTF-8");
        String docTyoe = "<!Doctype html> /n";
        out.println(docTyoe + "<html>/n" +
                    "<head><title>" + title + "</title><head>/n" +
                    "<body bgcolor = /"#f0f0f0/">/n" +
                    "<h1 align=/"center/">" + title + "</h1>/n" +
                    "<u1>/n" +
                    "<li><b>站点名</b>" +
                    name + "/n" +
                    "  <li><b>网址</b>:"
                   + request.getParameter("url") + "/n" +
                    "</ul>/n" +
                    "</body></html>");
    }

    // 处理Post
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        doGet(request, response);
    }
}

过滤器

Servlet过滤器可以动态的拦截响应。

变换中间的响应的信息。

其中过滤器是实现javax.servlet.Filter 的类。

该接口定义了以下的三个方法

public void doFilter (ServletRequest, ServletResponse, FilterChain)

当客户端请求匹配的URL的时候,容器会先调用过滤器的doFilter方法。其中FilterChain用于访问下一个过滤器

public void init(FilterConfig filterConfig)

该方法,可以获得当前的FilterConfig对象

public void destroy()

在销毁过滤器实例前销毁该方法

先写过滤器

import javax.servlet.*;
import java.io.IOException;
import java.util.*;

public class LogFilter implements Filter {
    public void  init(FilterConfig config) throws ServletException {
        // 获取初始化参数
        String site = config.getInitParameter("Site");

        // 输出初始化参数
        System.out.println("网站名称: " + site);
    }
    public void  doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {

        // 输出站点名称
        System.out.println("站点网址");

        // 把请求传回过滤链
        chain.doFilter(request,response);
    }
    public void destroy(){

    }
}

书写请求

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

public class DisplayHeader extends HttpServlet {
    // 创建GET请求方法
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        // 设置响应内容类型
        response.setContentType("text/html;charset=UTF-8");

        PrintWriter out = response.getWriter();
        String title = "HTTP Header 请求实例 - 菜鸟教程实例";
        String docType =
                "<!DOCTYPE html> /n";
        out.println(docType +
                "<html>/n" +
                "<head><meta charset=/"utf-8/"><title>" + title + "</title></head>/n"+
                "<body bgcolor=/"#f0f0f0/">/n" +
                "<h1 align=/"center/">" + title + "</h1>/n" +
                "<table width=/"100%/" border=/"1/" align=/"center/">/n" +
                "<tr bgcolor=/"#949494/">/n" +
                "<th>Header 名称</th><th>Header 值</th>/n"+
                "</tr>/n");
        // 返回一个枚举,包含在请求头中的所有头名
        Enumeration headerNames = request.getHeaderNames();
        // 输出结果
        while(headerNames.hasMoreElements()){
            String paramName = (String)headerNames.nextElement();
            out.println("<tr><td>" + paramName + "</td>/n");
            String paramValue = request.getHeader(paramName);
            out.println("<td>" + paramValue + "</td></tr>/n");
        }
        out.println("<table>/n<body></html>");
    }
    // 处理POST
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

最后在配置文件中,配置好过滤器映射

在客户端请求访问后端资源前,拦截这些请求。

在服务器响应发送回客户端之前,处理这些响应。

有以下几种过滤器,身份验证过滤器,数据压缩过滤器,加密过滤器,触发资源访问事件,图像转换过滤器等。

作用:如把获取请求的用户名密码,进行逻辑处理此时,如果如果验证用户名密码不正确,禁止访问web浏览器资源的时候,此时过滤器将会提示。

其中配置文件如下

<?xml version="1.0" encoding="UTF-8"?>
<web-app 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_4_0.xsd"
          version="4.0">
   <filter>
       <!-- 过滤器所在的类名 -->
       <filter-name>LogFilter</filter-name>
       <filter-class>LogFilter</filter-class>
       <init-param>
           <!-- 设置参数值-->
           <param-name>Site</param-name>
           <param-value>xiao</param-value>
       </init-param>
   </filter>
   <!-- 设置过滤器网址 -->
   <filter-mapping>
       <filter-name>LogFilter</filter-name>
       <url-pattern>/*</url-pattern>
   </filter-mapping>
   <!-- 设置容器网址 -->
   <servlet>
       <servlet-name>DisplayHeader</servlet-name>
       <servlet-class>DisplayHeader</servlet-class>
   </servlet>
   <servlet-mapping>
       <!-- 访问的网址 -->
       <servlet-name>DisplayHeader</servlet-name>
       <url-pattern>/DisplayHeader</url-pattern>
   </servlet-mapping>
</web-app>

异常处理

在servlet抛出异常的时候,web容器会在使用了 exception-type中搜索与之相匹配的设置,在配置文件当中需要指定error-page对于特定异常和状态码做出相应的servlet调用。

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class ErrorHandler extends HttpServlet {
    // 处理get请求
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        Throwable throwable = (Throwable)request.getAttribute("javax.servlet.error.exception");
        Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
        String servletName = (String)request.getAttribute("javax.servlet.error.servlet_name");
        if(servletName == null){
            servletName = "Unknow";
        }
        String requestUrl = (String)request.getAttribute("javax.servlet.error.request_url");
        if(requestUrl == null){
            requestUrl = "Unknown";
        }
        // 设置响应类型
        response.setContentType("text/html;charset=UTF-8");

        PrintWriter out = response.getWriter();
        out.println("出现错误");

    }
    // POST
    public void doPost(HttpServletRequest request,
                       HttpServletResponse response) throws ServletException, IOException{
        doGet(request, response);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app 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_4_0.xsd"
           version="4.0">
  <servlet>
      <servlet-name>ErrorHandler</servlet-name>
      <servlet-class>ErrorHandler</servlet-class>
  </servlet>
    <servlet-mapping>
        <servlet-name>ErrorHandler</servlet-name>
        <url-pattern>/ErrorHandler</url-pattern>
    </servlet-mapping>
    <error-page>
        <error-code>404</error-code>
        <location>/ErrorHandler</location>
    </error-page>
    <error-page>
        <exception-type>java.lang.Throwable</exception-type>
        <location>/ErrorHandler</location>
    </error-page>
</web-app>

Session

由于HTTP协议是一种无状态的协议。即,无法保存会话。

Cookies

在web服务器中,分配session会话ID作为web客户端的ID。把ID保存进入Cookies中,再次访问的时候,通过Cookies来验证

表单字段

在表单字段中指定sessionID作为本次会话的ID

URL重写

在URL末尾追加额外数据标识用来标识session会话。

服务器会把session会话进行关联

Token

即,在登录成功以后,服务器端生成一个token,即,用户唯一身份标识,+ 事件戳,+ 签名,签名是由token的前几位,+ 盐 用哈希算法进行压缩,防止第三方进行查询。

对于服务器来说,当用户登录的时候,根据用户唯一身份标识UID,+ 时间戳 + 签名,即,通过Token的前几位+ 盐,使用哈希算法生成,返回给客户端,客户端可以使用localhost,或者Cookie保存Token,并且,对Token进行过期时间设置,再次访问的时候,直接对Token,再次进行加密验证即可。

JWT Token

服务器端验证,信息通过哈希算法进行加密,私钥保存在服务器端,加密结果发送客户端,加密字符格式为三个点号分割的字符串Token,即,头部载荷,签名,头部,载荷,签名通过base64进行解码,客户端拿到Token,保存进入 local storage 再次发送请求,附加到header,服务端对header进行验证。

Oauth

即,保存一个令牌,用这个令牌,授权特定的网站,内的特定的资源,特定的时段,特定的信息。

即,点击登录按钮,用户引导进入授权页面,用户授权,重定向到redirect_uri,并附带上code。

使用code获取access_token 使用access_token获取用户的基本信息。

cookie - session

登录系统以后,创建session,把session保存进入cookie,再次请求通过cookie获取session进行校验。

改良

用户输入密码,登录系统后验证,数据保存进入redis,redis中获取key,把返回给客户端,根据key到redis中获取认证信息。

例子

下面的栗子核心在于isNew,servlet根据isNew来进行判断此用户是新用户还是老用户,当为新用户的时候,将会调用setAttribute,把当前的key,和id保存进行服务器的session。

若为老用户,将会getAttribute获取visitCountKey的key值,中间涉及一次自动装箱,然后把值进行相加。

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SessionTrack extends HttpServlet {
    private static final long serialVersionUID = 1L;
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        // 创建Session对象
        HttpSession session = request.getSession(true);
        // 获取创建时间
        Date createTime = new Date(session.getCreationTime());
        // 获取网页最后一次访问时间
        Date lastAccessTime = new Date(session.getLastAccessedTime());
        // 设置日期输出格式
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // 设置标题
        String title = "title";
        Integer visitCount = new Integer(0);
        String visitCountKey = new String("visitCount");
        String userIDKey = new String("userID");
        String userID = new String("ming");
        // 获取保存在共享区的visitCountKey的value
        // 因为servlet创建的session为所有线程所共享的
        // 若为空,将会进行初始化
        if(session.getAttribute(visitCountKey) == null){
            session.setAttribute(visitCountKey, new Integer(0));
        }

        // 如果用户是第一次访问的时候,将会对session进行第一次的初始化
        if(session.isNew()){
            title = "ming";
            session.setAttribute(userIDKey, userID);
        }else{
            // 如果是老用户,对session保存的visitCountKey的key值进行加1操作
            // 获得上一个ID
            visitCount = (Integer)session.getAttribute(visitCountKey);
            // 对ID进行加1
            visitCount = visitCount + 1;
            // 获得当前的ID
            userID = (String)session.getAttribute(userIDKey);
        }
        // 从新进行设置
        session.setAttribute(visitCountKey, visitCount);

        // 设置响应内容类型
        response.setContentType("text/html;cahrset=UTF-8");
        PrintWriter out = response.getWriter();
        String docType = "<!DOCTYPE html>/n";
        out.println(docType +
                "<html>/n" +
                "<head><title>" + title + "</title></head>/n" +
                "<body bgcolor=/"#f0f0f0/">/n" +
                "<h1 align=/"center/">" + title + "</h1>/n" +
                "<h2 align=/"center/">Session 信息</h2>/n" +
                "<table border=/"1/" align=/"center/">/n" +
                "<tr bgcolor=/"#949494/">/n" +
                "  <th>Session 信息</th><th>值</th></tr>/n" +
                "<tr>/n" +
                "  <td>id</td>/n" +
                "  <td>" + session.getId() + "</td></tr>/n" +
                "<tr>/n" +
                "  <td>创建时间</td>/n" +
                "  <td>" +  df.format(createTime) +
                "  </td></tr>/n" +
                "<tr>/n" +
                "  <td>最后访问时间</td>/n" +
                "  <td>" + df.format(lastAccessTime) +
                "  </td></tr>/n" +
                "<tr>/n" +
                "  <td>用户 ID</td>/n" +
                "  <td>" + userID +
                "  </td></tr>/n" +
                "<tr>/n" +
                "  <td>访问统计:</td>/n" +
                "  <td>" + visitCount + "</td></tr>/n" +
                "</table>/n" +
                "</body></html>");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app 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_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>SessionTrack</servlet-name>
        <servlet-class>SessionTrack</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>SessionTrack</servlet-name>
        <url-pattern>/SessionTrack</url-pattern>
    </servlet-mapping>
</web-app>

数据库访问

使用最最原始的JDBC访问即可

原文  https://www.iming.info/servlet/
正文到此结束
Loading...