什么是Servlet

  • Servlet是JavaEE13个规范中的一个,servlet接口是servlet规范中的核心接口。
  • Tomcat是负责管理和调用我们编写的servlet实现类,即是Servlet容器。
  • Tomcat或其他容器和我们编写的项目同时遵循Servlet规范,使得Servlet容器和我们项目能解耦合。
    今天在Tomcat运行的项目,明天也可以在jboss上运行,我们编写的项目遵循的servlet规范,
    也就是实现了servlet接口。

Servlet对象的生命周期

  • Tomcat启动时会读取webapps下的各个项目中的web.xml文件,将url-pattern的映射路径和对应的Servlet
    完整路径名存储到Map集合中,Map<String url-pattern映射路径,String Servlet完整路径名>

  • Servlet对象被创建成功后,这个Servlet对象和对应的url-pattern被web容器存储到Map集合中
    Map<String url-pattern,Servlet servlet对象>

  • 用户在地址栏输入url:http://localhost:8080/JavaWeb/jdbc

  • web容器截取请求路径:/JavaWeb/jdbc,根据请求路径在Map集合中查找Servlet对象

    • 若找到直接调用Servlet对象的service方法提供服务。
    • 若没找到,根据路径在Map集合中找到servlet完整路径名,通过反射机制调用Servlet类的无参构造方法
      完成Servlet对象的实例化,创建Servlet对象成功后,这个Servlet对象被存储到Map集合中
  • 创建Servlet对象成功后,web容器调用servlet对象的init方法完成初始化操作,
    紧接着调用service方法提供服务

  • web容器关闭时/webapp重新部署时/该Servlet对象长时间没有用户再次访问时,web容器会将该Servlet对象销毁,在销毁该对象之前,web容器会调用servlet对象的destroy方法,完成销毁的准备。

总结:

  • Servlet对象的构造方法,init,destroy方法只执行一次,service方法是,用户每请求一次,就执行一次。

  • Init方法调用时,Servlet对象已经被创建,destroy方法调用时,Servlet对象还没有被销毁,即将被销毁

  • Servlet对象是在单实例多线程的环境下运行的,那么Servlet对象中若有实例变量,
    并且实例变量涉及到修改操作,那么这个Servlet对象一定存在线程安全问题,
    不建议在Servlet对象中使用实例变量,最好用局部变量

  • 默认情况下,Servlet对象在web服务器启动阶段不会被实例化,若想在服务器启动阶段就创建Servlet对象,需要在web.xml的该servlet对象标签中配置load-on-startup标签 标签中值是加载顺序,
    最小为0,0代表最先被创建,配置好web.xml后,服务器启动阶段会直接调用无参构造和init方法

  • Servlet是伪单例模式,单实例多线程,真单例模式构造方法私有化

ServletConfig

  • ServletConfig是一个servlet规范中的一个接口

  • tomcat服务器实现了servlet规范,tomcat服务器专门写了一个servletConfig接口的实现类,类名是:StandardWrapperFacade

  • servletConfig是一个servlet对象的配置信息对象,servletConfig对象中封装了一个servlet对象的配置信息,servlet配置信息就是web.xml,一个servlet对象对应一个servletConfig对象

  • 如何在service方法中拿到init方法中的config参数对象?定义一个私密的全局变量即可,
    如果子类也想访问config对象,将config全局变量设置给servlet中的getServletConfig方法中返回

  • servletConfig的常用方法

    • getInitParameter(“userName”)方法可以获取web.xml中servlet中配置的
      init-param标签内数据,init-param标签中的配置被封装到了servletConfig对象中

    • servletConfig.getInitParameterNames();
      获取所有name的值,返回一个集合,通过遍历获取值

      Enumeration<String> names = servletConfig.getInitParameterNames();
      while (names.hasMoreElements()){
      	String name = names.nextElement();
      	String value = servletConfig.getInitParameter(name);
      	System.out.println(name+"="+value);
      
      }
    • getServletName(),获取xml文件中sevlet-name的值

    • getServletContext(),获取servletContext对象

