Java Web开发笔记

问题:
读取资源文件问题
servletContext.getRealPath()
servletContext.getResouce().getPath()
setvletContext.getResourceAsStream()
 

1、Servlet入门(编写Hello World程序)

     根据接口文档的接收,创建一个一个servlet需要以下步骤
     1)在tomcat的webapps目录下创建hello目录,然后在hello目录下创建WEB-INF/classes目录
     2)在classes目录下创建一个HelloWorldServlet.java文件
     3)在该源文件中定义一个HelloWorldServlet类,该类可以实现Servlet接口,为了方便,
          咱们可以直接继承HttpServlet或者类,然后重写service方法,代码如下
          备注:其实我们应该重写doGet( )或者doPost( )方法,因为HttpServlet中的service实现了一些处理逻辑,比如,根据请求方法调用doGet()或doPost(),或者是缓存的处理  等,但是我们这里主要是体验下servlet的执行过程,所以先重写service方法,真正项目不能这样干的,切记。对于service( )、doGet( )、doPost( )三种的关系,后面会做解析
         
 
    4)编译HelloWorldServlet.java源文件
         javac -d . HelloWorldServlet.java
         注意:编译时候,需要将tomcat主目录下的
lib中的servlet-api.jar加入到classpath,否则会编译失败
 
    5)在WEB-INF目录下创建web.xml的配置文件,并编写如下内容:
        
 
6)重启tomcat服务器,然后再浏览器上输入:http://localhost:8080/hello/helloWorld,结果如下:
      
 
搭建好的web目录结构如下
F:.
└─helloWorld
    └─WEB-INF
        └─classes           //存放Java class文件 
        └─lib                 //存放jar包
        └─web.xml         //web应用部署说明文件
    └─*.jsp、*.html
 
 
2、Servlet生命周期
     Servlet生命周期是由部署的Servlet容器控制的。Servlet不能单独运行,它的运行完全由Servlet引擎来控制和调度
     Servlet接口主要定义了以下一些方法:
         init(ServletConfig config)
         service(ServletRequest req, ServletResponse res)
         destroy()
     1)当Servlet被首次访问时,容器会创建该Servlet的实例,这里强调“首次”,因为该servlet实例对象在整个服务器运行期间仅存在一个
     2)然后调用init()方法进行Servlet对象初始化  
     3)然后调用service处理客户的请求 
     4)直到WEB服务器关闭,servlet容器会调用调用destroy()方法销户该servlet实例。
     注意:
     1)针对客户的多次Servlet请求,通常情况下,服务器只会创建一个servlet实例对象,也就是说
          Servlet实例对象一旦被创建,它就会驻留在内存中,为后续的其他请求服务,直至web容器
          退出,servlet实例对象才会销毁。
     2)在Servlet的整个生命周期内,Servlet的init方法只被调用一次。而对一个Servlet的每次访问请求
          都导致Servlet引擎调用一次Servlet的service方法。对于每个访问请求,Servlet引擎都会创建
          新的HttpServletRequest请求对象和一个新的HttpServletResponse相应对象,然后将两个对象
          作为参数传递给它的调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法
 
 
3、Servlet的使用
     在Servlet的开发过程中,一般不直接实现Serlvet接口来编写Serlvet,而是通过继承现成的HttpServlet来开发Serlvet,然后覆盖doGet和doPost方法
     HttpServlet类主要提供了一下方法:
         init(ServletConfig config)
         service(ServletRequest req, ServletResponse res)
         service(HttpServletRequest req, HttpServletResponse res)
         destroy()
         doXXX( )方法,如doGet()
 
     HttpServlet的调用过程
     当Servlet被首次访问时,容器会创建该Servlet的实例,
     1)然后调用init()方法进行Servlet对象初始化  
     2)然后调用service处理客户的请求,在Service方法中会根据客户请求method来判断具体执行哪个doXXX()方法
          例如,如果GET,则调用doGet() ,如果是post,则调用doPost方法进行处理
     3)当一个servlet许久不被访问的时候,servlet容器会调用调用destroy()方法销户该servlet实例。 
 
 
 
