Day36
servlet生命周期
service方法前端发送请求,无论发送什么请求都会调用,如果get请求和post方法要分开处理,则不适用。
此时可以用doGet()方法和doPost()方法,如果客户端发送给当前的servlet请求方式是get就调用doGet方法,如果是post就调用doPost方法,参数仍然是req和resp。
请求不加/,servlet里要加,因为/表示跳回到上一级。
研究生命周期的目的:什么生命周期的时候该调用什么方法。
如:servlet在项目结束的时候被销毁,会调用destroy()方法,可以再这个时候调用各种回收的方法。
如何研究:servlet类里面写一个构造方法,打印信息,查看什么时候创建的对象。写一个init()方法,查看什么时候初始化(这个是父类调用)。写一个destroy()方法查看什么时候销毁。
发现:在打开页面的时候没有创建,但是在页面第一次提交(前端第一次发送请求)的时候,会按顺序调用无参构造、init()、doPost()或doGet()方法,后续多次发送请求会不断调用doPost()(或者doGet)方法。
注:(但是如果构造方法为有参构造,会报错:响应状态码为500。
响应状态码可以按第一个数组分为五大类:
1xx、2xx、3xx、4xx、5xx4xx:客户端错误
5xx:服务端错误
出现错误信息的时候看root cause
发现没有找到构造方法,因为写了有参构造系统不会默认实现无参构造。所以经验:不要写有参构造。)
销毁:服务器正常关闭时。
注:非正常关闭不是指报错。报错项目会崩掉但是服务器不会。如果手动删掉服务器则算作非正常关闭,不会调用destroy()方法。
深入servlet生命周期
Servlet调用底层逻辑完整顺序:
客户端将请求发送到Tomcat服务器,服务器拿到请求去寻找项目,然后找到web.xml配置文件。通过配置文件里的和获取到类全路径。
Tomcat通过web.xml配置文件获取到类全路径后,Tomcat会获取Servlet容器(一个map,key存的全路径,value存的对象),在Servlet容器中查询是否有该Servlet对象。
一、没有的情况:1.调用该Servlet的构造方法(默认的无参构造)。
2.调用父类的父类(GenericServlet)的有参init()方法,传入当前Servlet的配置文件对象参数,(在里面可以配置属性,在Servlet类里可以调用get方法获取属性)但是现在没有对象。所以会在底层创建ServletConfig对象,即当前Servlet配置文件对象( ServletConfig对象是在Servlet的初始化过程中由Servlet容器创建的。 )。
3.解析web.xml里面的servlet里面的init-param的数据。
4.将init-param数据添加到ServletConfig对象里面去。
5.在底层调用GenericServlet类的init方法(ServletConfig config)(回到并完成第二步)。方法内容为将配置文件对象赋值给本对象的配置文件属性,再调用init()无参方法,即子类重写的init()方法。
Tomcat会将创建好的对象添加到Servlet容器中。
二、有的情况:
获取Servlet容器中的Servlet对象。
然后:
Tomcat服务器解析请求数据,并封装到HttpServletRequest对象中。
Tomcat会将响应数据封装到HttpServletResponse对象中。
Tomcat调用HttpServlet类的service(req,reps)方法。
该方法会判断method为post还是get,分别调用doPost()和doGet()去获取数据、传出数据返回前端等(可以重写doPost()和doGet()方法或者重写service()方法,建议前者,因为可以分别处理两种method的情况)。
面试题:Servlet是单例的吗?
是?–>有待考证。
面试题:Servlet什么时候创建?
1.第一次发送请求时
2.在web.xml里面的servlet里面配置了1,项目启动时就会创建Servlet对象。
注意:1.数字表示优先级别,数字>0,越小优先级越高。(一般从1开始)当有多个Servlet优先级相同的时候,多线程会抢资源。
2.可以再注释里面配置,@webServlet(value=“/register”,initParams={@webInitParam(name=“code”,value=“UTF-8”)},loedOnStartup=1)。
注意要加斜杠,项目无法运行很大概率是配置问题。
在servlet类也可以编写简单注解如@WebServlet(“/Servlet01”)
线程安全
高并发情况:多个客户端同时向servlet发送请求,多线程操作同一资源。
面试题:Servlet是否是单例的?
答案:Servlet一般情况下是单例的,但如果实现了SingleThreadModel接口,(目前这个接口已弃用),客户端(多个)第一次同时发送请求时阻塞会创建Servlet对象。之后只要阻塞就分别用多个Servlet对象服务多个客户端,如果不阻塞就只用一个。
实现SingleThreadModel接口可以一定程度上实现线程安全。
或者采用加锁的方法。
经验:怎样解决Servlet线程安全问题?
1.加锁
2.少用成员属性
页面跳转
跳转的几种方式:
1.页面跳页面:(1)超链接(2)按钮(3)表单<h1>页面跳转页面</h1><br/> <a href="page01.html">超链接跳转</a><br/> <button onclick="function01()">按钮跳转</button><br/> <form action="page01.html" method="post"><!--html文件、图像文件、CSS文件和JS文件都是静态资源,不用去配置文件里面进行配置--> <input type="submit" value="表单跳转页面"/><br/> </form> <hr/>
2.页面跳Servlet:(1)超链接(这种跳法是get请求,可以携带数据。)
(2)按钮,可以携带数据
(3)表单,可以指定请求
<h1>页面跳转Servlet</h1><br/> <a href="Servlet01?username=aaa&password=123123&name=小明">超链接跳转Servlet</a><br/> <button onclick="function02()">按钮跳转Servlet</button><br/> <form action="Servlet01" method="post"><!--指定请求--> 账号:<input type="text" name="username"/><br/> 密码:<input type="password" name="password"/><br/> 姓名:<input type="text" name="name"/><br/> <input type="submit" value="表单跳转Servlet"/><br/> </form> <hr/>
3.Servlet跳Servlet
(1)转发:利用请求对象跳转request.getRequestDispatcher(“目标Servlet”).forward(request,response)
(2)重定向:利用响应对象重定向
response.sendRedirect(“目标Servlet”)
<h1>Servlet跳转Servlet</h1><br/> <a href="Servlet02">转发 -Servlet02跳转Servlet03</a><br/> <a href="Servlet04">重定向-Servlet04跳转Servlet05</a><br/> <hr/>
4.Servlet跳页面
(1)转发:利用请求对象跳转request.getRequestDispatcher(“目标html”).forward(request,response)
(2)重定向:利用响应对象重定向
response.sendRedirect(“目标html”)
<h1>Servlet跳转页面</h1><br/> <a href="Servlet06">转发-Servlet06跳转page02</a><br/> <a href="Servlet07">重定向-Servlet07跳转page02</a><br/>
转发和重定向的区别
区别一:访问普通页面,转发是一次请求(属于服务器内部跳转);重定向是两次请求(客户端会再次发送请求)。
request.getRequestDispatcher("page01.html").forward(request,response);
转发:客户端直接找到Servlet01,Servlet01调用doPost()方法,里面执行转发方法,利用request和response参数对page01页面进行请求和响应,只发送一次请求,地址栏仍然是Servlet01,而不是page01。
response.sendRedirect("page01.html");
重定向:客户端发送请求到Servlet01,Servlet01调用doPost()方法,进行重定向,让客户端重新发送请求到page01页面,发送了两次请求,地址栏更改为page01。
区别二:访问外部页面,转发无法访问到外部页面,因为转发是服务器内部跳转,服务器不能访问外部的服务器。而重定向可以。
request.getRequestDispatcher("http://www.baidu.com").forward(request,response);
重定向可以访问到外部页面
response.sendRedirect("http://www.baidu.com");
转发无法访问到外部页面
区别三:访问受保护的页面(WEB-INF内的html)。客户端不能直接访问受保护的资源。
request.getRequestDispatcher("WEB-INF/page01.html").forward(request,response);
转发可以访问受保护的页面。
response.sendRedirect("WEB-INF/page01.html");
重定向无法访问受保护的页面。
乱码问题
(8.0版本)在doPost里面设置请求和响应的编码格式(request和response调用相关set方法)
注意:重定向里面涉及到中文会有问题,客户端无法识别到中文页面。但是跳转可以,因为跳转是服务器内部跳转,格式一致。
解决:response.sendRedirect(UrlEncoder.encode(“中文.html”,“UTF-8”));
重定向跳转中文页面必须加上编码格式。
@WebServlet("/Servlet02") public class Servlet02 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //注意:重定向跳转中文页面必须加上编码格式 response.sendRedirect(URLEncoder.encode("中文页面.html","UTF-8")); } }