ServletContext

  • 该接口也是servlet规范中的一个接口,tomcat实现该接口的实现类名是:ApplicationContextFacade
    程序员只需要面向接口调用就行,因为多态自动调用tomcat为我们实现的方法.
  • servlet,servletConfig,servletContext之间的关系
    一个servlet对象对应一个servletConfig对象,多个对象对应多个servletConfig对象
    多个servlet对象共享一个servletContext对象
  • servletContext到底是什么,什么时候被创建?
    servletContext被翻译为:Servlet上下文,是所有servlet对象的环境,
    多个servlet对象之间如果想共享数据,可以使用servletContext,一般放到servletContext对象中的数据不建议涉及修改操作,因为servletContext是多线程共享的变量,修改的时候会存在线程安全问题
    一个webapp只有一个web.xml文件,web.xml文件在服务器启动阶段被解析
    一个webapp只有一个ServletContext对象,servletContext在服务器启动阶段被实例化
    servletContext在服务器关闭时被销毁,servletContext对应的是web.xml文件,是web.xml文件的代表
  • servletContext中的常用方法:
    • getRealPath(),获取文件绝对路径,以/开头表示文件根目录,context.getRealPath(“/index.html”);
    • getInitParameter(“name”),getInitParameterNames(),获取web.xml中配置的Context-param信息
    • setAttrobute(),getAttribute(),removeAttribute()方法,设置数据,用于多个servlet对象间共享数据

设置欢迎页面和error页面

<welcome-file-list>
        <welcome-file>index.html</welcome-file>
</welcome-file-list>
  • 欢迎页面可以在浏览器中直接访问项目名,而不需要指定文件名,欢迎页面可以有多个
  • 欢迎页面的路径不需要以/开头,可以是静态资源.html,也可以是servlet,路径就是url-pattern去掉/
  • 上面的例子中,index.html必须在webapp的根目录下,如果在根目录下的html文件夹中,路径应该为:html/index.html
  • 欢迎页面有全局配置和局部配置,全局配置在tomcat根目录下的conf配置文件中的web.xml,这是全局的配置文件,局部配置就是我们项目中的web.xml文件,
    局部配置优先级高于全局配置的优先级,先执行局部,没有就执行全局
<error-page>
        <error-code>404</error-code>
        <location>/error.html</location>
</error-page>
  • 404,not found,资源未找到,请求路径写错了
  • 500,server inner error,服务器内部错误,一般是java程序出现异常
  • 404和500是http协议状态码,以上都是w3c制定的,正常响应的http协议状态码:200
    在web.xml中设置错误页面,如果出错会跳转到错误页面

http协议

  • 什么是http协议?
    超文本传输协议,浏览器和服务器之间的一种通讯协议,由W3c负责制定
    本质上就是数据传输格式提前制定好了,浏览器和服务器都必须按照这种数据格式进行接收和发送
  • http协议包括几部分?
    请求协议:从浏览器发送到服务器时采用的数据传送格式
    响应协议:从服务器发送到浏览器时采用的数据传送格式
  • 请求协议
    • 请求行,请求行包括:请求方式,URL,协议版本号
    • 请求头
    • 空白行:专门用来分离请求头和请求体
    • 请求体
  • 响应协议
    • 响应行:响应行包括:协议版本号,状态码,状态描述信息
    • 响应头
    • 空白行:专门用来分离响应头和响应体的
    • 响应体
  • get和post请求方式的区别
    • post请求只应用于表单提交,并且method方法为post时,提交方式才是post,其余方式都是get请求
    • post请求提交的数据存放在请求体中,地址栏中不会显示数据,get请求提交的数据存放在请求行中,地址栏上会显示数据,不安全
    • post请求方式不缓存数据,get请求方式的结果会被浏览器放进缓存中
      有时候使用get请求方式的时候,不需要缓存,可以在请求后加上毫秒值,Window.location=xxx+’?’+new Date().getTime();
  • 怎么知道前端请求的方式是get还是post?
    HTTP的请求协议全部信息被自动封装到HttpServletRequest对象中,在该类中有一个getMethod()方法,可以返回请求行中的请求方式,HttpServletRequest接口继承了ServletRequest接口
  • 为什么需要知道前端发过来的请求是get和post呢?
    因为后端要根据前端的请求方式的不同去处理数据,有些我们实现servlet的程序必须要求请求方式是post,不是我们就要抛异常,从而保证前端的请求方式和后端需要的请求方式一致

