Servlet线程安全问题
Servlet的service方法的调用比较特殊:service方法的执行, 每一次都是在新的线程中.
因为service方法,执行在新线程中, 有可能同时存在多个线程.多线程操作时, 有可能发生线程安全问题
1.静态的同步方法的同步锁 --> 类.class这个对象
2.非静态的同步方法的同步锁 --> this对象
3.同步代码块的同步锁 --> 程序员使用时提供.
代码块有{ }构造代码块,每次调用外面的方法是首先执行它,static{ }类加载时执行它.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package demo1;
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("/s1.do") public class Servlet1 extends HttpServlet { private int count = 10; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { synchronized(this) { if(count>0) { System.out.println("有票, 正在出票"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count--; System.out.println("出票完成, 余票:"+count); }else { System.out.println("很遗憾 ,没有余票了"); } } response.getWriter().append("ok"); } }
|
请求的转发
概念:将一个web组件为处理完毕的请求,通过tomcat转交给另一个web组件处理.
步骤:
1.获取请求转发器
RequestDispatcher rd = request.getRequestDispatcher(“转发的地址”);
2.通过转发器发起转发
rd.forward(请求对象,响应对象);
简写步骤:
request.getRequestDispatcher(“转发的地址”).forward(请求对象,响应对象);
原理(tomcat内部执行流程):
1.当浏览器访问服务器中的tomcat时.
2.tomcat将协议中的请求进行封装,封装为HttpServletRequest对象,将响应封装到HttpServletResponse对象中
3.找到对应映射地址的Servlet,(第一次寻找会创建对象),调用对象的service方法,传入请求与响应对象.
4.在serlvet中,我们可以控制将请求转发到另一个地址.
5.tomcat接收到请求转发指令后,将原请求对象和新的响应对象传给转发的新地址.
6.此时旧的响应对象就失效了,无法在进行响应了.由新的地址的响应对象 负责给用户进行响应.
7.新地址准备完毕响应体,发送给浏览器
特点:
1.转发的过程中,只发生了一次请求,多个Servlet之间共享一份请求信息.
2.转发无法跨域实现(无法跨网站进行转发,例如:京东无法转发给淘宝.)
3.无论转发过程中,触发了多少个web组件,对于浏览器而言,它能感知到的只是发起了一次请求,接收到了一次响应.
4.转发过程中,浏览器地址不会发生改变
5.相较于重定向而言,效率高.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package demo2;
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("/s2.do") public class Servlet2 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); if("admin".equals(username) && "123".equals(password)) { request.getRequestDispatcher("https://www.baidu.com").forward(request, response); }else { request.getRequestDispatcher("s4.do").forward(request, response); } } }
|
请求的重定向
概念:在进行响应时,告知浏览器新的请求地址,浏览器接到后,自动发起新的请求找指定的地址.
步骤:
response.sendRedirect("重定向的新地址");
原理(执行流程):
1.当浏览器请求某个Servlet时.
2.Servlet对浏览器响应一个302的状态码,以及一个键为location的值,这个值表示新的地址;
3.当浏览器接收到302的状态码以后,会自动寻找lcoation的值,并网页自动跳转到location的值表示地址上 !
特点:
1.重定向会产生新的请求和新的响应.
2.使用重定向,可以跨域实现(可以跨网站操作,例如:京东可以将请求重定向到淘宝)
3.浏览器地址会发生改变,显示的是重定向的地址;
4.相较于请求转发而言,效率较低.
注意:
1.在用户的一次访问过程中,我们可以进行无限次的转发/重定向!但是记住一点:在多次转发/重定向的操作中,一定要有出口!
2.不只是可以将请求转发/重定向到servlet上,任何的web资源都可以接受转发和重定向.
3.当我们的代码进行了转发/重定向时,相当于将响应的操作交给了其他资源.那么在进行转发和重定向的代码后面,就不要再编写响应的操作了.因为无效.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package demo3;
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("/s22.do") public class Servlet2 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); if("admin".equals(username) && "123".equals(password)) { response.sendRedirect("s33.do"); }else { response.sendRedirect("s44.do"); } } }
|
HttpServletRequest类的常用操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 1.获取客户端的ip地址: String ip = request.getRemoteAddr();
2.获取客户端访问的路径地址: String url = reuqest.getRequestURI();
3.获取tomcat运行的ip地址 String ip = request.getServerName();
4.获取tomcat运行的端口号 String port = request.getServerPort();
5.获取请求方式 String method = request.getMethod();
6.获取get请求的?后的参数列表 String params = request.getQueryString();
7.设置请求的内容编码格式(常用于请求键值) request.setCharacterEncoding("UTF-8");
8.获取请求地址中的键值 request.getParameter()
|
HttpServletResponse类的常用操作
1 2 3 4 5 6 7 8
| 1.设置响应的内容类型(网页) 以及 编码格式(UTF-8) response.setContentType("text/html;charset=utf-8");
2.设置响应的内容编码格式 (常用于响应JSON数据) response.setCharacterEncoding("UTF-8");
3.响应错误码给客户端 response.sendError(int status,String msg);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| package demo4;
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("/s5.do") public class Servlet5 extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
int port = request.getServerPort(); response.getWriter().append("tomcat运行的端口号是:"+port);
String method = request.getMethod(); response.getWriter().append("请求的方式是:"+method);
String params = request.getQueryString(); response.getWriter().append("获取get请求的?后的参数列表:"+method);
String ip = request.getRemoteAddr(); response.getWriter().append("你的ip地址是:"+ip);
String url = request.getRequestURI(); response.getWriter().append("你的url地址是:"+url);
if(request.getParameter("heiheihei")!=null) { response.getWriter().append("<h1>网站主页</h1>"); }else { response.sendError(404,"资源不存在, 哈哈哈"); } }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
|
ServletContext对象(Servlet上下文)
每一个Servlet都是一个独立的网页地址,它们之间无法进行通信以及交流
例如:我们想统计网站的访问次数,每一个servlet只能统计自己被访问的次数,无法汇总.
Servlet上下文对象,就是用于Servlet之间信息共享的,是多个servlet之间通信的桥梁
Servelt上下文对象,在项目的运行过程中,只有一个.每一个Servlet获取的上下文对象, 都是同一份.
Servlet上下文对象,就像一个Map集合,可以存储n个键值对信息!
格式:
ServletContext context = getServletContext();
-
上下文对象常用方法
1 2 3 4 5 6 7 8 9 10 11
| 1.存储数据 context.setAttribute(String name,Object value);
2.获取数据 Object value = context.getAttribute(String name);
3.删除数据 context.removeAttribute(String name);
4.获取项目运行时的文件夹 绝对路径. tring path = context.getRealPath("/");
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| package demo6;
import javax.servlet.ServletContext; 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("/c1.do") public class Count1 extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); ServletContext context = getServletContext(); Integer i = (Integer) context.getAttribute("count"); if(i==null) { i=0; } i++; context.setAttribute("count", i); String path = context.getRealPath("/");
response.getWriter().append("path:"+path); response.getWriter().append("c1: 你是项目的第"+i+"位访问者"); }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
|