- Servlet 是Tomcat给后端程序员提供的开发服务器程序的API合集,借助这个更方便搭建网站的后端。
- Servlet提供的类和方法有很多主要使用的就是三个(HttpServlet、HttpServletRequest和HttpServletResponse)
1.HttpServlet:
- HttpServlet,编写servlet代码的时候第一步就是先创建类继承自HttpServlet,并重写某些方法:
核心方法 | 说明 |
init | 首次创建出HttpServlet实例会调用一次 |
destroy | 在HttpServlet实例不使用时调用一次 |
service | 收到HTTP请求的时候调用头一次 |
doGet | 收到GET请求的时候service调用doGet |
doPost | 收到POST请求的时候service来调用 |
doPut / doDelete | 收到这些请求的时候service来调用 |
- init 方法:创建出HttpServlet实例会调用一次,init方法的作用就是用来初始化。注意:HttpServlet的实例只是在程序启动的时候创建一次,而不是每次收到HTTP请求都重新创建实例。
- destroy方法:不一定真的能调用到!如果Tomcat关闭了,则不再调用HttpServlet了。
Tomcat的关闭:
1.杀进程, 类似于拔电源
比如点击idea中的红色方框、cmd直接点×、任务管理器结束任务......
2.通过8005端口给Tomcat发送一个关闭操作, 类似于走正常的程序关闭电脑
这个时候Tomcat就会正常关机就可以调用到destroy方法
- service方法:Tomcat收到请求实际上是先调用service,在service里面再去根据方法调用不同的doXxx。
- 【面试题】谈一谈Servlet的生命周期。
初始阶段实例化的时候,调用一次 init
结束销毁之前,调用一次 destroy
每次收到请求,调用 service
2.HttpServletRequest:
- 当Tomcat通过Socket api读取HTTP请求(字符串),并且按照HTTP协议的格式把字符串解析成HttpServletRequest对象。
- URL是唯一资源定位符;URI是唯一资源标识符。
- 常用方法:
方法 | 说明 |
String getProtocol() | 返回请求协议的名称和版本号 |
String getMethod() | 返回请求的HTTP方法 |
String getPrequestURI() | 返回URL中的一部分 |
String getContextPath() | 返回ContextPath |
String getQueryString() | 返回查询字符串 |
Enumeration getParameterNames() | |
String getParameter(String name) | 返回请求参数的值,参数不存在返回null |
String[ ] getParameterValues(String name) | 返回请求参数的所有值 |
Enumeration getHeaderNames() | 返回包含请求中所有header的key的枚举 |
String getCharacterEncoding() | 返回请求中使用的字符编码 |
String getContentType() | 返回请求中使用的文本类型和字符编码 |
int getContentLength() | 返回请求主体的长度,字节为单位 |
String getHeader(String name) | 返回指定请求头的值 |
InputStream getInputStream() | 用来读取query string和body的内容 |
- 请求的对象是服务器收到的内容,不应该修改;因此上面都是读的方法不是写的方法。
3.HttpServletRequest代码实例:
3.1.打印请求的内容:
代码如下:
1. import javax.servlet.ServletException; 2. import javax.servlet.annotation.WebServlet; 3. import javax.servlet.http.HttpServlet; 4. import javax.servlet.http.HttpServletRequest; 5. import javax.servlet.http.HttpServletResponse; 6. import java.io.IOException; 7. import java.util.Enumeration; 8. 9. @WebServlet("/showRequest") 10. public class showRequest extends HttpServlet { 11. @Override 12. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 13. //resp是响应的对象,setContentType给响应的ContentType设置了值html 14. //声明响应body是html结构的数据 15. resp.setContentType("text/html"); 16. 17. StringBuilder stringBuilder = new StringBuilder(); 18. 19. //getProtocol返回请求协议的版本 20. stringBuilder.append(req.getProtocol()); 21. //要将\n 替换成 <br> 否则html无法识别 22. // stringBuilder.append("\n"); 23. stringBuilder.append("<br>"); 24. 25. //getMethod返回请求中http方法的名称 26. stringBuilder.append(req.getMethod()); 27. stringBuilder.append("<br>"); 28. 29. //getRequestURI返回请求的uri 30. stringBuilder.append(req.getRequestURI()); 31. stringBuilder.append("<br>"); 32. 33. //getRequeatURL返回请求的url 34. stringBuilder.append(req.getRequestURL()); 35. stringBuilder.append("<br>"); 36. 37. //返回Context Path 38. stringBuilder.append(req.getContextPath()); 39. stringBuilder.append("<br>"); 40. 41. //返回查询字符串 42. stringBuilder.append(req.getQueryString()); 43. stringBuilder.append("<br>"); 44. 45. 46. //拼接header中的内容 47. //getHeaderNames返回一个包含请求中所有的键值对key的值 48. Enumeration<String> headerNames = req.getHeaderNames(); 49. while(headerNames.hasMoreElements()){ 50. String name = headerNames.nextElement(); 51. //getHeader以字符串的形式返回key对应的value值 52. String value = req.getHeader(name); 53. stringBuilder.append(name+ ": " + value); 54. stringBuilder.append("<br>"); 55. } 56. 57. resp.getWriter().write(stringBuilder.toString()); 58. } 59. }
3.2.获取请求中的重要参数
(query string中的值):
- 对于获取query string来说,核心的方法就是getParameter 。
- 如果在浏览器页面上已经手动对特殊符号包括汉字encode编码了,那么servlet getParameter方法会自动的针对encode的结果进行decode解码操作,不需要手动处理。注意一点:浏览器地址栏里用户输入的查询字符串中包含汉字,也不一定会出错(有的浏览器自动编码)。
- getParameter方法使用时,如果键值对不存在,得到的就是 null;如果键值对的值不存在,得到的就是 “ ”,例如查询字符串为?studentId=1&studentName= )。
- 如果不写Content-Type,浏览器就不知道用什么方式什么格式来解析。
代码如下:
1. import javax.servlet.ServletException; 2. import javax.servlet.annotation.WebServlet; 3. import javax.servlet.http.HttpServlet; 4. import javax.servlet.http.HttpServletRequest; 5. import javax.servlet.http.HttpServletResponse; 6. import java.io.IOException; 7. 8. @WebServlet("/getParameter") 9. public class getParameterServlet extends HttpServlet { 10. @Override 11. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 12. //获取query string中的键值对 13. //取值的时候,如果键值对不存在返回的是 null 14. //如果键存在,值不存在那么返回 “ ” 15. String studentId = req.getParameter("studentId"); 16. String studentName = req.getParameter("studentName"); 17. 18. System.out.println(studentId); 19. System.out.println(studentName); 20. 21. //如果不加这行代码,浏览器就会显示出 ???,这里是 = ,不能是 - 22. resp.setContentType("text/html; charset=utf8"); 23. 24. resp.getWriter().write(studentId + " , " + studentName); 25. } 26. }
3.3.获取请求中的重要参数
(body中的值):
分为俩种情况!
- (1)如果请求的body是x-www-form-urlencode格式,form表单、这里也使用getParameter来获取。
- 用 html 和 postman 分别来操作!
- 使用html来发送form请求,需要先在webapp目录下创建一个html。
- 服务器的代码:
- 上述的前端和后端代码运行后会出现问题
- 请求req这里设置的utf8是告诉servlet(tomcat)如何解析;响应resp这里设置的utf8是告诉浏览器如何解析。
- 请求req和响应resp的字符编码。
请求这里使用这个方法:
req.setCharacterEncoding("utf8");
响应建议使用第一种,
因为不止要设置字符集还要设置格式
resp.setContentType("text/html; charset = utf8");
//resp.setCharacterEncoding("utf8");
- 加字符编码的总体原则:不要让程序猜!最好的做法就是显示声明该声明的东西。
- 通过postman来构造一个POST请求,postman也是一个http客户端,和浏览器是对等的。请求req也要设置字符集,否则还是会有乱码。
后端代码如下:
1. import javax.servlet.ServletException; 2. import javax.servlet.annotation.WebServlet; 3. import javax.servlet.http.HttpServlet; 4. import javax.servlet.http.HttpServletRequest; 5. import javax.servlet.http.HttpServletResponse; 6. import java.io.IOException; 7. 8. @WebServlet("/getParameter") 9. public class getParameterServlet extends HttpServlet { 10. @Override 11. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 12. //如果不加上这段代码,req就会随机解析,因此页面上显示的就是乱码 13. req.setCharacterEncoding("utf8"); 14. 15. String studentId = req.getParameter("studentId"); 16. String studentName = req.getParameter("studentName"); 17. 18. System.out.println(studentId); 19. System.out.println(studentName); 20. 21. resp.setContentType("text/html; charset = utf8"); 22. resp.getWriter().write(studentId + ", " + studentName); 23. } 24. }
- (2)如果请求的body是json格式,需要先读取body中的内容,进一步的再来读取流对象(getInputStream),下一步解析json格式,但是servlet中没有内置json解析,因此我们需要到借助第三方库jackson。
- Jackson的使用需要我们知道一个类和俩个方法。一个ObjectMapper类,方法是readValue和writeValueAsString。
- readValue的作用是先读取输入流获取要解析的字符串,再把字符串按照json格式解析得到一组键值对(map),再根据类对象创建一个实例,再遍历类对象中的属性的名字,拿着名字去上面的map中查询,查到的value赋值到对应的对象的属性中,最后返回这个构造完成的对象。
- readValue要求键值对中键的名字和类的属性名要一一对应。
- 如果类属性多,请求的key少,不会报错,少的那个属性就是null。如果类属性少,请求的key多这里key的多指的是个数多,不是种类,结果就是最后那个。如果类属性少,请求的key多这里key多的是种类,那就会报错。
- 还可以使用writeValueAsString方法来返回响应。
- 对三种返回响应的代码抓包:
后端代码如下:
1. import com.fasterxml.jackson.databind.ObjectMapper; 2. 3. import javax.servlet.ServletException; 4. import javax.servlet.annotation.WebServlet; 5. import javax.servlet.http.HttpServlet; 6. import javax.servlet.http.HttpServletRequest; 7. import javax.servlet.http.HttpServletResponse; 8. import java.io.IOException; 9. 10. 11. class Student{ 12. //1.这个student类的属性必须是public或者带有 public 的getter 和 setter 13. // 否则jackson无法访问这个对象的属性 14. //2.这个类必须要有无参版本的构造方法 15. public int studentId; 16. public String studentName; 17. public String studentSex; 18. } 19. 20. @WebServlet("/json") 21. public class JsonServlet extends HttpServlet { 22. @Override 23. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 24. //此处假设请求中的body是json格式的 25. //{studentId:66,studentName:"王大锤"} 26. //jackson 一个类ObjectMapper 和俩个方法 27. //一个方法是readValue,把json格式的数据转换成 java 对象 28. //另外一个方法是writeValueAsString,把java对象转换成json格式的字符串 29. 30. ObjectMapper objectMapper = new ObjectMapper(); 31. //readValue的第一个参数可以是字符串,也可以是输入流 32. //第二个参数是类的对象 33. Student s = objectMapper.readValue(req.getInputStream(),Student.class); 34. System.out.println(s.studentId); 35. System.out.println(s.studentName); 36. System.out.println(s.studentSex); 37. 38. // resp.setContentType("text/html; charset = utf8"); 39. // resp.getWriter().write(s.studentId + ", " + s.studentName +", " + s.studentSex); 40. 41. // //响应的数据是html格式的 42. // resp.setContentType("text/html; charset = utf8"); 43. // resp.getWriter().write(objectMapper.writeValueAsString(s)); 44. 45. 46. //响应的数据是json格式的 47. resp.setContentType("application/json; charset = utf8"); 48. resp.getWriter().write(objectMapper.writeValueAsString(s)); 49. 50. } 51. }
4.HttpServletResponse:
- HttpServletRequest的方法大部分都是前缀为get;HttpServletResponse的方法大部分都是前缀为set。
- 在doGet/doPost等方法中,这里面的HttpServletResponse对象都是空的。因此这些方法都是set系列也就好理解了。
- 常用方法:
方法 | 说明 |
void setStatus() | 设置状态码 |
void setHeader(String name,String value) | 设置一个header字段,如果已经存在,会覆盖 |
void addHeader(String name,String value) | 设置一个header字段,如果存在,会再创建一个 |
void setContentType() | 设置发送给客户端的响应的内容类型+字符编码 |
void setCharacterEncoding() | 设置发送给客户端的响应的字符编码 |
void sendRedirect(String location) | 重定向 |
PrintWriter getWriter() | 用于往body中写入文本格式数据 |
OutputStream getOutputStream() | 用于往body中写入二进制数据 |
- 状态码和header的设置要放到getWriter和getOutputStream前面。
5.HttpServletResponse代码示例:
5.1.设置不同的状态码
1. import javax.servlet.ServletException; 2. import javax.servlet.annotation.WebServlet; 3. import javax.servlet.http.HttpServlet; 4. import javax.servlet.http.HttpServletRequest; 5. import javax.servlet.http.HttpServletResponse; 6. import java.io.IOException; 7. 8. @WebServlet("/status") 9. public class StatusServlet extends HttpServlet { 10. @Override 11. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 12. //约定一下浏览器的query string传一个参数过来 13. //127.0.0.1:8080/servlet/status?type=1 14. //如果type=1返回200,如果为2返回404 15. String type = req.getParameter("type"); 16. 17. if(type.equals("1")){ 18. resp.setStatus(200); 19. }else if(type.equals("2")){ 20. resp.setStatus(404); 21. }else if(type.equals("3")){{ 22. resp.setStatus(500); 23. }}else{ 24. resp.setStatus(504); 25. } 26. } 27. }
5.2.设置响应的header,实现页面的自动刷新
- 在header中设置refresh属性,值是一个秒数,浏览器会在时间到了之后自动刷新
- 尽管我设置了3秒刷新一次,但是并不是精确的3000ms,会比3000略多一点
1. import javax.servlet.ServletException; 2. import javax.servlet.annotation.WebServlet; 3. import javax.servlet.http.HttpServlet; 4. import javax.servlet.http.HttpServletRequest; 5. import javax.servlet.http.HttpServletResponse; 6. import java.io.IOException; 7. 8. 9. @WebServlet("/autoRefresh") 10. public class AutoRefreshServlet extends HttpServlet { 11. @Override 12. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 13. //直接返回响应就行 14. resp.setHeader("refresh","3"); 15. resp.getWriter().write(System.currentTimeMillis()+""); 16. } 17. }
5.3.构造一个重定向响应
- 可以设置header中的Location属性,实现跳转;也可以使用sendRedirect方法。
1. import javax.servlet.ServletException; 2. import javax.servlet.annotation.WebServlet; 3. import javax.servlet.http.HttpServlet; 4. import javax.servlet.http.HttpServletRequest; 5. import javax.servlet.http.HttpServletResponse; 6. import java.io.IOException; 7. 8. @WebServlet("/redirect") 9. public class RedirectServlet extends HttpServlet { 10. @Override 11. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 12. //进行重定向,收到请求,跳转到哔哩哔哩 13. //第一种写法 14. //resp.setStatus(302); 15. //resp.setHeader("Location","http://www.bilibili.com"); 16. 17. //第二种写法 18. resp.sendRedirect("http://www.bilibili.com"); 19. } 20. }
6.代码案例:
- 实现表白墙:在页面上点击提交,希望在服务器这里保存数据,把数据发送给服务器;当关闭页面再启动,需要从服务器上读取之前保存过的数据,在页面上显示出来。
- 先要规划好请求和响应的细节。
- 提供俩种版本:顺序表和数据库。
使用顺序表的版本:
1. import com.fasterxml.jackson.databind.ObjectMapper; 2. 3. import javax.servlet.ServletException; 4. import javax.servlet.annotation.WebServlet; 5. import javax.servlet.http.HttpServlet; 6. import javax.servlet.http.HttpServletRequest; 7. import javax.servlet.http.HttpServletResponse; 8. import java.io.IOException; 9. import java.util.ArrayList; 10. 11. @WebServlet("/message") 12. public class MessageServlet extends HttpServlet { 13. public ObjectMapper objectMapper = new ObjectMapper(); 14. public ArrayList<Message> arrayList = new ArrayList<>(); 15. 16. @Override 17. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 18. resp.setContentType("text/json; charset=utf8"); 19. resp.getWriter().write(objectMapper.writeValueAsString(arrayList)); 20. } 21. 22. //浏览器提交数据 23. @Override 24. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 25. //获取到body中的数据并且解析 26. Message message = objectMapper.readValue(req.getInputStream(),Message.class); 27. //将得到的数据保存 28. arrayList.add(message); 29. resp.setStatus(200); 30. 31. System.out.println("提交数据成功,form:" + message.getFrom() + 32. " to: " + message.getTo() + " Message: " + message.getMessage()); 33. } 34. } 35. class Message { 36. private String From; 37. private String To; 38. private String Message; 39. public String getFrom() { 40. return From; 41. } 42. public void setFrom(String from) { 43. From = from; 44. } 45. public String getTo() { 46. return To; 47. } 48. public void setTo(String to) { 49. To = to; 50. } 51. public String getMessage() { 52. return Message; 53. } 54. public void setMessage(String message) { 55. Message = message; 56. } 57. }
使用数据库的版本:
1. //使用这个类来封装DataSource 2. public class DBUtil { 3. private static volatile DataSource dataSource = null; 4. 5. public static DataSource getDataSource(){ 6. if(dataSource == null){ 7. synchronized (DBUtil.class){ 8. if(dataSource == null){ 9. dataSource = new MysqlDataSource(); 10. ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java105?characterEncoding=utf8&useSSL=false"); 11. ((MysqlDataSource)dataSource).setUser("root"); 12. ((MysqlDataSource)dataSource).setPassword("111111"); 13. } 14. } 15. } 16. return dataSource; 17. } 18. 19. private DBUtil() {} 20. } 21. 22. import com.fasterxml.jackson.databind.ObjectMapper; 23. import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; 24. 25. import javax.servlet.ServletException; 26. import javax.servlet.annotation.WebServlet; 27. import javax.servlet.http.HttpServlet; 28. import javax.servlet.http.HttpServletRequest; 29. import javax.servlet.http.HttpServletResponse; 30. import javax.sql.DataSource; 31. import java.io.IOException; 32. import java.sql.Connection; 33. import java.sql.PreparedStatement; 34. import java.sql.ResultSet; 35. import java.sql.SQLException; 36. import java.util.ArrayList; 37. import java.util.List; 38. 39. @WebServlet("/message") 40. public class MessageServlet extends HttpServlet { 41. // 这个对象在多个方法中都需要使用 42. private ObjectMapper objectMapper = new ObjectMapper(); 43. 44. // 负责让页面获取到数据 45. @Override 46. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 47. // 显式声明当前的响应数据格式 不要让客户端去猜!!! 48. resp.setContentType("application/json; charset=utf8"); 49. // 把 messageList 转成 json 字符串, 并且返回给页面就行了. 50. List<Message> messageList = null; 51. try { 52. messageList = load(); 53. } catch (SQLException e) { 54. e.printStackTrace(); 55. } 56. resp.getWriter().write(objectMapper.writeValueAsString(messageList)); 57. } 58. 59. // 提交数据 60. @Override 61. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 62. // 获取到 body 中的数据并解析 63. Message message = objectMapper.readValue(req.getInputStream(), Message.class); 64. // 把这个 message 保存一下. 简单的办法就是保存在内存中. 65. // messageList.add(message); 66. try { 67. save(message); 68. } catch (SQLException e) { 69. e.printStackTrace(); 70. } 71. resp.setStatus(200); 72. 73. System.out.println("提交数据成功: from=" + message.getFrom() 74. + ", to=" + message.getTo() + ", message=" + message.getMessage()); 75. } 76. 77. private List<Message> load() throws SQLException { 78. // 从数据库查询数据 79. 80. // 1. 先有一个数据源 81. DataSource dataSource = DBUtil.getDataSource(); 82. 83. // 2. 建立连接 84. Connection connection = dataSource.getConnection(); 85. 86. // 3. 构造 SQL 87. String sql = "select * from message"; 88. PreparedStatement statement = connection.prepareStatement(sql); 89. 90. // 4. 执行 SQL 91. ResultSet resultSet = statement.executeQuery(); 92. 93. // 5. 遍历结果集合 94. List<Message> messageList = new ArrayList<>(); 95. while (resultSet.next()) { 96. Message message = new Message(); 97. message.setFrom(resultSet.getString("from")); 98. message.setTo(resultSet.getString("to")); 99. message.setMessage(resultSet.getString("message")); 100. messageList.add(message); 101. } 102. 103. // 6. 关闭连接 104. statement.close(); 105. connection.close(); 106. return messageList; 107. } 108. 109. private void save(Message message) throws SQLException { 110. // 把数据保存到数据库中 111. 112. // 1. 先有一个数据源 113. DataSource dataSource = DBUtil.getDataSource(); 114. 115. // 2. 建立连接 116. Connection connection = dataSource.getConnection(); 117. 118. // 3. 构造 SQL 119. String sql = "insert into message values(?, ?, ?)"; 120. PreparedStatement statement = connection.prepareStatement(sql); 121. statement.setString(1, message.getFrom()); 122. statement.setString(2, message.getTo()); 123. statement.setString(3, message.getMessage()); 124. 125. // 4. 执行 SQL 126. int ret = statement.executeUpdate(); 127. System.out.println("ret = " + ret); 128. 129. // 5. 关闭连接 130. statement.close(); 131. connection.close(); 132. } 133. }
如果对您有帮助的话,
不要忘记点赞+关注哦,蟹蟹
如果对您有帮助的话,
不要忘记点赞+关注哦,蟹蟹
如果对您有帮助的话,
不要忘记点赞+关注哦,蟹蟹