HttpServletRequest

  • HttpServletRequest是一个接口,继承于ServletRequest

  • HttpServletRequest接口的实现类由Web容器负责实现,我们只需要面向接口编程,直接调用接口中的方法即可

  • HttpServletRequest封装了http请求协议的全部数据

  • HttpServletRequest对象表示一次请求,一次请求对应一个request对象

  • HttpServletRequest常用的方法

    • 表单提交的数据会被自动封装到request对象中,request对象中有map集合存储这些数据
      map集合的key是name,字符串类型,value是一个字符串类型的一维数组

    • 表单的数据获取方法:

      • String getParameter(String name) //通过key获取value这个一维数组中的首元素,通常情况下这个一维数组中只有一个元素,所以这个方法使用的最多
      • Map getParameterMap() //获取整个map集合
      • Enumeration getParameterNames() //获取map集合的所有的key值
      • String[] getParameterValues(String name) //通过map集合的key获取value,适合取复选框数据
      • void setAttribute(String name,Object o) //向request范围中存储数据
      • Object getAttribute(String name) //向request范围中获得数据
      • void removeAttribute(String name) //向request范围中删除数据
      • void setCharacterEncoding(String env)
      • String getContextPath() //获取上下文路径[webapp的根路径],/项目名
      • String getMethod() //获取浏览器请求方式
      • String getRequestURI() //获取URI,URI和URL的区别是,URI不包括请求协议,地址,端口号
      • StringBuffer getRequestURL() //获取请求路径
      • String getServletPath() //web.xml中配置的url-pattern
      • String getRemoteAddr() //获取客户端的请求Ip地址
      • Cookie[] getCookies() //获取所有cookie
      • HttpSession getSession()
        HttpSession session = request.getSession(); //获取session,找不到session,则新建一个session
        HttpSession session = request.getSession(true); //获取session,找不到session,则新建一个session
        HttpSession session1 = request.getSession(false);//获取session,找不到session,则返回null
  • HttpServletRequest的范围:一次请求代表一个request对象,request只能在同一次请求中传递数据,即只能在一个servlet中访问数据。

  • 如何在其他servlet中也能获取到request对象中的数据呢?
    我们需要将另一个Servlet执行和本次servlet执行放在同一个请求中,必须使用转发技术:forward(转发),即,AServlet中request对象设置数据,Bservlet要想访问到数据要通过转发技术。
    转发步骤:1,获取请求转发器对象,2,调用请求转发器的forward方法即可完成转发。
    RequestDispatcher getRequestDispatcher(String path) //路径填其他servlet的url–pattern
    request.getRequestDispatcher(“/request2”).forward(request,response);

关于项目中出现的乱码问题

  • 乱码经常出现在什么位置
    数据”传递”过程中的乱码
    数据”展示”过程中的乱码
    数据”保存”过程中的乱码

  • 数据保存过程中的乱码?
    最终保存到数据库表中的时候,数据出现乱码,导致数据保存过程中的乱码包括以下两个情况
    第一种情况,在保存之前,数据本身就是乱码,保存到数据库表的时候一定是乱码
    第二种情况,在保存之前,数据不是乱码,但是由于本身数据库不支持简体中文,保存之后出现乱码

  • 数据展示过程中的乱码?
    最终显示到网页上的数据出现乱码

    • 经过执行java程序之后,java程序负责向浏览器响应的时候,中文出现乱码,怎么解决?
      设置响应的内容类型,以及对应的字符编码方式:
      response。setContentType(“text/html;charset=UTF-8”);

    • 没有经过执行java程序,直接访问html页面,出现中文乱码,怎么解决?
      设置html页面中标签

  • 数据传递过程中的乱码?
    将数据从浏览器发送给服务器的时候,服务器收到的数据是乱码,因为浏览器会将用户填写的数据使用ISO-8859-1的编码,服务端接收到数据是ISO-8859-1格式的字符串,解码后才能使用。
    直接看https://www.cnblogs.com/xdouby/p/8308915.html
    解决数据传递过程中的乱码:

  • 第一种解决方案:万能公式,既能够解决POST请求乱码,又能解决GET请求乱码
    先将服务器接收到的数据采用ISO-8859-1的方式解码,回归原始状态
    再给定一种支持简体中文的编码方式重新编码组装[组装的时候编码方式需要和浏览器的编码方式相同]

  • 第二种解决方案:只支持POST请求,因为这种方式只对请求体编码
    request.setChatacterEncoding(“UTF-8”),以上代码必须在从request中获取任何数据之前设置有效果

