{"id":18812357,"url":"https://github.com/mozhu811/learning-servlet","last_synced_at":"2025-08-19T20:17:30.390Z","repository":{"id":36352128,"uuid":"142743565","full_name":"mozhu811/learning-servlet","owner":"mozhu811","description":":frog::gun: 老年人的Servlet代码","archived":false,"fork":false,"pushed_at":"2022-06-20T23:17:05.000Z","size":1375,"stargazers_count":67,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-06-01T07:51:06.072Z","etag":null,"topics":["java","servlet"],"latest_commit_sha":null,"homepage":"https://cruii.io/archives/2019111907155860383","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mozhu811.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-07-29T08:26:05.000Z","updated_at":"2024-06-14T03:23:31.000Z","dependencies_parsed_at":"2022-07-29T21:09:59.855Z","dependency_job_id":null,"html_url":"https://github.com/mozhu811/learning-servlet","commit_stats":null,"previous_names":["mozhu811/learning-servlet"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mozhu811/learning-servlet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mozhu811%2Flearning-servlet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mozhu811%2Flearning-servlet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mozhu811%2Flearning-servlet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mozhu811%2Flearning-servlet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mozhu811","download_url":"https://codeload.github.com/mozhu811/learning-servlet/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mozhu811%2Flearning-servlet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271215037,"owners_count":24720098,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-19T02:00:09.176Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","servlet"],"created_at":"2024-11-07T23:32:18.442Z","updated_at":"2025-08-19T20:17:30.362Z","avatar_url":"https://github.com/mozhu811.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"* [项目记录](#项目记录)\n* [HTTP协议相关](#http协议相关)\n * [HTTP协议的特征](#http协议的特征)\n * [请求类型](#请求类型)\n    * [GET和POST的区别](#get和post的区别)\n * [HTTP数据包](#http数据包)\n    * [GET方式提交（Java代码控制台打印为例）](#get方式提交java代码控制台打印为例)\n* [Servlet继承结构](#servlet继承结构)\n* [Servlet的生命周期](#servlet的生命周期)\n * [Tomcat执行Servlet过程(伪代码)](#tomcat执行servlet过程伪代码)\n* [Servlet的作用](#servlet的作用)\n * [Servlet如何获取表单数据](#servlet如何获取表单数据)\n * [解决Servlet中的中文乱码问题](#解决servlet中的中文乱码问题)\n * [关于Servlet的线程安全的问题](#关于servlet的线程安全的问题)\n * [Servlet文件上传](#servlet文件上传)\n * [自启动的Servlet](#自启动的servlet)\n * [Servlet的常见对象](#servlet的常见对象)\n    * [ServletContext](#servletcontext)\n       * [ServletContext的主要用法](#servletcontext的主要用法)\n    * [ServletConfig](#servletconfig)\n    * [Cookie](#cookie)\n       * [什么是Cookie](#什么是cookie)\n       * [Cookie中如何传递中文信息](#cookie中如何传递中文信息)\n    * [HttpSession](#httpsession)\n       * [什么是HttpSession](#什么是httpsession)\n       * [HttpSession的运行过程](#httpsession的运行过程)\n       * [HttpSession的生命周期](#httpsession的生命周期)\n* [编写一个自定义Servlet](#编写一个自定义servlet)\n * [自定义Servlet类继承HttPServlet,并且重写doGet和doPost方法](#自定义servlet类继承httpservlet并且重写doget和dopost方法)\n * [编写到运行Servlet的步骤（不适用IDE的方法）](#编写到运行servlet的步骤不适用ide的方法)\n## 项目记录\n+ myServlet01: 实现简单的Servlet的Hello World\n+ myServlet02: 实现三种乱码处理方案\n+ myServlet03: 实现通过Servlet的API获取用户浏览器的基本信息\n+ myServlet04: 实现通过IO流向客户端传输图片并展示\n+ myServlet05: 实现文件下载\n+ myServlet06: 实现通过JDBC对数据库进行操作,待施工\n\n本笔记为个人学习思考解惑记录,多方面引用互联网资料。\n若有纰漏,欢迎指正。\n\n## HTTP协议相关\n### HTTP协议的特征\n\n1. 单向性：客户端和服务端建立连接依靠于客户端发送请求，如果客户端不发送请求，服务端不会主动发送数据到客户端\n2. 无状态：HTTP对于事务处理没有记忆能力。无法“断点续传”\n3. 灵活：HTTP允许传输任意类型的数据对象。正在传输的类型有Content-Type加以标记\n4. 简单快速：客户端向服务器发送请求时，只需传送请求方法和路径。请求方法常用的有GET,POST,HEAD,PUT,DELETE。每种方法规定了客户端与服务器建立连接的类型不同。由于HTTP协议简单，使得HTTP服务器的程序规模小，因而通信速度快\n\n------------\n\n\n### 请求类型\n#### GET和POST的区别\n\n1. GET方式提交表单时，表单数据会在地址栏显示。而POST不会\n2. GET方式提交表单时，表单数据长度是有限的（地址栏长度有限）。而POST理论上没有限制\n3. GET方式提交表单时，表单数据都是以字符方式提交。而POST既可以用字符也可以用字节，默认用字符。\n4. GET方式提交表单会在Http数据包中的第一行出现，而POST提交表单会在空一行的body中出现\n5. GET请求能够被缓存，POST请求不能被缓存下来\n\n------------\n\n\n### HTTP数据包\n#### GET方式提交（Java代码控制台打印为例）\n\n```java\npublic class App {\n    public static void main(String[] args) {\n        try {\n            ServerSocket ss = new ServerSocket(8080);\n            Socket s = ss.accept();\n            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));\n            String str;\n            while ((str = br.readLine()) != null){\n                System.out.println(str);\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n    }\n}\n```\n运行以上代码后，在浏览器输入localhost:8080  \n则在控制台打印以下内容\n```text\n          GET / HTTP/1.1\n          Host: localhost:8080\n          Connection: keep-alive\n          Upgrade-Insecure-Requests: 1\n          User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36\n          Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\n          Accept-Encoding: gzip, deflate, br\n          Accept-Language: zh-CN,zh;q=0.9\n          Cookie: Idea-7798877d=aa86ef31-e4df-4cc6-bf8d-66407314771c\n```\n\n------------\n\n\n## Servlet继承结构\n\n![image](https://www.crzmy.com/wp-content/uploads/2018/07/Servlet.png) \n\n------------\n\n\n## Servlet的生命周期\n\nServlet接口中定义了作为一个Servlet在整个生命周期中应该拥有三个阶段\n1. 初始化\n2. 服务 service\n3. 销毁 destroy \n\n```text\nServlet的生命周期是由容器管理的,Servlet初始化以后立即调用init()方法，开发者可以重写该方法让Servlet初始化以后执行相应的操作\n```\n\n------------\n\n\n### Tomcat执行Servlet过程(伪代码)\n\n当客户端请求Servlet的时,Tomcat获取HTTP数据包信息,得到了头部信息中的\n```text\nGET /myservlet/hello.do HTTP/1.1\n```\n假如有一个类存储HTTP相关信息\n```java\npublic class HttpInfo{\n    private String method;\n    private String uri;\n    private String protocol;\n    ....\n    /* getter and setter */\n}\n```\n```java\n// Tomcat新建一个ServerSocket对象,监听端口\nServerSocket socket = new ServerSocket(监听端口号);\nSocket s;\nboolean flag = true;\nwhile(flag){\n    // 获取对应客户端的Socket对象\n    s = socket.accept();\n}\n// 通过IO流去读取数据\nBufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(),\"iso-8859-1\");\nString line;\nHttpInfo info = new HttpInfo();\nHttpServletRequest request;\nHttpServletResponse response;\nwhile((line = br.readLine()) != null){\n    // Tomcat解析HTTP数据包\n    // 例:拆分头部第一行的信息,获取请求方法,URI,协议版本\n    String[] arr = line.split(\"\\\\s\");\n    info.setMethod(arr[0]);\n    // 存储的已经处理字符串后得到的uri\n    info.setUri(arr[1]);\n    info.setProtocol(arr[2]);\n    // 处理第一行以外的其他信息\n    ...\n    request = new HttpServetRequest();\n    request.setMethod(arr[0]);\n    // 例如获取表单数据\n    ...\n\n    // Tomcat解析客户端的相关信息,比如发送请求的客户端的IP地址\n    response.setSocket(s);\n}\n\n// 对请求的HTTP数据包解析完毕后,处理请求\n// Tomcat在启动时就会解析项目下的部署描述文件web.xml\n// 在里面我们在\u003cservlet-class\u003e标签里配置了我们Servlet类的全路径,通过反射获取到Class对象,进而通过Class.newInstance()方法生成实例化对象.\nClass clazz = Class.forName(配置文件中Servlet类的全路径);\n// 多态性,父类引用指向子类对象\nServlet obj = clazz.newInstance(); \n// Servlet对象初始化之后的相关操作\n// 比如,人到了公司打卡后,职工对象就初始化好了,但是你还不能直接去工作(service)\n// 还要去找到你的办公位置,还要把电脑开机等操作,这就是init()里应该做的事情\n// 调用的是重写后的init()方法\nobj.init(); \n// 说明是多线程的,只是例子,这种方法开启线程很蠢\nnew Thread(new St(request, response)).start();\n...\n...\n// 到了容器要销毁Servlet对象之前\n// 容器就会去调用destroy()方法\n// 还是那个上班的例子\n// 办公楼晚上要锁门了,要把所有人都赶走,但是职工必须得在赶走之前,把文档,代码这些保存好,然后把电脑关机,关掉办公室的灯等动作\n// 这就是destroy()方法做的类似事情\n```\n模拟Tomcat里多线程\n```java\npublic St implements Runnable{\n    private Servlet obj;\n    private HttpServletRequest request;\n    private HttpServletResponse response;\n\n    public St(Servlet obj, HttpServletRequest request, HttpServletResponse response){\n        this.obj = obj;\n        this.request = request;\n        this.response = response;\n    }\n    @Override\n    public void run(){\n        // 执行HttpServlet里的service()方法\n        this.obj.service(request, response);\n        // 执行后续操作\n        // 判断request里保存的请求方式\n        // GET POST ...\n        // 根据不同的方式请求不同的方法,以GET为例\n        // 会去调用重写后的doGet方法\n        ...\n    }\n}\n```\n\u003e 总结:\n\nServlet的生命周期是由容器管理的,分为init,service和destroy三个阶段.  \n当有客户端第一次访问这个Servlet时,容器会立即初始化这个Servlet对象,初始化结束以后立即调用init()方法,并且在新的线程中调用service()方法.  \n容器会将初始化后的Servlet对象进行缓存,当有客户端再次请求该Servlet时,容器不会再进行创建,而是直接在新的线程里调用service()方法.  \n当容器关闭时,容器会在销毁这个Servlet对象之前,调用一次destroy()方法.\n\n------------\n\n\n## Servlet的作用\n\n1. 获取表单数据\n2. 获取浏览器的附加信息\n3. 处理数据(本身不具备处理数据的能力,比如持久化.它是通过调用其他的处理数据的方式来实现的,比如JDBC...)\n4. 给客户端产生一个响应\n5. 在响应中添加附加信息\n\n------------\n\n\n### Servlet如何获取表单数据\n\n在HttpServletRequest里有几个方法.\n```java\n1. String getParameter(String name)\n\t这个方法处理键值对比如key=value,通过key来获取对应的value.但是这个方法不能获取页面上checkbox的value,因为他的数据格式是key=value1\u0026key=value2\u0026key=value3\n2. String[] getParameterValues(String name)\n\t该方法就可以处理checkbox的value,它可以获取对应key的所有value\n3. String getQueryingString(String str)\n\t这个方法可以获取URL中的查询字符串(?后面的字符串)\n4. Map getParameterMap()\n\t这个方法可以获取请求参数将其封装成Map数据格式\n```\n\n------------\n\n\n### 解决Servlet中的中文乱码问题\n\n首先要知道的是Tomcat的默认字符集是ISO-8859-1\n1. 通用,通过jdk的new String产生新的对应编码的String对象\n\n```java\n/*解决中文乱码问题 第一种方式\nGET和POST都有效,但不推荐,会有大量冗余代码*/\n\nString name = request.getParameter(\"name\");\n// 乱码\nSystem.out.println(name);\nname = new String(name.getBytes(\"iso-8859-1\"),\"utf-8\");\n// 正常\nSystem.out.println(name);\n```\n2. 只适用POST,通过HttpServletRequest的API\n\n```java\n/*解决中文乱码问题 第二种方式\n只适用于POST请求,使用request里的内置方法*/\n\nrequest.setCharacterEncoding(\"utf-8\");\nString name = request.getParameter(\"name\");\n// 正常\nSystem.out.println(name);\n```\n3. 通用,修改Tomcat配置文件server.xml\n\n```xml\n\u003cConnector port=\"8080\" protocol=\"HTTP/1.1\"\n           connectionTimeout=\"20000\"\n           redirectPort=\"8443\"\n           URIEncoding=\"utf-8\" /\u003e\n```\n```java\n/*解决中文乱码问题 第三种方式\n* 修改Tomcat配置文件server.xml\n * 在Connector节点添加URIEncoding=\"utf-8\"\n * */\nString value = request.getParameter(\"name\");\n// 正常\nSystem.out.println(value);\n```\n\n------------\n\n\n### 关于Servlet的线程安全的问题\n\nServlet是一个线程不安全的技术,在Servlet中定义成员变量时,如果一定要定义成员变量,那么以读取为主.尽量不要同时读同时写.如果一定有这样的需求,则需要加锁.\n~~interface SingeThreadModel~~ 是Servlet提供的一个标识接口,该接口标识实现了该接口的Servlet的运行方式会由并行化改为串行化.效率低下,所以该接口在解决线程安全的问题时,已经不推荐使用了.\n\n------------\n\n### Servlet文件上传\n\n1. 在实现文件上传是,表单的提交方式必须是POST方式提交,因为POST请求才支持让表单数据以字节的方式提交,而GET只能是字符提交.\n2. 在form表单中修改请求的信息,将原来默认的字符提交修改为字节提交:通过修改form标签中的enctype属性,将其值改为multipart/form-data,该属性表示当前表单为字节格式,当服务器接收到当前数据包的时候,则不会去解析,所以也无法使用request.getParameter()去获取表单数据.\n3. 在2的情况下,如果需要处理表单中的内容,需要通过request对象的getInputStream()方法来获取一个通信流,通过对流对象的操作完成相应的表单处理.但是相比更推荐使用Apache的common-fileupload组件.\n\n------------\n\n\n### 自启动的Servlet\n自启动的Servlet的实例化不依赖于客户端请求,而是依赖于容器.当容器启动时就回去初始化这个Servlet.\n在web.xml文件中在对应Servlet的\u003cservlet\u003e节点最后一行添加\n```xml\n\u003cload-on-startup\u003e1\u003c/load-on-startup\u003e\n```\n其中数字表示启动优先级,数字越小,优先级越高\n\n------------\n\n### Servlet的常见对象\n#### ServletContext\n##### ServletContext的主要用法\n1. 相对路径转绝对路径\n```java\nServletContext sc = this.getServletContext();\nString rootPath = sc.getRealPath(\"/file.txt\");\nFile file = new File(rootPath);\n```\n2. 获取容器附加信息\n```java\n// 获取Servlet的主版本号\nint getMajorVersion();\n// 获取Servlet的副版本号\nint getMinorVersion();\n// 获取服务器的版本信息\nString getServerInfo();\n...\n...\n```\n3. 全局容器\nServlet通过以下两个API完成对自身的添加和读取数据的操作.\n```java\n// 添加数据\nvoid setAttribute(String name, Object value);\n// 获取数据\nObject getAttribute(String name);\n```\n注意:\n建议不要存储业务数据,数据会随着生命周期一直在内存中,增大了服务器的负担.其次也避免了与数据库数据同步的问题.\n\n4. 读取web.xml里的配置信息\n```xml\n\u003ccontext-param\u003e\n    \u003cparam-name\u003ekey\u003c/param-name\u003e\n    \u003cparam-value\u003evalue\u003c/param-value\u003e\n\u003c/context-param\u003e\n```\n通过以下方式可以获取该节点信息\n```java\nServletContext sc = this.getServletContext();\nString value = sc.getInitParameter(\"key\");\n```\n该配置信息是全局可访问,可以配置多个\u003ccontext-param\u003e,但是在一个\u003ccontext-param\u003e里只能有一个key/value配置.\n#### ServletConfig\n作用:用于读取在web.xml的\u003cservlet\u003e节点中的配置信息.在\u003cservlet\u003e节点中可以加入\u003cinit-param\u003e节点.\n\t\n```xml\n\u003cservlet\u003e\n    \u003cservlet-name\u003eMyServlet\u003c/servlet-name\u003e\n    \u003cservlet-class\u003ecom.ray.MyServlet\u003c/servlet-class\u003e\n    \u003cinit-param\u003e\n        \u003cparam-name\u003ekey\u003c/param-name\u003e\n        \u003cparam-value\u003evalue\u003c/param-value\u003e\n    \u003c/init-param\u003e\n\u003c/servlet\u003e\n```\n通过以下方式可以获取配置的key/value\n```java\n// 手动写key获得value\nServletConfig config = this.getServletConfig();\nString value = sc.getInitParameter(\"key\")\n\n// 可以使用另一个方法来获得所有的key/value\nServletConfig config = this.getServletConfig();\nEnumeration parameterNames = sc.getInitParameterNames();\nString value = \"\";\nwhile (parameterNames.hasMoreElements()){\n    value = sc.getInitParameter(parameterNames.nextElement());\n    System.out.println(value);\n}\n```\n同样在一个\u003cservlet\u003e节点中可以配置多个\u003cinit-param\u003e,但是一个\u003cinit-param\u003e只能配置一个key/value信息.  \n但是这个配置信息是只能在对应配置的Servlet才能访问到.其他Servlet无法访问.\n#### Cookie\n##### 什么是Cookie\nCookie是一个依赖于客户端维持会话状态的对象  \nCookie的特点:如果程序需要给客户端浏览器返回一个Cookie,那么则需要自己来创建;Cookie的结构为key/value结构.\nCookie分为两种:\n1. 状态Cookie: 随着浏览器的生命周期存在,浏览器关闭,则对象消失\n2. 持久化Cookie: Cookie对象持久化到磁盘中,当Cookie的存活时间到达时,浏览器不会再在请求中传递该Cookie.对于这些Cookie文件,浏览器会自己管理.通过setMaxAge(int s)方法来将Cookie持久化.\n\n当需要使用Cookie对象向客户端浏览器传递数据,数据本身不能是中文.  \n当客户端浏览器请求Servlet是,客户端浏览器会将该服务器以前写回的所有Cookie对象在请求中传递.  \n  \n可以通过以下简单的实例来实现判断用户访问\n```java\n@Override\nprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n    request.setContentType(\"text/html;charset=utf-8\");\n    PrintWriter pw = response.getPrintWriter();\n    Cookie[] cookies = request.getCookies();\n    if(cookies == null || cookies.length \u003c= 0){\n        pw.println(\"欢迎光临!\");\n        Cookie c = new Cookie(\"cookie\",\"cookie\");\n        // 持久化Cookie,如果没有这一句则是状态Cookie\n        c.setMaxAge(120);\n        response.addCookie(c);\n    } else {\n        pw.println(\"欢迎回来!\");\n    }\n}\n\n@Override\nprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n        this.doPost(request, response);\n}\n```\n\n##### Cookie中如何传递中文信息\n1. 通过可逆的加密算法\n```java\n/* 使用一个Java的加密字符串工具包 EncryptUtils,自行百度或编写 */\n....\nCookie[] cookies = request.getCookies();\nif(cookies == null || cookies.length\u003c=0){\n    String str = \"今天是二零一八年七月十二日\";\n    // 加密字符串\n    String ciphertext = EncryptUtils.encrypt(str);\n    Cookie c = new Cookie(\"cookie\", ciphertext);\n    c.setMaxAge(60*60*24);\n    response.addCookie(c);\n} else {\n    pw.println(\"欢迎回来!\");\n    Cookie c = null;\n    for(Cookie cookie : cookies){\n        if(\"cookie\".equals(cookie.getName())){\n            c = cookie;\n            break;\n        }\n    }\n    if(c != null){\n        // 解密字符串\n        String text = EncryptUtils.decrypt(c.getValue());\n        pw.println(\"欢迎回来!\" + text);\n    }\n}\n```\n2. 通过字符编码\n```java\n/* \n使用了java.net包下的操作URL编码的包\n修改了上面代码的加密解密两行,其他完全一致 */\n....\nCookie[] cookies = request.getCookies();\nif(cookies == null || cookies.length\u003c=0){\n    String str = \"今天是二零一八年七月十二日\";\n    // 加密字符串\n    String ciphertext = URLEndoder.encode(str, \"utf-8\");\n    Cookie c = new Cookie(\"cookie\", ciphertext);\n    // 单位是秒,60 * 60 * 24 = 1天\n    c.setMaxAge(60*60*24);\n    response.addCookie(c);\n} else {\n    pw.println(\"欢迎回来!\");\n    Cookie c = null;\n    for(Cookie cookie : cookies){\n        if(\"cookie\".equals(cookie.getName())){\n            c = cookie;\n            break;\n        }\n    }\n    if(c != null){\n        // 解密字符串\n        String text = URLDecoder.decode(c.getValue(), \"utf-8\");\n        pw.println(\"欢迎回来!\" + text);\n    }\n}\n```\n\n------------\n\n\n#### HttpSession\n##### 什么是HttpSession\nHttpSession对象可以建立客户端与服务器之间的对话,但会话是否建立.取决于服务器是否为这个客户端创建了HttpSession对象.如果是,则HttpSession就表示当前客户端与服务器的会话已经建立.进而该HttpSession对象只为该客户端使用,不会与其他客户端共享.\n\n##### HttpSession的运行过程\n首先HttpServletRequest中有两个获取HttpSession的方法,源码如下,删除了部的注释\n\n```java\n/**\n *\n * Returns the current HttpSession associated with\n * this request or, if there is no\n * current session and create is true, returns \n * a new session.\n *\n * If create is false\n * and the request has no valid HttpSession,\n * this method returns null.\n *\n * @param create true to create a new session for\n * this request if necessary;\n * false to return null if there's no current session\n *\n * @return \tthe HttpSession associated with this request or null\n * if create is false and the request has no valid session\n *\n */\n\npublic HttpSession getSession(boolean create);\n    \n/**\n *\n * Returns the current session associated with this request,\n * or if the request does not have a session, creates one.\n * \n * @return\tthe HttpSession associated with this request\n */\npublic HttpSession getSession();\n```\n阅读了上面的代码可以知道,getSession()和getSession(true)是同一个执行结果.  \n当客户端浏览器访问Servlet时,如果代码中调用了getSession(boolean create)或者getSession(),那么服务器会根据传递的参数来执行不同的逻辑.如果create参数是true或者没有参数则会在底层的SessionId/HttpSession对象映射中寻找是否有该客户端的HttpSession对象,如果没有则创建一个新的HttpSession对象并且把该对象的SessionId通过状态Cookie返回给客户端.如果create参数是false,同样也会在底层的SessionId/HttpSession对象映射中寻找是否有该客户端的HttpSession对象,但是如果没有对应的HttpSession对象,则会返回null.\n\n------------\n\n\n##### HttpSession的生命周期\n\n1. 创建: 当有客户端浏览器第一次请求Servlet时,该Servlet中调用了HttpServletRequest里的getSession(boolean create)或者request.getSession()方法时,则会创建一个HttpSession对象.\n2. 销毁:\n    1. 使用HttpSession里的invalidate()方法,该方法会直接是该Session直接销毁,并且取消绑定的任何对象.\n    ```java\n    /**\n     *\n     * Invalidates this session then unbinds any objects bound\n     * to it. \n     *\n     * @exception IllegalStateException\tif this method is called on an\n     *\t\t\t\t\talready invalidated session\n     *\n     */\n\n    public void invalidate();\n    ```\n    2. 修改Tomcat里的conf目录下的web.xml文件,默认是30分钟,超过配置时间则会销毁,对Tomcat里所有项目有效\n    ```xml\n    \u003csession-config\u003e\n        \u003csession-timeout\u003e30\u003c/session-timeout\u003e\n    \u003c/session-config\u003e\n    ```\n    3. 修改本项目的web.xml,同理也是超过配置时间则会自动销毁,但是仅对本项目有效.\n    ```xml\n    \u003csession-config\u003e\n        \u003csession-timeout\u003e1\u003c/session-timeout\u003e\n    \u003c/session-config\u003e\n    ```\n\n------------\n\n\n## 编写一个自定义Servlet\n\n### 自定义Servlet类继承HttPServlet,并且重写doGet和doPost方法\n\n```java\npublic class MyServlet extends HttpServlet {\n\n    public MyServlet() {\n        super();\n    }\n\n    @Override\n    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {\n        PrintWriter pw = response.getWriter();\n        pw.println(\n                \"\u003chtml\u003e\u003chead\u003e\u003ctitle\u003eHelloWorld\u003c/title\u003e\u003chead\u003e\u003cbody\u003e\" +\n                \"\u003cfont color='blue'\u003eHelloWorld\u003c/font\u003e\"+\n                \"\u003c/body\u003e\u003chtml\u003e\");\n        pw.close();\n    }\n\n    @Override\n    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n\n    }\n}\n\n```\n### 编写到运行Servlet的步骤（不适用IDE的方法）\n\n1. 编写自定义Servlet类继承HttpServlet\n2. 重写doGet和doPost方法\n3. 使用javac工具编译代码\n4. 在Tomcat的webapp目录下创建项目名称文件夹，在该文件夹下创建WEB-INF目录，继续在WEB-INF下创建classes目录，在里面创建包结构\n5. 把javac生成的.class文件放入对应的包结构目录下\n6. 在WEB-INF目录下创建web.xml\n7. 编辑web.xml,配置Servlet\n```xml\n\u003cweb-app\u003e\n\t\t\u003cdisplay-name\u003eArchetypeCreatedWebApplication\u003c/display-name\u003e\n\n\t\t\u003cservlet\u003e\n\t\t\t\u003cservlet-name\u003emyServlet\u003c/servlet-name\u003e\n\t\t\t\u003cservlet-class\u003ecom.ray.servlet.myservlet.MyServlet\u003c/servlet-class\u003e\n\t\t\u003c/servlet\u003e\n\n\t\t\u003cservlet-mapping\u003e\n\t\t\t\u003cservlet-name\u003emyServlet\u003c/servlet-name\u003e\n\t\t\t\u003curl-pattern\u003e*.do\u003c/url-pattern\u003e\n\t\t\u003c/servlet-mapping\u003e\n\u003c/web-app\u003e\n```\n8. 启动Tomcat容器\n9. 在浏览器地址栏输入\n```text\nlocalhost:8080/(servlet名字)/(请求路径)\n本例为：localhost:8080/myServlet/hello.do\n```\n10. 结果展示  \n![image](https://note.youdao.com/yws/api/personal/file/WEB087b923b25abb635430f047f7bebbd6b?method=download\u0026shareKey=64398d4c6780b770074e3f9d3fab43f7) \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmozhu811%2Flearning-servlet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmozhu811%2Flearning-servlet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmozhu811%2Flearning-servlet/lists"}