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访问即可