Servlet线程安全问题

  • servlet是单实例,多线程环境下运行的
  • 什么时候程序存在线程安全问题
    多线程并发,有共享的数据,共享数据有修改操作
  • JVM中哪些数据存在线程安全问题
    • 局部变量存在栈中,一个线程一个栈,100个线程100个栈空间,栈空间不共享,所以没有线程安全问题
    • 常量不会被修改,所以也不会有线程安全问题
    • 所有线程共享一个堆,堆中存放对象,所以实例变量是存在线程安全问题
    • 所有线程共享一个方法区,方法区存放静态变量,所以静态变量也存在线程安全问题
  • 线程安全问题,不仅发生在jvm中,也发生在数据库中,例如,多个线程共享同一张表,并且同时去修改表中的数据,那么这些记录就存在安全问题,怎么解决数据库表中数据的线程安全问题?
    • 在java程序中使用synchronized关键字,线程排队执行,自然不会在数据库中并发,解决线程安全问题
    • 行级锁(悲观锁)
    • 事务隔离级别(串行化)
    • 乐观锁
  • 怎么解决线程安全问题
    • 不使用实例变量,尽量使用局部变量
    • 若必须使用实例变量,那么我们可以考虑将该对象变成多例对象,一个线程一个java对象,实例变量的内存也不会共享
    • 若必须使用单例,那只能使用synchronized关键字,线程一旦排队执行,则吞吐量降低,降低用户体验
  • servlet怎么解决线程安全问题
    • 不使用实例变量,尽量使用局部变量
    • servlet必须是单例的,所以剩下的方式只能考虑synchronized,线程同步机制

关于web系统中资源跳转

  • 跳转包括两种方式
    转发:forward
    重定向:redirect
  • 转发和重定向代码怎么完成?
    转发:request.getRequestDispatcher(“/request2”).forward(request,response);
    重定向:response.sendRedirect(request.getContextPath()+”/b”);
    request.getContextPath()获取上下文路径,/JavaWeb
  • 转发和重定向的区别
    • 相同点,都可以完成资源跳转
    • 不同点:
      • 转发是request对象触发的,重定向是response对象触发的
      • 转发是一次请求,浏览器地址栏上地址不会发生变化,重定向是两次请求,地址栏上的地址会发生变化,重定向的路径需要加webapp根路径
      • 转发是项目内部完成资源跳转,重定向可以完成跨app跳转资源
  • 跳转的资源可以是,servlet,html,jsp
  • 重定向原理,response.sendRedirect(“/JavaWeb/b”);
    程序执行到这,服务器将路径/JavaWeb/b反馈给浏览器,浏览器又自动向服务器发送了一次全新的请求
  • 什么时候采用转发,什么时候采用重定向(大部分情况下都是用重定向)
    • 若想完成跨app跳转,必须采用重定向
    • 若在上一个资源中(servlet)向request范围中存储了数据,希望在下一个资源中从request范围将数据取出来,必须使用转发
    • 重定向可以解决浏览器刷新问题,浏览器点刷新,会执行上一次的请求
  • Cookie是什么,有什么作用?
    Cookie可以保存会话状态,但是这个会话状态是保存在客户端上的
    只要cookie清除,或者cookie失效,这个会话状态就没有了
    Cookie可以保存在浏览器的缓存中,浏览器关闭Cookie消失
    Cookie也可以保存在客户端的硬盘文件中,浏览器关闭,cookie还在,除非cookie失效
  • cookie实现的功能,常见的有哪些?
    保留购物车商品的状态在客户端上
    十天内免登陆
  • 在java中cookie被当做类来处理,使用new运算符可以创建cookie对象,而且cookie由两部分组成,分别是cookie的name,value,name和value都是字符串类型
  • 在java程序中怎么创发送Cookie?
    Cookie cookie = new Cookie(“username”,”zahngsan”);
    response.addCookie(cookie);
  • 服务器可以一次向浏览器发送多个cookie,默认情况下,服务器发送cookie给浏览器后,浏览器将cookie保存在浏览器缓存中,只要不关闭浏览器,cookie永远存在,并且有效,当浏览器关闭后,缓存中的cookie被清除
  • 在浏览器客户端无论是硬盘文件还是缓存中保存的cookie,什么时候会再次发送给服务器?
    浏览器会不会提交发送这些cookie给服务器,和请求路径有关,请求路径和cookie紧密相连
    • 如果请求路径是http://localhost:8080/JavaWeb/request,并且是在这个servlet中保存的cookie,那么只要你访问/JavaWeb/下任何资源,浏览器都会发送cookie,
    • 如果你请求路径是http://localhost:8080/JavaWeb/text/request,那么cookie默认路径是/JavaWeb/text/,只要访问/text/下任何资源,浏览器都会发cookie,
      cookie也可以自定义路径,cookie.setPath();cookie.setPath(“/JavaWeb/king”);
  • cookie默认不能设置中文,要存储中文需要设置编码
  • 设置cookie的有效期
    cookie.setMaxAge(60);单位是秒,只要设置了cookie的有效期,cookie则存储在客户端硬盘上,浏览器关闭后,cookie还存在,直到cookie有效期失效,
    有效期大于0则储存cookie,等于0则cookie立即被删除,小于0cookie不存储
  • 服务器获取浏览器发送的cookie,request对象的Cookie[] getCookies()方法,如果浏览器没有发cookie,我们得到的cookie数组为空,所以在遍历的时候要先判断一下是否为空
    Cookie[] cookies = request.getCookies();

