Java Servlet 是运行在web服务器和应用程序上的程序,是一个中间层。
运行过程如图
即,当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进行垃圾回收。
第一次创建servlet时被调用,后续每次用户请求时不在调用。即,它是一次性的初始化。
public void init() throws ServletException{
// 初始化代码
}
为执行实际任务时的主要方法。
Servlet容器会调用service方法来处理客户端的请求,并把格式化后的响应返回给浏览器。
每次服务器接收到Servlet的时候,会产生一个新的线程来调用服务。
service方法是用来检查HTTP请求的类型。并在适当的时候,会调用适当的方法。
public void service(ServletRequest request, ServletResponse response) throws Exception{
// service 方法
}
service方法,容器将会调用,并且是在适当的时候调用doGet和doPost等方法。
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// Servlet 代码
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// Servlet 代码
}
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>
由于HTTP协议是一种无状态的协议。即,无法保存会话。
在web服务器中,分配session会话ID作为web客户端的ID。把ID保存进入Cookies中,再次访问的时候,通过Cookies来验证
在表单字段中指定sessionID作为本次会话的ID
在URL末尾追加额外数据标识用来标识session会话。
服务器会把session会话进行关联
即,在登录成功以后,服务器端生成一个token,即,用户唯一身份标识,+ 事件戳,+ 签名,签名是由token的前几位,+ 盐 用哈希算法进行压缩,防止第三方进行查询。
对于服务器来说,当用户登录的时候,根据用户唯一身份标识UID,+ 时间戳 + 签名,即,通过Token的前几位+ 盐,使用哈希算法生成,返回给客户端,客户端可以使用localhost,或者Cookie保存Token,并且,对Token进行过期时间设置,再次访问的时候,直接对Token,再次进行加密验证即可。
服务器端验证,信息通过哈希算法进行加密,私钥保存在服务器端,加密结果发送客户端,加密字符格式为三个点号分割的字符串Token,即,头部载荷,签名,头部,载荷,签名通过base64进行解码,客户端拿到Token,保存进入 local storage 再次发送请求,附加到header,服务端对header进行验证。
即,保存一个令牌,用这个令牌,授权特定的网站,内的特定的资源,特定的时段,特定的信息。
即,点击登录按钮,用户引导进入授权页面,用户授权,重定向到redirect_uri,并附带上code。
使用code获取access_token 使用access_token获取用户的基本信息。
登录系统以后,创建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访问即可