1、Servlet系列HTTP协议\tomcat\servlet
一、总体介绍
webserver:处理客户端提交过来的请求
A\B: 代表页面 response相应 请求的uri
有了servlet,问题再java代码里嵌入页面标签比较麻烦,因此有了jsp
随着时间发展 有了高级框架ssm,完成了请求的过程,并且动态生成页面
二、Tomcat
Tomcat服务器是一个免费的开放源代码的web应用服务器,属于轻量级应用服务器
我们之前javase运行一个代码,在IDEA里面run application运行了,自己能够控制什么时候运行的
对于客户端请求,我们不知道客户端什么时候发送请求,在webserver要准备一个长服务接收客户端请求
可以嵌入n个applet,比如有用户模块、商品模块、购物车模块,每一个模块都时一个applet,代码完成的逻辑放到webserver里
三、Tomcat的安装
/* https://tomcat.apache.org/ */
可执行程序
脚本启动和可执行程序启动,运行需要jdk支持
conf目录:配置文件
比如配置端口:server.xml
webapps
项目完成后打成war包
public class MyServlet extends HttpServlet {
会发现HttpServlet飘红了
这就ok了
四、servlet基本原理
是一种Web服务器端编程技术。
是实现了特殊接口的Java类。(HttpServlet)
由支持Servlet的Web服务器调用和启动运行。
一个Servlet负责对应的一个或一组URL访问请求,并返回相应的响应内容。
使用:
创建一个普通java文件
Java文件的类名实现HttpServlet
重写service的方法
在WEB-INF下的web.xml中添加请求与servlet类的映射关系
import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().write("first web project"); System.out.println("my first web project"); } }
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!--配置servlet的别名,同时在servlet-class配置项中添加servlet类的完全限定名 包名+类名--> <servlet> <servlet-name>myServlet</servlet-name> <servlet-class>com.mashibing.MyServlet</servlet-class> </servlet> <!--配置servlet跟请求的映射关系--> <servlet-mapping> <servlet-name>myServlet</servlet-name> <url-pattern>/first</url-pattern> </servlet-mapping> </web-app>
需要把当前web tomcat包加进来
有个问题,它是怎么找到的service,也没调doGet,确调用了service
xml配置也是只是指定到了com.mashibing.MyServlet
怎么实现的???
面向接口编程,继承了HttpServlet,实现了service方法
多态,父类对象指向子类引用
再写一个Myservice2.java
import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class MyServlet2 extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().write("hehe......"); } }
xml基于first加一个
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> ... ... ... <servlet> <servlet-name>second</servlet-name> <servlet-class>com.mashibing.MyServlet2</servlet-class> </servlet> <servlet-mapping> <servlet-name>second</servlet-name> <url-pattern>/second</url-pattern> </servlet-mapping> </web-app>
1、servlet访问流程
url:http://localhost:8080/firstweb/first
组成:
服务器地址:端口/虚拟项目名/servlet的别名
uri:虚拟项目名/servlet别名
过程:
浏览器发送请求到服务器,服务器根据请求URL地址中的URI信息在webapps目录下找到对应的项目文件夹,
然后再web.xml中检索对应的servlet,找到后调用并执行servlet
2、servlet的生命周期
import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(name = "ServletLife") public class ServletLife extends HttpServlet { /** * 完成servlet对象的初始化工作 * 在servlet接受到第一次请求的时候创建对象 * 生命周期:从第一次接受请求开始到服务器关系之后销毁 * 当在web.xml文件中配置了<load-on-startup>1</load-on-startup>,在开启tomcat的时候就会创建servlet对象,中间的数值表示优先级的意思 * @throws ServletException */ @Override public void init() throws ServletException { System.out.println("init"); } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().write("servlet life"); System.out.println("learn servlet life"); System.out.println(req.getParameter("name")); } @Override public void destroy() { System.out.println("我被销毁了"); } }
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> ... ... ... <servlet> <servlet-name>life</servlet-name> <servlet-class>com.mashibing.ServletLife</servlet-class> </servlet> <servlet-mapping> <servlet-name>life</servlet-name> <url-pattern>/life</url-pattern> </servlet-mapping> </web-app>
运行之后发送请求创建对象执行init
停止运行时销毁对象, 服务器不停,对象一致存在
3、service、doGet、doPost方法的区别
import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MethodServlet extends HttpServlet { public MethodServlet() { } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("我是post"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //System.out.println(10 / 0); System.out.println("我是get"); } protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("我是service"); super.service(req, resp); } }
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> ... ... ... <servlet> <servlet-name>MethodServlet</servlet-name> <servlet-class>com.mashibing.MethodServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MethodServlet</servlet-name> <url-pattern>/method</url-pattern> </servlet-mapping></web-app> </web-app>
如果发送post请求,写一个jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <form action="method" method="get"> 用户名:<input type="text" name="name" value=""/><br/> 密码:<input type="text" name="pwd" value=""/><br/> 爱好:<br/> <input type="checkbox" name="fav" value="1">java <input type="checkbox" name="fav" value="2">c# <input type="checkbox" name="fav" value="3">php <input type="submit" value="登录"> </form> </body> </html>
总结:
/** * Servlet类中可以有service方法, * 用来接受get或者post请求 * 如果service和doGet或者doPost同时存在,那么默认会调用service方法 * 如果同时又server,doGet和doPost方法,在service方法的实现中调用了super.service()会根据请求的方式跳转到doGet或者doPost * doget方法: * 用来接受get请求 * doPost方法: * 用来接受post请求 * * 总结: * 在编写servlet类的时候,不需要重新实现service方法,只需要重写doGet和doPost方法即可,用来接受post或者get请求 * */
Service方法
不管是get还是post请求方式,如果service方法存在,则优先执行service方法
doGet方法
在没有service的情况下,如果是get请求,调用doGet方法
doPost方法
在没有service的情况下,如果是post请求,调用doPost方法
常见错误:
404:访问资源不存在 请求路径跟web.xml中填写的请求不一致 请求路径的项目虚拟名称填写错误
405: 请求的方式跟servlet中支持的方式不一致
500:服务器内部错误 web.xml中servlet类的名称错误 servlet对应的处理方法中存在代码逻辑错误
五、request和response对象
1、request对象
请求行:
/** * HttpServletRequest用来存放客户端请求的参数 * 请求行 * 请求头 * 请求体 * * */ public class RequestServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("post请求"); this.doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("get请求"); //获取请求行数据 //获取请求中的请求方式 String method = request.getMethod(); System.out.println(method); //获取请求的完整地址 StringBuffer url = request.getRequestURL(); System.out.println(url); //获取请求中的资源路径 String uri = request.getRequestURI(); System.out.println(uri); //获取请求中的协议 String schema = request.getScheme(); System.out.println(schema); } }
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!--配置servlet的别名,同时在servlet-class配置项中添加servlet类的完全限定名 包名+类名--> <servlet> <servlet-name>request</servlet-name> <servlet-class>com.mashibing.RequestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>request</servlet-name> <url-pattern>/request</url-pattern> </servlet-mapping> </web-app>
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <form action="request" method="get"> 用户名:<input type="text" name="name" value=""/><br/> 密码:<input type="text" name="pwd" value=""/><br/> 爱好:<br/> <input type="checkbox" name="fav" value="1">java <input type="checkbox" name="fav" value="2">c# <input type="checkbox" name="fav" value="3">php <input type="submit" value="登录"> </form> </body> </html>
get提交
打印结果为:
/* get请求 GET http://localhost:8889/firstweb/request /firstweb/request http */
请求头
/** * HttpServletRequest用来存放客户端请求的参数 * 请求行 * 请求头 * 请求体 * * */ public class RequestServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("post请求"); this.doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("get请求"); //获取请求头数据 //根据key获取value的值 String userAgent = request.getHeader("User-Agent"); System.out.println(userAgent); //获取请求头信息中的所有key的枚举对象 Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()){ String key = headerNames.nextElement(); String value = request.getHeader(key); // System.out.println(headerNames.nextElement()); System.out.println(key+":"+value); } } }
get提交
打印结果为:
/* get请求 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36 host:localhost:8889 connection:keep-alive sec-ch-ua:" Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96" sec-ch-ua-mobile:?0 sec-ch-ua-platform:"Windows" upgrade-insecure-requests:1 user-agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36 accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 sec-fetch-site:same-origin sec-fetch-mode:navigate sec-fetch-user:?1 sec-fetch-dest:document referer:http://localhost:8889/firstweb/method.jsp accept-encoding:gzip, deflate, br accept-language:zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6 cookie:JSESSIONID=89A18C7377FF29B4DC804D4615A95E76; Pycharm-4c27e691=9fe5ccdf-9cdc-403e-93fc-1f8f84d8d045; Pycharm-407b6bae=bb7b0f2c-184a-45bc-98de-14b230158790; Idea-b0806bee=73cc7b1f-4f5f-407d-b188-f3e761ca2000 */
请求体
/** * HttpServletRequest用来存放客户端请求的参数 * 请求行 * 请求头 * 请求体 * * */ public class RequestServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("post请求"); this.doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("get请求"); //获取用户请求数据 //无论请求方式是post还是get,获取用户数据的方式不变 String name = request.getParameter("name"); String pwd = request.getParameter("pwd"); String fav = request.getParameter("fav"); System.out.println(name+":"+pwd+":"+fav); //获取用户数据中的所有key Enumeration<String> parameterNames = request.getParameterNames(); while(parameterNames.hasMoreElements()){ System.out.println(parameterNames.nextElement()); } //获取相同key的多个数据值,例如checkbox String[] parameterValues = request.getParameterValues("fav"); for (String str:parameterValues) { System.out.println("fav:"+str); } //其他常用方法 //获取远程客户端的地址 String remoteAddress = request.getRemoteAddr(); //获取远程客户端的主机名称 String remoteHost = request.getRemoteHost(); //获取远程客户端的端口号 int remotePort = request.getRemotePort(); System.out.println(remoteAddress+":"+remoteHost+":"+remotePort); String localAddr = request.getLocalAddr(); String localName = request.getLocalName(); System.out.println(localAddr+":"+localName); } }
打印结果为:
/* get请求 admin:fsadf:1 name pwd fav fav:1 0:0:0:0:0:0:0:1:0:0:0:0:0:0:0:1:57848 0:0:0:0:0:0:0:1:0:0:0:0:0:0:0:1 */
2、response对象
HttpServletResponse对象是服务器的响应对象,这个对象中封装了向客户端发送数据,发送响应头,发送响应状态码的方法。
public class ResponseServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("this is post"); this.doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("this is get"); //设置响应头,按照key-value键值对的方式来设置,如果存在相同的key,会把value的值覆盖 response.setHeader("hehe","haha"); response.setHeader("hehe","heihei"); //设置响应头,按照key-value键值对的方式来设置,如果存在相同的key,不会覆盖值 response.addHeader("beijing","shanghai"); response.addHeader("beijing","guangzhou"); //服务端返回的对象数据要按照一定的格式要求进行渲染,只有是html格式才会识别标签 // response.setHeader("Content-Type","text/html"); response.setHeader("Content-Type","text/plain"); // response.setContentType("text/html"); //设置响应状态码 // response.sendError(404,"not found"); response.getWriter().write("<b>java is easy</b>"); } }
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!--配置servlet的别名,同时在servlet-class配置项中添加servlet类的完全限定名 包名+类名--> <servlet> <servlet-name>response</servlet-name> <servlet-class>com.mashibing.ResponseServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>response</servlet-name> <url-pattern>/response</url-pattern> </servlet-mapping> </web-app>
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <form action="response" method="post"> 用户名:<input type="text" name="name" value=""/><br/> 密码:<input type="text" name="pwd" value=""/><br/> 爱好:<br/> <input type="checkbox" name="fav" value="1">java <input type="checkbox" name="fav" value="2">c# <input type="checkbox" name="fav" value="3">php <input type="submit" value="登录"> </form> </body> </html>
/* this is post this is get */
3、用户登陆示例:
/* 登录小项目流程: 1、使用原生的方式生成页面 2、客户端先发送请求,得到要登录的页面 3、用户填写相应的用户数据,发送请求给服务端 4、服务端接受到请求后进行逻辑处理 5、服务端将处理之后的结果返回给客户端 分层: web项目中包含层次的概念 control service dao */
control: 接收用户的请求, 并解析参数
service: 代码的逻辑处理
dao: 与数据库交互
entity: 用户类
control 调 service, service调dao
从里到外一次填写代码
dao下代码
public interface UserService { public User checkUser(User user); }
/** * 跟数据库发生交互 */ public class UserDaoImpl implements UserDao { @Override public User checkUser(User user) { //定义连接的对象 Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; User u = null; try { //加载驱动 Class.forName("com.mysql.jdbc.Driver"); //获取连接 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/login","root","123456"); //获取预处理块 pstmt = conn.prepareStatement("select * from user where name = ? and pwd =?"); //给?赋值 pstmt.setString(1,user.getName()); pstmt.setString(2,user.getPwd()); //执行sql语句 rs = pstmt.executeQuery(); //从resultset中获取结果值 while (rs.next()){ u = new User(rs.getInt("id"),rs.getString("name"),rs.getString("pwd")); } } catch (Exception e) { e.printStackTrace(); }finally { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { pstmt.close(); } catch (SQLException e) { e.printStackTrace(); } try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } return u; } }
service下代码
public interface UserService { public User checkUser(User user); }
import com.mashibing.dao.UserDao; import com.mashibing.dao.impl.UserDaoImpl; import com.mashibing.entity.User; import com.mashibing.service.UserService; public class UserServiceImpl implements UserService { UserDao userDao = new UserDaoImpl(); @Override public User checkUser(User user) { return userDao.checkUser(user); } }
control下代码
public class LoginServlet extends HttpServlet { UserService userService = new UserServiceImpl(); protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("gbk"); //获取请求数据 String name = request.getParameter("name"); String pwd = request.getParameter("pwd"); //封装对象 User user = new User(name,pwd); //调用service进行逻辑处理 User u = userService.checkUser(user); System.out.println(u); if(u!=null){ // response.getWriter().write("success"); // request.getRequestDispatcher("hello").forward(request,response); response.sendRedirect("hello"); }else{ // response.getWriter().write("failure"); // //设置响应编码 // response.setContentType("text/html"); // //获取响应的输出流对象 // PrintWriter out = response.getWriter(); // out.write("<html>"); // out.write("<head>"); // out.write("</head>"); // out.write("<body>"); // out.write("<form action='login' method='get'>"); // out.write("name:<input type='text' name='name' value='' ><br/>"); // out.write("pwd:<input type='text' name='pwd' value=''><br/>"); // out.write("<input type='submit' value='login'>"); // out.write("</form>"); // out.write("</body>"); // out.write("</html>"); //设置参数 request.setAttribute("str","用户名或者密码错误"); //请求servlet的时候,写相对路径,同时后续不需要逻辑代码的处理, 请求转发 request.getRequestDispatcher("page").forward(request,response); return ; } } }
PageServlet.java 返回用户输入框
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 PageServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("接受到客户端的请求,返回响应的登录页面"); String str = (String) request.getAttribute("str")==null?"":(String) request.getAttribute("str"); System.out.println(str); //设置响应编码 response.setContentType("text/html"); //获取响应的输出流对象 PrintWriter out = response.getWriter(); out.write("<html>"); out.write("<head>"); out.write("</head>"); out.write("<body>"); out.write("<font color='red' size='20px'>"+str+"</font>"); out.write("<form action='login' method='get'>"); out.write("name:<input type='text' name='name' value='' ><br/>"); out.write("pwd:<input type='text' name='pwd' value=''><br/>"); out.write("<input type='submit' value='login'>"); out.write("</form>"); out.write("</body>"); out.write("</html>"); } }
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!--用来获取登录页面的请求--> <servlet> <servlet-name>PageServlet</servlet-name> <servlet-class>com.mashibing.control.PageServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>PageServlet</servlet-name> <url-pattern>/page</url-pattern> </servlet-mapping> <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.mashibing.control.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> <servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>com.mashibing.control.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> </web-app>
4、servlet乱码问题解决
上面写的小程序没用到中文,如果中文就会出现乱码
/** * 处理乱码问题的方式 * 1、get请求 * 1、获取字符串之后使用new String(name.getBytes("iso-8859-1"),"utf-8") * 2、设置request的编码格式,request.setCharacterEncoding("utf-8"); 同时在server.xml中添加useBodyEncodingForURI=true的属性 * 3、在server.xml中添加URIEncoding="utf-8" * 2、post请求 * 1、request.setCharacterEncoing("utf-8") * 3、response响应编码 * response.setCharacterEncoding("gbk"); */ public class CharsetServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, IOException { System.out.println("post"); request.setCharacterEncoding("utf-8"); String name = request.getParameter("name"); System.out.println(name); response.setCharacterEncoding("gbk"); response.getWriter().write("欢迎你!"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, IOException { System.out.println("get"); //设置请求的编码格式 // request.setCharacterEncoding("utf-8"); String name = request.getParameter("name"); // System.out.println(new String(name.getBytes("iso-8859-1"),"utf-8")); System.out.println(name); } }
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>CharsetServlet</servlet-name> <servlet-class>com.mashibing.CharsetServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CharsetServlet</servlet-name> <url-pattern>/charset</url-pattern> </servlet-mapping> </web-app>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> </head> <body> <form action="charset" method="post"> <input type="text" name="name" value=""> <input type="submit" value="login"> </form> </body> </html>
六、servlet request的作用域
我们上面登陆的例子, 输入完用户名密码, 如果错误, 应该有个提示信息,用户名错误之类的
我们第一次请求请求的是
返回一个页面
输入完信息呢第二次请求
如果错误, 请求转发 请求page
为了实现不同servlet之间的数据共享, 通过 request.setAttribute 设置
可以在page里拿到配置
第一次获取的是null, 所以要做一个判断
七、servlet重定向
上面我们请求错误,做了一个请求转发, 那请求成功后,我们也不能直接返回一个success
应该跳转到某一个页面里面
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 HelloServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("gbk"); String name = request.getParameter("name"); PrintWriter out = response.getWriter(); out.write("<html>"); out.write("<head>"); out.write("</head>"); out.write("<body>"); out.write("<h1>欢迎"+name+"登录<h1>"); out.write("</body>"); out.write("</html>"); } }
可以请求转发
如果频繁点刷新,每个请求都带name password,每一次点击都相当于是两个请求
正常点击只需要刷新欢迎页面就可以,不用登陆请求
所以就需要重定向
七、cookie、session
1、cookie
如何解决重定向之后两次请求null的问题
就要用到cookie
http协议无状态
import javax.servlet.http.Cookie; import java.io.IOException; /** * cookie:用来处理客户端发送不同请求的时候如何使用相同的参数信息 * cookie的使用 * Cookie cookie = new Cookie("00001","beijing"); * response.addCookie(cookie); * 设置cookie的参数 * cookie.setMaxAge( int seconds) * cookie.setPath(String url) * 获取cookie对象 * Cookie[] cookies = request.getCookies() * * 特点: * 1、cookie是保存在浏览器端的数据名称 * 2、cookie分类:临时cookie,默认是存储在内存中的,所以当浏览器关闭的时候,cookie自动失效 * 持久化cookie,保存在浏览器的某个存储目录,当时间过期之后,才会失效 * 3、每一个cookie对象中保存一个key-value键值对的数据,想要存储多个k-v数据,需要创建多个Cookie对象 * */ public class CookieServlet extends javax.servlet.http.HttpServlet { protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { this.doGet(request,response); } protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("gbk"); //创建Cookie对象 Cookie cookie = new Cookie("00001","beijing"); Cookie cookie1 = new Cookie("0002","shanghai"); //给cookie对象添加时间有效期,单位是s cookie.setMaxAge(3*24*3600); //给cookie设置固定路径值 cookie1.setPath("/cookie/abc"); //将cookie设置到response对象中 response.addCookie(cookie); response.addCookie(cookie1); response.getWriter().write("学习cookie"); } }
重新发送一个请求
Cookie中也存在,保存成功了,浏览器重新打开也会存在
我么定义的cookie1是临时cookie所以没有
因为我们设置了setPath
这样就两个都会有了
现在已经设置好了,怎么获取数据呢??
public class GetCookieServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置编码 request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("gbk"); //获取cookie对象 Cookie[] cookies = request.getCookies(); if(cookies.length>0){ for (Cookie c: cookies) { String key = c.getName(); String value = c.getValue(); System.out.println(key+":"+value); } } } }
要判断getCookies()的长度, 否则直接访问会报空指针
功能实现:
实现用户的一周免登录
/* 用户免登录实现: 1、用户发送验证cookie信息的请求,编写对应的servlet进行处理 2、如果包含cookie,直接跳转到成功页面 3、如果不包含cookie信息,直接跳转到登录页面 */
第一次登陆成功需要创建cookie, 返回给客户端
在LoginServlet接收请求后通过service逻辑处理, 返回不为null后成功, 设置cookie, 跳转hello界面
UserDaoImpl 下的 getUserById方法
总结流程
url 输入/cookie 判断cookie是否存在, 不存在跳转page登陆界面, 存在cookie, 判断cookie中的key值是否为登陆成功设置的key值(uid: 1)
存在, 则获取key对应的value, 通过value值调用servers逻辑判断, 判断成功返回hello界面
2、session
概念:
Session表示会话,在一段时间内,用户与服务器之间的一系列的交互操作。
session对象:用户发送不同请求的时候,在服务器端保存不同请求共享数据的存储对象
特点
Session是依赖cookie技术的服务器端的数据存储技术
由服务器进行创建
每个用户独立拥有一个session对象
默认存储时间是30分钟
用户使用浏览器第一次向服务器发送请求, 服务器在接受到请求后, 调用对应的 Servlet 进行处理。 在处理过程中会给用户创建一个 session 对象, 用来存储用户请求处理相关的公共数据, 并将此 session 对象的 JSESSIONID 以 sessoin 的形式存储在浏览器中(临时存储, 浏览器关闭即失效)。 用户在发起第二次请求及后续请求时, 请求信息中会附带 JSESSIONID, 服务器在接收到请求后,调用对应的 Servlet 进行请求处理, 同时根据 JSESSIONID 返回其对应的 session 对象
创建sessoin对象
HttpSession session = request.getSession()
向Session对象中添加值
sessoin.setAttribute(String name,Object object)
获取Session中的值
session.getAttribute(String name)
设置sessoin属性
session.setMaxInactiveInterval(5)//设置存活时间
session.invalidate(); //session强制失效
import javax.servlet.http.HttpSession; import java.io.IOException; /** * * session * 作用: * 解决相同用户发送不同请求时的数据共享问题 * 特点: * 1、服务器端存储共享数据的技术 * 2、session需要依赖cookie技术 * 3、每个用户对应一个独立的session对象 * 4、每个session对象的有效时长是30分钟 * 5、每次关闭浏览器的时候,重新请求都会开启一个新的session对象,因为返回的JSESSIONID保存在浏览器的内存中,是临时cookie,所以关闭之后自然消失 * 使用: * 获取session对象 * HttpSession session = request.getSession(); * 修改session会话的保持时间 * session.setMaxInactiveInterval(int second); * 设置强制失效 * session.invalidate(); */ public class SessionServlet extends javax.servlet.http.HttpServlet { protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { this.doGet(request,response); } protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { //设置请求响应的编码格式 request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); System.out.println("接受到请求get"); //获取session对象 HttpSession session = request.getSession(); //设置session的有效时长 // session.setMaxInactiveInterval(5); 过期重新获取session_id //getid方法拿到JSESSIONID System.out.println(session.getId()); //设置session强制失效 // session.invalidate(); 每次请求都想要一个新的session对象 //向session中设置参数 session.setAttribute("111","zhangsan"); response.getWriter().write("学习session"); } }
相应头中会有一个Set-Cookie
控制台打印
下次访问就会携带cookie_id
获取session中的参数值
public class SessionServlet2 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置编码 request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); //获取session对象 HttpSession session= request.getSession(); //获取session中的参数值 String str = (String) session.getAttribute("111"); System.out.println(str); response.getWriter().write(str); } }
如果没有访问session,直接访问session2,就会引起空指针异常
用session解决之前出现过的null问题
第一次登陆成功创建session对象,设置参数值
hello页面拿到name值
这里没有完善判断第二次发送请求拿到cookie_id 是否与 session_id是否相等,只是简单演示了一下session
八、servletContext对象与ServletConfig对象
1、servletContext对象
request: 把客户端的请求参数包装到request对象里, 不同servlet流转的数据共享问题
cookie: 把数据保存在客户端的一项技术
session: 同一个用户发送不同请求的数据共享问题, 数据放到服务端
思考: 不同用户的数据共享怎么办???
不同用户为什么需要数据共享
浏览技术贴吧时, 会有当前有 多少人 访问
也不能放到cookie, 也不能放到session, 一个放到客户端,一个是每个用户的共享对象
用ServletContext来处理
仿照session的思路, 一个用户发送不同请求时, 获取到的是同一个对象
不同用户请求同一个服务器,能不能从同一台服务器里获取到同一个对象呢
运行在JVM上的每一个web应用程序都有一个与之对应的Servlet上下文(Servlet运行环境)
Servlet API提供ServletContext接口用来表示Servlet上下文,
ServletContext对象可以被web应用程序中的所有servlet访问
ServletContext对象是web服务器中的一个已知路径的根
ServletContext 对象由服务器进行创建, 一个项目只有一个对象。
不管在项目的任意位置进行获取得到的都是同一个对象,
那么不同用户发起的请求获取到的也就是同一个对象了, 该对象由所以用户共同拥有。
import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * * Servletcontext: * 作用: * 解决不同用户的数据共享问题 * 特点: * 1、由服务器创建 * 2、所有用户共享同一个ServletContext对象 * 3、所有的servlet都可以访问到同一个ServletContext中的属性 * 4、每一个web项目对应的是一个ServletContext * 用法: * 获取servletcontext对象 * //1 * ServletContext context = this.getServletContext(); * //2 * ServletContext context1 = this.getServletConfig().getServletContext(); * //3 * ServletContext context2 = request.getSession().getServletContext(); * 向ServletContext对象中设置属性值 * context.setAttribute(String key,Object value) * 获取属性值 * context.getAttribute(String key) * 其他用途 * 1、获取web,xml中配置的公共属性 * 在web.xml中添加公共属性 * <context-param> * <param-name>beijing</param-name> * <param-value>beautiful</param-value> * </context-param> * context.getInitParameter(String key) * 如果有多组公共属性,使用多个context-param标签 * 2、获取项目的虚拟目录路径 * context.getContextPath() * 3、获取某个资源的绝对路径 * context.getRealPath(String filename) */ public class ServletContextServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取servletContext对象 //1 ServletContext context = this.getServletContext(); //2 ServletContext context1 = this.getServletConfig().getServletContext(); //3 ServletContext context2 = request.getSession().getServletContext(); System.out.println(context==context1); System.out.println(context == context2); System.out.println(context1==context2); //设置属性值 context.setAttribute("111","zhangsan"); //从web.xml中获取参数值 String value = context.getInitParameter("china"); System.out.println(value); //获取某个文件的绝对路径 String path = context.getRealPath("web.xml"); System.out.println(path); //获取web项目的上下文路径 String path2 = context.getContextPath(); System.out.println(path2); } }
在web.xml中配置属性
打印结果为:
/* true true true great C:\kxq\E_file\java\code\servletcontext\out\artifacts\servletcontext_war_exploded\web.xml /context */
打印的context 指的是
测试是否共享
@WebServlet(name = "ServletContextServlet2") public class ServletContextServlet2 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext context = this.getServletContext(); String value = (String) context.getAttribute("111"); System.out.println(value); } }
打印结果为:
示例:
实现网站计数器
import javax.servlet.ServletContext; 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 NumServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置编码格式 request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); //获取Servletcontext对象 ServletContext context = this.getServletContext(); //获取属性值 Integer num = (Integer) context.getAttribute("num"); if(num==null){ context.setAttribute("num",1); }else{ //实现每次访问加1的功能 num++; //将num设置回servletcontext对象中 context.setAttribute("num",num); } //获取输出对象 PrintWriter out = response.getWriter(); out.write("<html>"); out.write("<head>"); out.write("</head>"); out.write("<body>"); out.write("用户访问的次数是"+context.getAttribute("num")+"次"); out.write("</body>"); out.write("</html>"); } }
用户第一次访问没有num, 需要判断, 以后的所有用户共享上下文, 都有num了, 都会走到else 进行 ++ 操作
关闭浏览器也无所谓会继续加
2、ServletConfig对象
思考:使用ServletContext对象可以获取web.xml中的全局配置文件,
在web.xml中,每个Servlet也可以进行单独的配置,那么该怎么获取配置信息呢?
使用ServletConfig对象
作用:
ServletConfig对象是Servlet的专属配置对象,每个Servlet都单独拥有一个ServletConfig对象 ,用来获取web.xml中的配置信息
使用: 获取ServletConfig对象 获取web.xml中的servlet配置信息
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>ServletConfigServlet</servlet-name> <servlet-class>com.mashibing.ServletConfigServlet</servlet-class> <init-param> <param-name>china</param-name> <param-value>beijing</param-value> </init-param> <init-param> <param-name>hebei</param-name> <param-value>shijiazhuang</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>ServletConfigServlet</servlet-name> <url-pattern>/config</url-pattern> </servlet-mapping> </web-app>
/** * * Servletconfig * 作用: * 方便每一个servlet获取自己单独的属性配置 * 特点: * 1、每一个servlet单独拥有一个servletConfig对象 * 使用: * 获取对象 * ServletConfig config = this.getServletConfig(); * 获取值 * config.getInitParameter("china"); * 获取所有的key值 * config.getInitParameterNames(); */ public class ServletConfigServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取对象 ServletConfig config = this.getServletConfig(); String value = config.getInitParameter("china"); System.out.println(value); //获取所有的key Enumeration<String> initParameterNames = config.getInitParameterNames(); while (initParameterNames.hasMoreElements()){ String key = initParameterNames.nextElement(); String value2 = config.getInitParameter(key); System.out.println(key+"----"+value2); } } }
打印结果为:
/* beijing hebei----shijiazhuang china----beijing */
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
2021-04-25 01-GO安装