4、web.xml配置
由于客户端是通过URL地址访问web服务器中的资源,所以Servlet程序若想被外界访问,必须把servlet程序映射到一个URL地址上,这个工作在web.xml文件中使用<servlet>元素和<servlet-mapping>元素完成。
<servlet>元素用于注册Servlet,它包含有两个主要的子元素:<servlet-name><servlet-class>,分别用于设置Servlet的注册名称和Servlet的完整类名。
<servlet-mapping>元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个主要的子元素:<servlet-name><url-pattern>,分别用于指定Servlet的注册名称和Servlet的对外访问路径。例如:
 
 
1)同一个Servlet可以被映射到多个URL上,即多个<servlet-mapping>元的的<servlet-name>子元素的设置可以是同一个
2)在Servlet映射到的URL中可以使用 * 通配符,但是只能有两种固定的格式:一种是 *.扩展名;另外一种是以正斜杆( / ) 开头并以“/*"结尾,即不带后缀名的URL,则必须以正斜杆( / ) 开头
 
 第一种方式
<servlet-mapping>
     <servlet-name> servletName <servlet-name>
     <url-pattern>/* <url-pattern>
</servlet-mapping>
 
第二种方式
<servlet-mapping>
     <servlet-name> servletName <servlet-name>
     <url-pattern>*.do <url-pattern>
</servlet-mapping>
 
<servlet-mapping>
     <servlet-name> servletName <servlet-name>
     <url-pattern>xxx/yyy/test.do <url-pattern>
</servlet-mapping>
 
注意:第二种方式中的第一个例子,*.do前面不能带任何内容,否则报错,例如,不能配置成如下
<servlet-mapping>
     <servlet-name> servletName <servlet-name>
     <url-pattern>xx/yy/*.do <url-pattern>
</servlet-mapping>
 
 
3)Servlet URL映射原则
     适用通配符时,有些时候,多个Servlet映射的URL可能存在冲突,那么此时会使用最具体匹配原则进行匹配
     *.后缀 的URL优先级最低
 
     对于如下的一些映射关系:
     Servlet1 映射到 /abc/*
     Servlet2 映射到 /*
     Servlet3 映射到 /abc
     Servlet4 映射到 *.do
 
     问题:
     当请求URL为“/abc/a.html”,“/abc/*”和“/*”都匹配,哪个servlet响应
      Servlet引擎将调用Servlet1
     当请求URL为“/abc时,“/abc/*”和“/abc都匹配,哪个servlet响应
 
     Servlet引擎将调用Servlet3
     当请求URL为“/abc/a.do”时,“/abc/*”和“*.do”都匹配,哪个servlet响应
      Servlet引擎将调用Servlet1
     当请求URL为“/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应
 
     Servlet引擎将调用Servlet2
     当请求URL为“/xxx/yyy/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应
 
     Servlet引擎将调用Servlet2
 
 
4)配置Servlet在web服务器启动就加载
      如果在web.xml中<servlet>元素中配置了一个<load-on-startup>元素,那么WEB服务器在启动时,就会装载并创建
      Servlet的实例对象以及调用Servlet实例对象的init( )方法。
      例如:
      
     注意:
     <load-on-startup>元素指定的指只能为自然数(大于等于0的整数),数值越小,优先级越高,越优先被加载
 
 
5)默认的servlet
     ①如果某个Servlet的URL映射路径仅仅为一个正斜杆( / ) ,那么这个Servlet就成为当前Web应用程序的缺省Servlet.
     ②凡是在web.xml中找不到匹配的<servlet-mapping>元素的URL时,它们的访问请求都将交给缺省的Servlet处理,
        也就是说,缺少的Servlet的用于处理所有其他的Servlet都不能处理的访问请求
     ③在<tomcat的安装目录>\conf\web.xml文件中,注册了一个名称为org.apache.catalina.servlets.DefaultServlet
        的Servlet,并将这个Servlet设置为缺省的Servlet。
     ④当访问Tomcat服务器的某个静态HTML文件和图片时,实际上是在访问这个缺省servlet,由这个Servlet将这些
        静态资源返回给浏览器
 
     注意:一般不要自己配置默认的Servlet,不然,客户端无法访问静态的web资源
      
 
 
5、Servlet中常用对象
    ServletConfig
    ServletConfig封装了web.xml中<init-param>配置的初始化参数,该对象由Servlet容器在创建Servlet实例对象时创建,
    然后调用Servlet的init( )方法时,传递给Serlvet,进而可以在Servlet中访问当前Servlet的初始化参数。
    1)在web.xml配置初始化参数的案例
         
    2)ServletConfig对象提供方法
         getInitParameter(param_name)      #通过参数参数名称获取参数的具体值
         getInitParameterNames( )              #获取所有初始化参数的名称,以Enumeration类型返回
         getServletName( )                          #获取当前Servlet名称,即web.xml中配置的<servlet-name>元素值
         getServletContext()                        #获取当前web应用的context域对象
    
    注意:如果重写了init( )方法,则必须调用父类的init方法,super.init(config),否则this.getServletConfig()获取不到ServletConfig对象
 
    ServletContext
    WEB容器在启动的时候,会为每个WEB应用都创建一个对应的ServletContext对象,它代表当前的WEB应用。
       1)如何得到ServletContext实例对象
         ServletConfig对象维护了对ServletContext的引用,可以通过ServletConfig的getServletContext对象获取
    
       2ServletContext的作用
           ① 多个Servlet通过ServletContext对象实现该WEB应用的数据共享,这个就是所谓的context域对象
               Servlet可以通过ServletContext对象的setAttribute(key ,value)方法将数据存放到ServletContext对象中
               然后其他的Servlet对象可以通过ServletContext对象的getAttribute( key ) 来获取这些数据
               例如:
                   servlet1:
                        this.getServletConfig().getServletContext().setAttribute("abc" ,"123456789") ;
                  servlet2:
                        this.getServletConfig().getServletContext().getAttribute("abc")
 
          ② 获取WEB应用的初始化参数,就是web.xml的<context> </context>元素维护的WEB应用初始化参数
              
              程序中的使用
              
         ③ 实现Servlet的转发
             RequestDispatcher rd = context.getRequestDispatcher("/1.jsp"); 
     rd.sendRedirect(req ,resp);
 
         ④ 访问资源文件
             得到文件路径
             读取资源文件的三种方式
             .properties的文件
 
             数据有关系则使用xml,没有关系则使用.properties文件
 
             在sevlet中,“/"表示当前web应用目录
             
            ServletContext.getResourceAsStream()
            ServletContext.getRealPath()
            //普通java类中读取文件
            ClassLoader.getResourceAsStream(); //1)文件不能太大 2)文件只会被加载一次,所以不能实时读取更新后的文件内容
            ClassLoader.getResource(file_name).getPath()   //得到文件路径,然后再使用FileInputStream进行处理
 
 
    ServletResponse对象
    用于向客户端输出响应数据。该对象实例再每次客户端请求时,web服务器都会创建一个新的response对象,然后通过service()方法传给当前servlet。
    在子类HttpServletResponse中提供了许多用户操作Http响应消息头的方法,如addHeader(name ,value) 和setHeader(name ,value) ,
    这两个方法的区别是addHeader是添加HTTP消息头,而setHeader则如果已经存在消息头,则会更新,否则添加该消息头
    OutputStream 和writer,都是用于输出响应报文体内容。
    outputStream用于输出字节流,可以处理任何内容,而writer处理字符流,只能处理字符数据
    response.getOutputStream()与response.getWriter()不能同时调用
 
   实现文件下载
  
 
 
    控制浏览器缓存
    
    
 
     ServletResponse实现重定向
       1)通过http消息头来实现
            response.setStatus(302);
            response.setHeader("location" ,"/firstServlet/index.jsp")
       2)通过response提供的函数来实现
            response.sendRedirect("/firstServlet/index.jsp")
       特点:
       1)浏览器地址栏的的地址内容会发生变化
       2)浏览器发生了两次请求
       例如:
       
 
 
       response一些细节
       1)getOutputStream 和 getWriter这两个方式相互排斥,不能在一个请求中同时这两个方法
            
       2)Servlet程序向ServletOutputStream或PrintWriter对象写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当做响应消息的正文,然后在与各响应状态行和各响应消息头组合后输出到客户端
       3)Servlet的service方法调用结束后,Serlvet引擎会检查getOutputStream 和 getWriter返回的输出流对象是否已经调用过close,如果没有,则Servlet引擎会调用close。
       4)服务器在向客户端输出时,即调用out.prinln(),并不会及时的返回到客户端,而是会放到缓冲区中,只有当缓冲区溢出或则调用out.flush()   或则response.flushBuffer()提交时,才向客户端输出,而调用response.sendRedirect()会清空缓冲区内容,所以在跳转之前写的客户端输出代码不会有效果
 
 
 
 
     ServletRequest对象
     获取客户端传输的参数
     1)request.getParameter("key")
     2)request.getParameterNames();
     3)request.getParameterValues();
     4)request.getParameterMap();
          可以结合BeanUtils使用
 
     request乱码问题
     对于post提交方式:
          在getParameter()之前先设置request的码表,即request.setCharacterEncoding("charset")
     对于get方式(这里无论是表单提交还是普通的超链接提交)
          默认情况下,request使用ISO8859-1进行解码,所以先获取参数值,再手工转码处理
          String value = request.getParameter("key")
          String newValue = new String(values.getBytes("ISO8859-1"),"new charset")
 
     request实现服务器端跳转
     request.getRequestDispatcher("url").forward(req ,resp);
 
 
      
     实现页面跳转方式总结
     首先,页面跳转有浏览器端跳转和服务器跳转两种方式,
     浏览器端跳转时,浏览器地址栏请求地址发生改变,浏览器总共发生了两次请求,此时这两次请求时独立的
     服务器端跳转时,这种方式对客户来说是透明的,客户完成察觉不到。浏览器地址栏请求地址没变化,跳转前请求页面和跳转后页面是同一个request对象
 
      //客户端跳转,通过SerlvetResponse对象完成,location可以为绝对URL或者相对URL,如果是相对URL,则系统在将它放入location报头之前,自动将相对URL转换成绝对URL
      resp.sendRedirect(location)
   等价于
   response.setStatus(302);
      response.setHeader("location"  , location)
 
   //服务器端跳转,通过RequestDispatcher对象完成,RequestDispatcher对象可以通过SerlvetRequest对象或者SerlvetContext对象的获取,但是两者有区别
   RequestDispatcher.forward(req, resp);
   
   //这个可以是绝对地址也可以使相对地址,如果这里使用相对地址,则同ServletContext.getRequestDispatcher,相对于application_root的地址
   req.getRequestDispatcher("/servlet005/005")   
 
   //这里使用相对地址,相对于application_root的地址 
   ServletContext.getRequestDispatcher("/servlet005/005"
 
 
  
   Servlet实现缓存
   浏览器缓存原理
   第一种, 浏览器把缓存文件的最后修改时间通过 header ”If-Modified-Since“来告诉Web服务器
       1. 浏览器客户端想请求一个文档,  首先检查本地缓存,发现存在这个文档的缓存,  获取缓存中文档的最后修改时间,通过: If-Modified-Since, 发送Request给Web服务器。

       2. Web服务器收到Request,将服务器的文档修改时间(Last-Modified): 跟request header 中的,If-Modified-Since相比较, 如果时间是一样的, 说明缓存还是最新的, Web服务器将发送304 Not Modified给浏览器客户端, 告诉客户端直接使用缓存里的版本

 

 

       第二种, 浏览器把缓存文件的ETag, 通过header "If-None-Match", 来告诉Web服务器。
 
 
       禁止浏览器缓存
         resp.setHeader("Pragma","No-cache");
     resp.setHeader("Cache-Control","no-cache");、
     resp.setDateHeader("Expires",0);
 
 
 
 Servlet处理表单数据
 
servlet容易已经帮我进行url解码,并将相关参数进行解析处理,我们仅需要调用以下一些方法便可以获取requet中的相关参数
 
//该方法获取某个参数名为name的参数值,如果该参数名对应多个参数,则返回第一出现的值

req.getParameter(name)


例如:
xxxx?userName=test&passwd=123456
调用getParameter("userName") 则返回test
 
xxxx?userName=test&passwd=123456&userName=abc
上面url出现参数userName两次,调用getParameter("userName") 也是返回test
 
 
 
 
//返回该请求中所有的参数名称,它是一个Enumeration类型
req.getParameterNames()
 
例如:
xxxx?userName=test&passwd=123456
调用getParameterNames() 则返回Enumeration,该对象包含userName和passwd两个参数名
 
 
 
//返回该请求中所有的参数名称,它是一个Map类型,该Map实例的key是参数名称,value就是参数值所组成的数组。
getParameterMap()
 
 
 
//获取指定参数名称的所有值,返回值为数组类型
getParameterValues(name)
xxxx?userName=test&passwd=123456&userName=abc
上面url出现参数userName两次,调用getParameter("userName") 则返回由{"test" ,"abc"}组成的数组
 
 
 
Servlet Web页面压缩技术—gzip
在serlvet 中使用gzip压缩技术页面进行压缩返回给客户端浏览器,使用步骤如下:
 
1)首先通过客户端发送的Http报文头Accept-Encoding的字段值是否包含gzip,例如:Accept-Encoding : gzip  //因为不是所有客户端都支持gzip编码
2)设置返回内容编码格式:resp.setHeader("Content-Encoding""gzip");  //告诉客户端返回的gzip编码内容
2)创建GZIPOutputStream输出流对象,OutputStream out = new GZIPOutputStream(resp.getOutputStream());
3)将输出内容写入输出流对象,out.write(content.getBytes());
4)关闭输出流对象out.close( ) //??这一步必须执行,否则无法将输出内容返回给客户端
 
 
 
Servlet中Cookie的管理
cookie是由服务器向客户端发送,并将内容保存在浏览器客户端
 
服务器端向客户端发送cookie
1、创建Cookie对象,创建Cookie对象需要传入cookie的name和value,这两个参数都是字符串类型
      Cookie cookie = new Cookie("name" ,"vaue");
      注意:这样创建出来的cookie是会话级别的,即,会话结束后,该cookie对象就会被浏览器清理掉,如果希望cookie保存到硬盘,则进行第二步
 
2、设置cookie的最大有效期限。负数表示临时的,是会话级别的, 0表示是临时的(如果是新cookie)或者要求浏览器删除掉已经存在cookie,正数表示cookie的有效期限,以秒为单位
     cookie.setMaxAge(senconds)  //senconds以秒为单位
 
3、将cookie发送到客户端。
      如果cookie对象在服务器端创建后没有发送到客户端,那么该cookie是不启作用的。
      HttpServletResponse.addCookie( cookie )  //把cookie添加http协议报头的Set-Cookies字段返回给客户端
 
 服务器端读取客户端的cookies
 Cookie[] cookies = HttpServletRequest.getCookies( ); 
 因为浏览器是以主机或者域名的方式保存cookie,因此,即使当前请求的URL没有向浏览器发送过cookie,但是HttpServletRequest.getCookies( )可能不为空的。
 因此,需要遍历cookies这个数组,然后根据cookie的name来判断是否我们需要的cookie
 
修改浏览器cookie值
因为浏览器向服务器端发送cookie信息时,仅仅是发送cookie的name和lvalue,其他的信息,如maxage都已经"丢失"
所以需要修改cookie值时,需要重新创建一个同名的cookie,同时设置它maxage等属性
 
 
 
 
Servlet会话跟踪(Session)
1、什么是session?
Session代表服务器与浏览器的一次会话过程,这个过程是连续的,也可以时断时续的,但是这个过程通过session来维护。在Servlet中,session指的是HttpSession类的对象。
 
2、session的原理
首先session是由服务器端产生的,在服务器端保存,然后在服务器返回响应页面时,在http响应报文头中将session的id以cookie的方式返回给浏览器吗,一般为JSESSIONID=xxxxx。后续浏览器请求页面时,再将该session id传给服务器,服务器首先根据这个session id查看有没对应的session对象,如果有,则直接取出来用。
 
首次访问,服务器通过Set-Cookie报头将session id发送给客户端:
 
第二次访问时,浏览器通过请求报头Cookie,将session id发送给服务器
 
3、Servlet对session的管理
Servlet中对应session接口类为HttpSession,该类提供一下方法对session进行管理
 
 
4、session误区
《1》Session创建的时间,即session是什么时候被创建的?
一 个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <% @page session="false"%> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句 HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的 session对象的来历。
由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。
 
引申:
1)、访问*.html的静态资源因为不会被编译为Servlet,也就不涉及session的问题。
2)、当JSP页面没有显式禁止session的时候,在打开浏览器第一次请求该jsp的时候,服务器会自动为其创建一个session,并赋予其一个sessionID,发送给客户端的浏览器。以后客户端接着请求本应用中其他资源的时候,会自动在请求头上添加:
Cookie:JSESSIONID=客户端第一次拿到的session ID
这样,服务器端在接到请求时候,就会收到session ID,并根据ID在内存中找到之前创建的session对象,提供给请求使用。这也是session使用的基本原理----搞不懂这个,就永远不明白session的原理。
首次访问
 
第二次访问
 
通过图可以清晰发现,第二次请求的时候,已经添加session ID的信息。
 
 
《2》服务器端Session删除的时间
  1)Session超时:超时指的是连续一定时间服务器没有收到该Session所对应客户端的请求,并且这个时间超过了服务器设置的Session超时的最大时间。
  2)程序调用HttpSession.invalidate()
  3)服务器关闭或服务停止
 
 
《3》session存放在哪里
 服务器端的内存中。不过session可以通过特殊的方式做持久化管理
    
 
《4》session的id是从哪里来,sessionID是如何使用的
当 客户端第一次请求session对象时候,服务器会为客户端创建一个session,并将通过特殊算法算出一个session的ID,用来标识该 session对象,当浏览器下次(session继续有效时)请求别的资源的时候,浏览器会偷偷地将sessionID放置到请求头中,服务器接收到请 求后就得到该请求的sessionID,服务器找到该id的session返还给请求者(Servlet)使用。一个会话只能有一个session对象, 对session来说是只认id不认人
 
 
《5》session会因为浏览器的关闭而删除吗?
 不会,session只会通过上面提到的方式去关闭。
 
 
《6》关闭浏览器是否session也就无效了?
其实不是,关闭浏览器,只能说该浏览器客户端与服务器端的本次会话结束,因为关闭浏览器时,浏览器会把保存session id的cookie JSESSIONID删除了,
因此,该浏览器客户端下次再请求时,已经没有session id发给服务器端,因此服务器会创建一个新的session,旧的那个session,等待超时后,自动销毁
 
         
          
      





原文地址:https://www.cnblogs.com/itmanxgl/p/9617f98d947941174454f4a6b574c3f7.html