servlet路径的总结

路径填写有三种情况:

  • 加/,并且加项目名的,例如:/JavaWeb/xxx
    • a标签,form表单中的路径
    • 重定向的路径,response.sendRedirect(request.getContextPath()+”/b”);
    • cookie的路径,cookie.setPath(“/JavaWeb/king”)
  • 加/,不加项目名的,例如:/xxx
    • web.xml中的url-pattern映射路径
    • ServletContext.getRealPath(“/index.html”)获取资源绝对路径
    • 请求转发的路径request.getRequestDispatcher(“/request2”).forward(request,response);
    • web.xml中配置error页面,/error.html
  • 不加/,也不加项目名的,只有web.xml中的欢迎页面的路径是这种情况

获取各种路径:

  • request.getContextPath() //获取上下文路径[webapp的根路径],/项目名
  • request.getRequestURI() //获取URI,URI和URL的区别是,URI不包括请求协议,地址,端口号
  • request.getRequestURL() //获取请求路径
  • request.getServletPath() //web.xml中配置的url-pattern
  • ServletContext.getRealPath(“/index.html”) //获取资源绝对路径

url-pattern的编写方式

url-pattern可以编写多个

  • 精确匹配

    <url-pattern>/checkCookie</url-pattern>
    <url-pattern>/check/checkCookie</url-pattern>
  • 扩展匹配

    <url-pattern>/check/*</url-pattern>
  • 后缀匹配

    <url-pattern>*.do</url-pattern>
    <url-pattern>*.action</url-pattern>
  • 全局匹配

    <url-pattern>/*</url-pattern>

HttpSession

HttpSession表示会话,是一个会话级别的对象,一次会话对应一个HttpSession对象
一次会话包括多个请求,在会话过程中,web服务器一直为当前用户维护着一个会话对象
Cookie可以将会话状态保存在客户端,HttpSession可以将会话状态保存在服务器端

  • session实现原理
    在浏览器第一次访问服务器的时候,服务创建一个session对象,并且创建一个cookie对象
    这个cookie对象的name是jsessionid,值是具有唯一性的随机值,将Cookie的值作为key,Session对象作为value,保存在服务器的session列表中,最后将cookie发给浏览器客户端,
    当浏览器再次访问服务器时,会把该cookie发送给服务器,服务器接收到cookie通过cookie的值
    找到对应的session对象。所以服务器知道,用户访问一个网站时该用户的每一次请求都属于同一个用户,所以,cookie和session是紧密相连的,当浏览器关闭时,cookie失效,session也会失效
  • 当浏览器禁用cookie时,则浏览器缓存中不再保存cookie,导致在同一个会话中,无法获取到对应的session对象,所以每一次获取会话对象都是新创建的
    如果浏览器禁用cookie之后,还想拿到对应的session对象,必须使用URL重写机制,即在地址中以;为间隔加上cookie的name和value
  • 浏览器关闭后,保存在服务器上的session不会被销毁,因为BS架构基于http协议,http协议是一种无状态协议
    什么是无状态?请求的瞬间,浏览器和服务器是连接状态,请求和响应之后,连接状态关闭,这样做的目的是降低服务器的压力
  • 什么时候session对象被销毁?
    web系统中引入了session超时概念,
    当很长时间(这个时间可以设置),没有用户再次访问这个session,此时session对象超时,web服务器自动回收session对象
    不配置超时时间默认session时间30分钟,web.xml中可以配置超时时间 120 //2小时之内,没有访问session对象,session被销毁
  • 什么是一次会话?
    一般而言,用户打开浏览器,进行操作后,关闭浏览器,表示一个会话结束
    本质上说,从session对象的创建,到最终session对象因超时而销毁,才是一个完整的会话结束
  • HttpSession接口的常用方法
    • session.setAttribute();
    • session.getAttribute();
    • session.removeAttribute(); //会话间共享数据
    • session.invalidate(); //销毁session
  • ServletContext(所有用户共享数据),HttpServletRequest(一次请求一个),HttpSession(一个用户一个)接口之间的对比
    • 以上都是范围对象
    • ServletContext是应用范围,HttpServletRequest是请求范围,HttpSession是会话范围
    • ServletContext跨会话共享数据
      HttpSession完成跨请求共享数据,但是这些请求必须在同一个会话中
      HttpServletRequest完成跨servlet共享数据,但是这些servlet必须在同一个请求中[转发]