JavaWeb开发与代码的编写(二)

2023-11-15

JavaWeb开发与代码的编写(二)

Http协议

  HTTP是hypertext transfer protocol(超文本传输协议)的简写,它是TCP/IP协议的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的过程。客户端连上web服务器后,若想获得web服务器中的某个web资源,需遵守一定的通讯格式,HTTP协议用于定义客户端与web服务器通迅的格式。

  HTTP协议的版本:HTTP/1.0、HTTP/1.1

  在HTTP1.0协议中,客户端与web服务器建立连接后,只能获得一个web资源。
  在HTTP1.1协议,允许客户端与web服务器建立连接后,在一个连接上获取多个web资源。

HTTP请求包括的内容

  客户端连上服务器后,向服务器请求某个web资源,称之为客户端向服务器发送了一个HTTP请求。

一个完整的HTTP请求包括如下内容:一个请求行、若干消息头、以及实体内容
范例:

  

HTTP请求的细节——请求行

  请求行中的GET称之为请求方式,请求方式有:POST、GET、HEAD、OPTIONS、DELETE、TRACE、PUT,常用的有: GET、 POST
  用户如果没有设置,默认情况下浏览器向服务器发送的都是get请求,例如在浏览器直接输地址访问,点超链接访问等都是get,用户如想把请求方式改为post,可通过更改表单的提交方式实现。
  不管POST或GET,都用于向服务器请求某个WEB资源,这两种方式的区别主要表现在数据传递上:如果请求方式为GET方式,则可以在请求的URL地址后以?的形式带上交给服务器的数据,多个数据之间以&进行分隔,例如:GET /mail/1.html?name=abc&password=xyz HTTP/1.1
  GET方式的特点:在URL地址后附带的参数是有限制的,其数据容量通常不能超过1K。
  如果请求方式为POST方式,则可以在请求的实体内容中向服务器发送数据,Post方式的特点:传送的数据量无限制。

HTTP请求的细节——消息头

  HTTP请求中的常用消息头

  accept:浏览器通过这个头告诉服务器,它所支持的数据类型
  Accept-Charset: 浏览器通过这个头告诉服务器,它支持哪种字符集
  Accept-Encoding:浏览器通过这个头告诉服务器,支持的压缩格式
  Accept-Language:浏览器通过这个头告诉服务器,它的语言环境
  Host:浏览器通过这个头告诉服务器,想访问哪台主机
  If-Modified-Since: 浏览器通过这个头告诉服务器,缓存数据的时间
  Referer:浏览器通过这个头告诉服务器,客户机是哪个页面来的  防盗链
  Connection:浏览器通过这个头告诉服务器,请求完后是断开链接还是何持链接

例如:

 Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, 
     application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
 Referer: http://localhost:8080/JavaWebDemoProject/Web/2.jsp
 Accept-Language: zh-CN
 User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3)
 Accept-Encoding: gzip, deflate
 Host: localhost:8080
 Connection: Keep-Alive

HTTP响应

HTTP响应包括的内容

  一个HTTP响应代表服务器向客户端回送的数据,它包括: 一个状态行、若干消息头、以及实体内容 。

  
范例:

 HTTP/1.1 200 OK
 Server: Apache-Coyote/1.1
 Content-Type: text/html;charset=ISO-8859-1
 Content-Length: 105
 Date: Tue, 27 May 2014 16:23:28 GMT 
 <html>
     <head>
         <title>Hello World JSP</title>
     </head>
     <body>
         Hello World!
 
     </body>
 </html>

HTTP响应的细节——状态行

    状态行格式: HTTP版本号 状态码 原因叙述<CRLF>
      举例:HTTP/1.1 200 OK
  状态码用于表示服务器对请求的处理结果,它是一个三位的十进制数。响应状态码分为5类,如下所示:
  

HTTP响应细节——常用响应头

  HTTP响应中的常用响应头(消息头)
  Location: 服务器通过这个头,来告诉浏览器跳到哪里
  Server:服务器通过这个头,告诉浏览器服务器的型号
  Content-Encoding:服务器通过这个头,告诉浏览器,数据的压缩格式
  Content-Length: 服务器通过这个头,告诉浏览器回送数据的长度
  Content-Language: 服务器通过这个头,告诉浏览器语言环境
  Content-Type:服务器通过这个头,告诉浏览器回送数据的类型
  Refresh:服务器通过这个头,告诉浏览器定时刷新
  Content-Disposition: 服务器通过这个头,告诉浏览器以下载方式打数据
  Transfer-Encoding:服务器通过这个头,告诉浏览器数据是以分块方式回送的
  Expires: -1  控制浏览器不要缓存
  Cache-Control: no-cache  
  Pragma: no-cache

在服务端设置响应头来控制客户端浏览器的行为

设置Location响应头,实现请求重定向

 package gacl.http.study;
 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 ServletDemo01 extends HttpServlet {
     public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
 
         response.setStatus(302);//设置服务器的响应状态码
         /**
          *设置响应头,服务器通过 Location这个头,来告诉浏览器跳到哪里,这就是所谓的请求重定向
          */
         response.setHeader("Location", "/JavaWeb_HttpProtocol_Study_20140528/1.jsp");
     }
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         this.doGet(request, response);
     }
 }

  当在浏览器中使用URL地址"http://localhost:8080/JavaWeb_HttpProtocol_Study_20140528/servlet/ServletDemo01"访问ServletDemo01时,就可以看到服务器作出响应后发送到浏览器的状态码和响应头信息,如下图所示:

  

  服务器返回一个302状态码告诉浏览器,你要的资源我没有,但是我通过Location响应头告诉你哪里有,而浏览器解析响应头Location后知道要跳转到/JavaWeb_HttpProtocol_Study_20140528/1.jsp页面,所以就会自动跳转到1.jsp,如下图所示:

  

设置Content-Encoding响应头,告诉浏览器数据的压缩格式

 package gacl.http.study;
  
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.util.zip.GZIPOutputStream;
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
 /**
  *这个小程序是用来演示以下两个小知识点
  *1、使用GZIPOutputStream流来压缩数据
  *2、设置响应头Content-Encoding来告诉浏览器,服务器发送回来的数据压缩后的格式
  */
 public class ServletDemo02 extends HttpServlet {
 
     public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
         String data = "abcdabcdabcdabcdabcdabcdab" +
                 "cdabcdabcdabcdabcdabcdabcdabcdabc" +
                 "dabcdabcdabcdabcdabcdabcdabcdabc" +
                 "dabcdabcdabcdabcdabcdabcdabcdabcdab" +
                 "cdabcdabcdabcdabcdabcdabcdabcdabcdab" +
                 "cdabcdabcdabcdabcdabcdabcdabcdabcdab" +
                 "cdabcdabcdabcdabcdabcdabcdabcdabcdab" +
                 "cdabcdabcdabcdabcdabcdabcdabcdabcdabcd";
        System.out.println("原始数据的大小为:" + data.getBytes().length);         
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
         GZIPOutputStream gout = new GZIPOutputStream(bout); //buffer
         gout.write(data.getBytes());
         gout.close();
         //得到压缩后的数据
         byte g[] = bout.toByteArray();
         response.setHeader("Content-Encoding", "gzip");
         response.setHeader("Content-Length",g.length +"");
         response.getOutputStream().write(g);
     }
 
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         this.doGet(request, response);
     }
 }

服务器发给浏览器的响应信息如下:

浏览器支持的压缩格式有:

设置content-type响应头,指定回送数据类型

 package gacl.http.study;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  public class ServletDemo03 extends HttpServlet {
     public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         /**
          * 浏览器能接收(Accept)的数据类型有: 
          * application/x-ms-application, 
          * image/jpeg, 
          * application/xaml+xml, 
          * image/gif, 
          * image/pjpeg, 
          * application/x-ms-xbap, 
          * application/vnd.ms-excel, 
          * application/vnd.ms-powerpoint, 
          * application/msword, 
          */
         response.setHeader("content-type", "image/jpeg");//使用content-type响应头指定发送给浏览器的数据类型为"image/jpeg"
         //读取位于项目根目录下的img文件夹里面的WP_20131005_002.jpg这张图片,返回一个输入流
         InputStream in = this.getServletContext().getResourceAsStream("/img/WP_20131005_002.jpg");
         byte buffer[] = new byte[1024];
         int len = 0;
         OutputStream out = response.getOutputStream();//得到输出流
         while ((len = in.read(buffer)) > 0) {//读取输入流(in)里面的内容存储到缓冲区(buffer)
             out.write(buffer, 0, len);//将缓冲区里面的内容输出到浏览器
         }
     }
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         this.doGet(request, response);
     }
 }

服务器发给浏览器的响应信息如下:

ServletDemo03的运行结果如下图所示:

在浏览器中显示出了图片

设置refresh响应头,让浏览器定时刷新

 package gacl.http.study;
 
 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 ServletDemo04 extends HttpServlet {
     public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         /**
          * 设置refresh响应头,让浏览器每隔3秒定时刷新
          */
         // response.setHeader("refresh", "3");
         /**
          * 设置refresh响应头,让浏览器3秒后跳转到http://www.baidu.com
          */
         response.setHeader("refresh", "3;url='http://www.baidu.com'");
        response.getWriter().write("gacl");
     } 
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         this.doGet(request, response);
     }
 }

设置content-disposition响应头,让浏览器下载文件

 package gacl.http.study; 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream; 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse; 
 public class ServletDemo05 extends HttpServlet {
     public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         /**
          * 设置content-disposition响应头,让浏览器下载文件
          */
         response.setHeader("content-disposition", "attachment;filename=xxx.jpg");
         InputStream in = this.getServletContext().getResourceAsStream("/img/1.jpg");
         byte buffer[] = new byte[1024];
         int len = 0;
         OutputStream out = response.getOutputStream();
         while ((len = in.read(buffer)) > 0) {
             out.write(buffer, 0, len);
         }
     } 
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         this.doGet(request, response);
     }
 
 }

在浏览器中访问ServletDemo05就会弹出文件下载框,如下图所示:

  

 

Servlet简介

 Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。使用 Servlet,可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
  Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:
  1、编写一个Java类,实现servlet接口。
  2、把开发好的Java类部署到web服务器中。
  按照一种约定俗成的称呼习惯,通常我们也把实现了servlet接口的java程序,称之为Servlet

 

Servlet的运行过程

Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:
  ①Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。
  ②装载并创建该Servlet的一个实例对象。 
  ③调用Servlet实例对象的init()方法。
  ④创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
  ⑤WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。 

Servlet调用图

Servletè°ç¨å¾

 

在Eclipse中开发Servlet

第一种方式:继承HttpServlet

Servlet接口实现类

  Servlet接口SUN公司定义了两个默认实现类,分别为:GenericServlet、HttpServlet。

  HttpServlet指能够处理HTTP请求的servlet,它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能更为强大。因此开发人员在编写Servlet时,通常应继承这个类,而避免直接去实现Servlet接口。
  HttpServlet在实现Servlet接口时,覆写了service方法,该方法体内的代码会自动判断用户的请求方式,如为GET请求,则调用HttpServlet的doGet方法,如为Post请求,则调用doPost方法。因此,开发人员在编写Servlet时,通常只需要覆写doGet或doPost方法,而不要去覆写service方法。

 选中gacl.servlet.study包,右键→New→Servlet,如下图所示:

  

  

  

这样,我们就通过Eclipse帮我们创建好一个名字为ServletDemo1的Servlet,创建好的ServletDemo01里面会有如下代码:

 package gacl.servlet.study; 
 import java.io.IOException;
 import java.io.PrintWriter; 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse; 
 public class ServletDemo1 extends HttpServlet {
     /**
      * The doGet method of the servlet. <br>
      *
      * This method is called when a form has its tag value method equals to get.
      * 
      * @param request the request send by the client to the server
      * @param response the response send by the server to the client
      * @throws ServletException if an error occurred
      * @throws IOException if an error occurred
     */
     public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
           response.setContentType("text/html");
         PrintWriter out = response.getWriter();
         out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
         out.println("<HTML>");
         out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");
         out.println("  <BODY>");
         out.print("    This is ");
         out.print(this.getClass());
         out.println(", using the GET method");
         out.println("  </BODY>");
         out.println("</HTML>");
         out.flush();
         out.close();
     } 
     /**
      * The doPost method of the servlet. <br>
      *
      * This method is called when a form has its tag value method equals to post.
      * 
      * @param request the request send by the client to the server
      * @param response the response send by the server to the client
      * @throws ServletException if an error occurred
      * @throws IOException if an error occurred
      */
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
        response.setContentType("text/html");
         PrintWriter out = response.getWriter();
         out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
         out.println("<HTML>");
         out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");
         out.println("  <BODY>");
         out.print("    This is ");
         out.print(this.getClass());
         out.println(", using the POST method");
         out.println("  </BODY>");
         out.println("</HTML>");
         out.flush();
         out.close();
     }
 }

  这些代码都是Eclipse自动生成的,而web.xml文件中也多了<servlet></servlet>和<servlet-mapping></servlet-mapping>两对标签,这两对标签是配置ServletDemo1的,如下图所示:

然后我们就可以通过浏览器访问ServletDemo1这个Servlet,如下图所示:

  

第二种方式:实现接口 Servlet

第一步:创建一个JavaWeb项目,并创建一个servlet类-----HelloServlet,实现接口 Servlet

import java.io.IOException;
 
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
 
public class HelloServlet implements Servlet{
    //只被调用一次,第一次请求Servlet时,创建Servlet的实例,调用构造器
    public HelloServlet() {
        System.out.println("构造器 HelloServelt()...");
    }
     
    //该方法用于初始化Servlet,就是把该Servlet装载入内存
    //只被调用一次,在创建好实例后立即被调用
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("初始化方法 init()...");
    }
     
    //被多次调用,每次请求都会调用service方法。实际用于响应请求的
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("执行方法主体 service()...");
    }
    //只被调用一次,在当前Servlet所在的WEB应用被卸载前调用,用于释放当前Servlet所占用的资源
    @Override
    public void destroy() {
        System.out.println("servlet 销毁时调用方法 destroy()...");
    }
 
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }
 
    @Override
    public String getServletInfo() {
        return null;
    }
}

第二步:在 web.xml 文件中配置上面创建的 HelloServlet 映射关系

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
     http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      id="WebApp_ID" version="3.0">
  <!--在tomcat 服务器中运行时,如果不指名访问文件名,默认的根据项目名访问文件顺序如下配置  -->
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
   
  <!--给创建的 Servlet 配置映射关系  -->
  <servlet>
    <servlet-name>helloServlet</servlet-name>
    <servlet-class>com.ys.servlet.HelloServlet</servlet-class>
                    <!--servlet的完整名称--> 
  </servlet>
   
  <servlet-mapping>
    <servlet-name>helloServlet</servlet-name>
                <!-- 与上面配置的 servlet-name 名字要对应,一个servlet可以有多个 servlet-mapping  -->
    <url-pattern>/hello</url-pattern> 
                <!--访问路径-->
  </servlet-mapping>
</web-app>

第三步:将项目部署在 tomcat 服务器,如何部署请看这篇文章:http://www.cnblogs.com/ysocean/p/6893446.html,然后启动服务器

  这里我们项目的结构为:

    

   ①、我们直接通过项目名来访问,由于我们在 web.xml 文件中配置了 <welcome-file-list>,那么会依次找下面配置的文件,我们只创建了一个 index.jsp,那么就会访问这个JSP 文件

  

  ②、通过在 web.xml 文件中配置的<url-pattern>/hello</url-pattern>  来访问

  

  我们可以看控制台打印内容如下:

  

  如果我们不断的刷新  http://localhost:8080/ServletImprove/hello 这个访问链接,那么控制台如下:

  

Servlet 的生命周期

  我们通过上面的实例,可以看到也就是只有第一次才会执行 构造器和 init() 方法,后面每次点击都只调用 service() 方法。那这是为什么呢?

  

上面这幅图可以这样理解:

  1、客户端向 Web 服务器发送请求,服务器查询 web.xml 文件配置。根据请求信息找到对应的 Servlet。

  2、Servlet 引擎检查是否已经装载并创建了该 Servlet 的实例对象,如果有,则直接执行第4步,否则执行第3步,

  3、Web 服务器加载 Servlet,并调用 Servlet 构造器(只会调用一次),创建 Servlet 的实例对象。并调用 init() 方法,完成 Servlet 实例对象的初始化(只会调用一次)。

  4、Web 服务器把接收到的 http 请求封装成 ServletRequest 对象,并创建一个 响应消息的 ServletResponse 对象,作为 service() 方法的参数传入。(每一次访问都会调用一次该方法)

  5、执行 service() 方法,并将处理信息封装到 ServletResponse 对象中返回

  6、浏览器拆除 ServletResponse 对象,形成 http 响应格式,返回给客户端。

  7、Web 应用程序停止或者重新启动之前,Servlet 引擎将卸载 Servlet实例,并在卸载之前调用 destory() 方法

 

创建 Servlet 的三种方法

  第一种:就是我们上面写的 实现接口 Servlet

  第二种:由于实现接口我们需要实现里面所有的方法,里面有一些方法我们可能并不想实现,那么我们就继承 GenericServlet 类

import java.io.IOException;
 
import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
 
public class HelloServlet extends GenericServlet{
    //只被调用一次,第一次请求Servlet时,创建Servlet的实例,调用构造器
    public HelloServlet() {
        System.out.println("构造器 HelloServelt()...");
    }
    //该方法用于初始化Servlet,就是把该Servlet装载入内存
    //只被调用一次,在创建好实例后立即被调用
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("初始化方法 init()...");
    }
     
    //被多次调用,每次请求都会调用service方法。实际用于响应请求的
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("执行方法主体 service()...");
    }
    //只被调用一次,在当前Servlet所在的WEB应用被卸载前调用,用于释放当前Servlet所占用的资源
    @Override
    public void destroy() {
        System.out.println("servlet 销毁时调用方法 destroy()...");
    }
 
}

 第三种:通常我们浏览器发出的请求都是 http 请求,那么请求方式可能有多种,比如 get,post,而我们在处理请求的时候都是在 service() 方法中,这种方式显然不够明确。那么我们通常是 继承 HttpServlet 类

import java.io.IOException;
 
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class HelloServlet extends HttpServlet{
    //只被调用一次,第一次请求Servlet时,创建Servlet的实例,调用构造器
    public HelloServlet() {
        System.out.println("构造器 HelloServelt()...");
    }
    //该方法用于初始化Servlet,就是把该Servlet装载入内存
    //只被调用一次,在创建好实例后立即被调用
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("初始化方法 init()...");
    }
    //处理 post 请求
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     
    }
    //处理get请求
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     
    }
     
    //只被调用一次,在当前Servlet所在的WEB应用被卸载前调用,用于释放当前Servlet所占用的资源
    @Override
    public void destroy() {
        System.out.println("servlet 销毁时调用方法 destroy()...");
    }
 
     
}

  其实上面三种方法,后面两种都是对 Servlet 类的封装,我们可以看 API,其实 HttpServlet 是继承 GenericServlet的。

    

  而 GenericServlet 又是实现 Servlet 接口的

    

  

 

Servlet开发注意细节

Servlet访问URL映射配置

  由于客户端是通过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的对外访问路径。例如:

   <servlet>
     <servlet-name>ServletDemo1</servlet-name>
     <servlet-class>gacl.servlet.study.ServletDemo1</servlet-class>
   </servlet>
   <servlet-mapping>
     <servlet-name>ServletDemo1</servlet-name>
     <url-pattern>/servlet/ServletDemo1</url-pattern>
   </servlet-mapping>

  同一个Servlet可以被映射到多个URL上,即多个<servlet-mapping>元素的<servlet-name>子元素的设置值可以是同一个Servlet的注册名。 例如:

  <servlet>
     <servlet-name>ServletDemo1</servlet-name>
     <servlet-class>gacl.servlet.study.ServletDemo1</servlet-class>
   </servlet> 
   <servlet-mapping>
     <servlet-name>ServletDemo1</servlet-name>
     <url-pattern>/servlet/ServletDemo1</url-pattern>
   </servlet-mapping>
  <servlet-mapping>
     <servlet-name>ServletDemo1</servlet-name>
     <url-pattern>/1.htm</url-pattern>
   </servlet-mapping>
    <servlet-mapping>
     <servlet-name>ServletDemo1</servlet-name>
     <url-pattern>/2.jsp</url-pattern>
   </servlet-mapping>
    <servlet-mapping>
     <servlet-name>ServletDemo1</servlet-name>
     <url-pattern>/3.php</url-pattern>
   </servlet-mapping>
    <servlet-mapping>
     <servlet-name>ServletDemo1</servlet-name>
    <url-pattern>/4.ASPX</url-pattern>
   </servlet-mapping>

 通过上面的配置,当我们想访问名称是ServletDemo1的Servlet,可以使用如下的几个地址去访问:

  http://localhost:8080/JavaWeb_Servlet_Study_20140531/servlet/ServletDemo1

  http://localhost:8080/JavaWeb_Servlet_Study_20140531/1.htm

  http://localhost:8080/JavaWeb_Servlet_Study_20140531/2.jsp

  http://localhost:8080/JavaWeb_Servlet_Study_20140531/3.php

  http://localhost:8080/JavaWeb_Servlet_Study_20140531/4.ASPX

    ServletDemo1被映射到了多个URL上。

 

Servlet访问URL使用*通配符映射  

在Servlet映射到的URL中也可以使用*通配符,但是只能有两种固定的格式:一种格式是"*.扩展名",另一种格式是以正斜杠(/)开头并以"/*"结尾。例如:

  

  <servlet>
     <servlet-name>ServletDemo1</servlet-name>
     <servlet-class>gacl.servlet.study.ServletDemo1</servlet-class>
   </servlet> 
    <servlet-mapping>
     <servlet-name>ServletDemo1</servlet-name>
   <url-pattern>/*</url-pattern>

  *可以匹配任意的字符,所以此时可以用任意的URL去访问ServletDemo1这个Servlet,如下图所示:

  

对于如下的一些映射关系:
  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。
  匹配的原则就是"谁长得更像就找谁"

 

Servlet 的多线程问题

  我们通过 Servlet 的生命周期可以知道,Servlet 类的构造器只会在第一次访问的时候调用,后面的请求都不会再重新创建 Servlet 实例。即 Servlet 是单例,那么既然是单例的,那就要注意多线程访问所造成的安全问题。如下:

import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
 
public class HelloServlet extends GenericServlet{
    //多线程共享资源
    private int i = 0;
     
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        i++;
        //为了使多线程访问安全问题更加突出,我们增加一个延时程序
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(i);     
    }
     
}

  我们用两个浏览器,输入 http://localhost:8080/ServletImprove/hello,然后一起访问,不断刷新,结果如下:

    

结果分析:显然,我们用两个浏览器访问,便相当于两个线程,第一个访问,已经执行了 i++,但是还没来得及打印 i 的值,就马上就睡眠了;接着第二个浏览也来访问,执行 i++,那么i的值相当于增加加了两次1,然后这两个浏览器输出最终结果。这便造成了多线程访问共享资源造成冲突。

不存在线程安全问题的代码:

 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 ServletDemo3 extends HttpServlet {
 
     
     public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         
         /**
          * 当多线程并发访问这个方法里面的代码时,会存在线程安全问题吗
          * i变量被多个线程并发访问,但是没有线程安全问题,因为i是doGet方法里面的局部变量,
          * 当有多个线程并发访问doGet方法时,每一个线程里面都有自己的i变量,
          * 各个线程操作的都是自己的i变量,所以不存在线程安全问题
          * 多线程并发访问某一个方法的时候,如果在方法内部定义了一些资源(变量,集合等)
          * 那么每一个线程都有这些东西,所以就不存在线程安全问题了
          */
         int i=1;
         i++;
         response.getWriter().write(i);
     } 
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         doGet(request, response);
     } 

那么在 Servlet 中如何处理呢? 

  第一种方法:使用同步代码块  

import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
 
public class HelloServlet extends GenericServlet{
    //多线程共享资源
    private int i = 0;
     
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        synchronized (this) {
            i++;
            //为了使多线程访问安全问题更加突出,我们增加一个延时程序
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i);     
        }
    }
     
}

 结果:

    

分析:这种办法虽然能解决多线程同步问题,但是如果 延时程序特别长,那么会造成访问假死的现象。即第一个线程访问结果没有出来,第二个线程就会一直卡死,出不来结果。

但是编写Servlet却万万不能用这种方式处理线程安全问题,假如有9999个人同时访问这个Servlet,那么这9999个人必须按先后顺序排队轮流访问。

  针对Servlet的线程安全问题,Sun公司是提供有解决方案的:让Servlet去实现一个SingleThreadModel接口,如果某个Servlet实现了SingleThreadModel接口,那么Servlet引擎将以单线程模式来调用其service方法。
  查看Sevlet的API可以看到,SingleThreadModel接口中没有定义任何方法和常量,在Java中,把没有定义任何方法和常量的接口称之为标记接口,经常看到的一个最典型的标记接口就是"Serializable",这个接口也是没有定义任何方法和常量的,标记接口在Java中有什么用呢?主要作用就是给某个对象打上一个标志,告诉JVM,这个对象可以做什么,比如实现了"Serializable"接口的类的对象就可以被序列化,还有一个"Cloneable"接口,这个也是一个标记接口,在默认情况下,Java中的对象是不允许被克隆的,就像现实生活中的人一样,不允许克隆,但是只要实现了"Cloneable"接口,那么对象就可以被克隆了。

  让Servlet实现了SingleThreadModel接口,只要在Servlet类的定义中增加实现SingleThreadModel接口的声明即可。  
  对于实现了SingleThreadModel接口的Servlet,Servlet引擎仍然支持对该Servlet的多线程并发访问,其采用的方式是产生多个Servlet实例对象,并发的每个线程分别调用一个独立的Servlet实例对象。
  实现SingleThreadModel接口并不能真正解决Servlet的线程安全问题,因为Servlet引擎会创建多个Servlet实例对象,而真正意义上解决多线程安全问题是指一个Servlet实例对象被多个线程同时调用的问题。事实上,在Servlet API 2.4中,已经将SingleThreadModel标记为Deprecated(过时的)。  

第二种办法:实现接口 SingleThreadModel

import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.SingleThreadModel;
 
public class HelloServlet extends GenericServlet implements SingleThreadModel{
    //多线程共享资源
    private int i = 0;
     
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        i++;
        //为了使多线程访问安全问题更加突出,我们增加一个延时程序
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(i);     
    }
     
}

  结果:

    

  分析:SingleThreadModel 接口指定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定,那么在这个Servlet中的service方法将不会有两个线程被同时执行,当然也就不存在线程安全的问题。但是,如果一个Servlet实现了SingleThreadModel接口,Servlet引擎将为每个新的请求创建一个单独的Servlet实例,这将引起大量的系统开销,在现在的Servlet开发中基本看不到SingleThreadModel的使用,这种方式了解即可,尽量避免使用。

第三种办法:避免使用实例变量

  线程安全问题很大一部分是由于实例变量造成的,那么我们只要在 Servlet 里面不定义任何的实例变量,那么就不会有线程安全的问题。因为在 Java 内存模型中,方法中的临时变量是在栈上分配空间,而且每个线程都有自己的私有栈空间,不会造成线程安全问题。

import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
 
public class HelloServlet extends GenericServlet{
     
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        int i = 0;
        i++;
        //为了使多线程访问安全问题更加突出,我们增加一个延时程序
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(i);     
    }
     
}

 结果:

  

 

Servlet与普通Java类的区别  

  Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度。
  针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,servlet实例对象才会销毁。
  在Servlet的整个生命周期内,Servlet的init方法只被调用一次。而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servlet的service方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。

  如果在<servlet>元素中配置了一个<load-on-startup>元素,那么WEB应用程序在启动时,就会装载并创建Servlet的实例对象、以及调用Servlet实例对象的init()方法。
    举例:

    <servlet>
        <servlet-name>invoker</servlet-name>
        <servlet-class>
            org.apache.catalina.servlets.InvokerServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

  用途:为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据。

 

Servlet 和 JSP 的区别

  ①、JSP 的本质就是 Servlet,JSP 经过编译后就会变为一个类似 Servlet 的Java文件

  ②、Servlet 基本是JAVA程序代码构成,擅长于流程控制和事务处理,当然也可以用来生成html代码,但是通过Servlet来生成动态网页很不直观.

  ③、JSP由HTML代码和JSP标签构成,可以方便地编写动态网页,当然里面也可以编写 Java代码,但是整体看上去不够优雅。而且比较麻烦

  所以:JSP侧重于视图,Servlet主要用于控制逻辑。

我们可以看一个 JSP 文件,index.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
    index.jsp
</body>
</html>

经过编译后:很显然下面的代码结构和 Servlet 是差不多的

package org.apache.jsp;
 
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent {
  private static final javax.servlet.jsp.JspFactory _jspxFactory =
          javax.servlet.jsp.JspFactory.getDefaultFactory();
  private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
  private javax.el.ExpressionFactory _el_expressionfactory;
  private org.apache.tomcat.InstanceManager _jsp_instancemanager;
  public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
    return _jspx_dependants;
  }
 
  public void _jspInit() {
    _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
    _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
  }
 
  public void _jspDestroy() {
  }
 
  public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
        throws java.io.IOException, javax.servlet.ServletException {
    final javax.servlet.jsp.PageContext pageContext;
    javax.servlet.http.HttpSession session = null;
    final javax.servlet.ServletContext application;
    final javax.servlet.ServletConfig config;
    javax.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.PageContext _jspx_page_context = null;
    try {
      response.setContentType("text/html; charset=ISO-8859-1");
      pageContext = _jspxFactory.getPageContext(this, request, response,
                null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;
 
      out.write("\r\n");
      out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");
      out.write("<html>\r\n");
      out.write("<head>\r\n");
      out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">\r\n");
      out.write("<title>Insert title here</title>\r\n");
      out.write("</head>\r\n");
      out.write("<body>\r\n");
      out.write("\tindex.jsp\r\n");
      out.write("</body>\r\n");
      out.write("</html>");
    } catch (java.lang.Throwable t) {
      if (!(t instanceof javax.servlet.jsp.SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try { out.clearBuffer(); } catch (java.io.IOException e) {}
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
        else throw new ServletException(t);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}

  JSP 页面的九个隐含对象:

  ①、request:HttpServletRequest的一个对象,封装请求信息

      ②、pageContext:页面的上下文,是PageContext的一个对象,可以从该对象中获取其它8个隐含对象。

      ③、session:代表浏览器和服务器的一次会话,是HttpSession 的一个对象

      ④、application:代表当前WEB应用,是ServletContext对象

      ⑤、config:当前JSP对应Servlet的ServletConfig对象

      ⑥、out:JspWriter对象,调用out.prinln()可以直接把字符串打印到浏览器上

      ⑦、page:指向当前JSP对应的Servlet对象的应用,但为Object类型,只能调用 Object 类的方法

      ⑧、exception:在声明了page指令的isErrorPage="true"时,才可以使用

 

缺省Servlet

  如果某个Servlet的映射路径仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用程序的缺省Servlet。 
  凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理所有其他Servlet都不处理的访问请求。 例如:

  <servlet>
     <servlet-name>ServletDemo2</servlet-name>
     <servlet-class>gacl.servlet.study.ServletDemo2</servlet-class>
     <load-on-startup>1</load-on-startup>
   </servlet>   
   <!-- 将ServletDemo2配置成缺省Servlet -->
   <servlet-mapping>
     <servlet-name>ServletDemo2</servlet-name>
     <url-pattern>/</url-pattern>
   </servlet-mapping>

 当访问不存在的Servlet时,就使用配置的默认Servlet进行处理,如下图所示:

  

  在<tomcat的安装目录>\conf\web.xml文件中,注册了一个名称为org.apache.catalina.servlets.DefaultServlet的Servlet,并将这个Servlet设置为了缺省Servlet。

     <servlet>
         <servlet-name>default</servlet-name>
         <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
         <init-param>
             <param-name>debug</param-name>
             <param-value>0</param-value>
         </init-param>
         <init-param>
             <param-name>listings</param-name>
             <param-value>false</param-value>
         </init-param>
         <load-on-startup>1</load-on-startup>
     </servlet>
  <!-- The mapping for the default servlet -->
     <servlet-mapping>
         <servlet-name>default</servlet-name>
         <url-pattern>/</url-pattern>
     </servlet-mapping>

  当访问Tomcat服务器中的某个静态HTML文件和图片时,实际上是在访问这个缺省Servlet。

 

Servlet 的转发和重定向

  重定向:

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
         
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        response.sendRedirect("index.jsp");//重定向
    }

  转发:

HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        //response.sendRedirect("index.jsp");
        request.getRequestDispatcher("/index.jsp").forward(request, response);//转发

我们再看看浏览器访问:同时输入 http://localhost:8080/ServletImprove/hello

  重定向变为:

     

  转发为:

    

本质区别:转发只发出了一次请求,而重定向发出了两次请求

  ①.转发:地址栏是初次发出请求的地址
         重定向:地址栏不再是初次发出的请求地址,地址栏为最后响应的那个地址

   ②.转发:在最终的Servlet中,request对象和中转的那个request是同一个对象
         重定向:在最终的Servlet中,request对象和中转的那个request不是同一个对象

  ③.转发:只能转发给当前WEB应用的资源
         重定向:可以重定向到任何资源
                response.sendRedirect("http://www.baidu.com");是可以的
                转发就不行

   ④.转发:/  代表的是当前WEB应用的根目录(http://localhost:8080/项目名称/)
         重定向: / 代表的是当前WEB站点的根目录(http://localhost:8080/

注意:这两条跳转语句不能同时出现在一个页面中,否则会报IllegalStateException - if the response was already committed

 

Servlet 的过滤器

  ①、什么是 过滤器?

     JavaWEB 的一个重要组件,可以对发送到 Servlet 的请求进行拦截,并对响应也进行拦截
  ②、如何实现一个过滤器?

第一步:创建一个过滤器类,实现 Filter 接口

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
 
public class HelloFilter implements Filter{
    public HelloFilter() {
        System.out.println("构造器 HelloFilter()...");
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init()...");
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("doFilter()...");
    }
 
    @Override
    public void destroy() {
        System.out.println("destroy()...");
    }
 
}

第二步:在 web.xml 文件中配置过滤器

<!--给创建的过滤器配置关系  -->
  <filter>
    <filter-name>helloFilter</filter-name>
    <filter-class>com.ys.filter.HelloFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>helloFilter</filter-name>
    <url-pattern>/*</url-pattern><!-- 这表示可以拦截任何请求 -->
  </filter-mapping>

  启动服务器:我们发现还没发送请求,过滤器的 构造方法和 init() 方法就已经开始运行了

  服务器启动成功之后,我们输入任意连接,比如

    

  每刷新一次,控制台都会打印 doFilter()...

  

总结:生命周期和 Servlet 的类似。只不过其构造方法和初始化方法是在容器启动时就调用了,而其 doFilter() 方法则是在每次请求的时候调用。故过滤器可以对请求进行拦截过滤。可以用来进行权限设置,对传输数据进行加密等等操作。

 

ServletConfig讲解

1.1、配置Servlet初始化参数

  在Servlet的配置文件web.xml中,可以使用一个或多个<init-param>标签为servlet配置一些初始化参数。

例如:

  <servlet>
      <servlet-name>ServletConfigDemo1</servlet-name>
      <servlet-class>gacl.servlet.study.ServletConfigDemo1</servlet-class>
      <!--配置ServletConfigDemo1的初始化参数 -->
      <init-param>
          <param-name>name</param-name>
          <param-value>gacl</param-value>
      </init-param>
       <init-param>
         <param-name>password</param-name>
         <param-value>123</param-value>
     </init-param>
     <init-param>
         <param-name>charset</param-name>
         <param-value>UTF-8</param-value>
     </init-param>
 </servlet>

1.2、通过ServletConfig获取Servlet的初始化参数

  当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,我们通过ServletConfig对象就可以得到当前servlet的初始化参数信息。

例如:

 import java.io.IOException;
 import java.util.Enumeration;
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 public class ServletConfigDemo1 extends HttpServlet { 
     /**
      * 定义ServletConfig对象来接收配置的初始化参数
      */
     private ServletConfig config;
     
     /**
      * 当servlet配置了初始化参数后,web容器在创建servlet实例对象时,
      * 会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,
      * 将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以
      * 得到当前servlet的初始化参数信息。
      */
     @Override
     public void init(ServletConfig config) throws ServletException {
         this.config = config;
     } 
     public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         //获取在web.xml中配置的初始化参数
         String paramVal = this.config.getInitParameter("name");//获取指定的初始化参数
         response.getWriter().print(paramVal);
         
         response.getWriter().print("<hr/>");
         //获取所有的初始化参数
         Enumeration<String> e = config.getInitParameterNames();
         while(e.hasMoreElements()){
             String name = e.nextElement();
            String value = config.getInitParameter(name);
             response.getWriter().print(name + "=" + value + "<br/>");
         }
     } 
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         this.doGet(request, response);
     } 
 }

运行结果如下:

  

 

ServletContext对象

  WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用。
  ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext方法获得ServletContext对象。
  由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。

多个Servlet通过ServletContext对象实现数据共享

  范例:ServletContextDemo1和ServletContextDemo2通过ServletContext对象实现数据共享

  import java.io.IOException;
  import javax.servlet.ServletContext;
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
 
 public class ServletContextDemo1 extends HttpServlet { 
     public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         String data = "xdp_gacl";
         /**
          * ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,
          * 可以通过ServletConfig.getServletContext方法获得ServletContext对象。
          */
         ServletContext context = this.getServletConfig().getServletContext();//获得ServletContext对象
         context.setAttribute("data", data);  //将data存储到ServletContext对象中
     }
 
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         doGet(request, response);
     }
 }
 import java.io.IOException;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse; 
 public class ServletContextDemo2 extends HttpServlet { 
     public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         ServletContext context = this.getServletContext();
         String data = (String) context.getAttribute("data");//从ServletContext对象中取出数据
         response.getWriter().print("data="+data);
     } 
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         doGet(request, response);
     }
 }

  先运行ServletContextDemo1,将数据data存储到ServletContext对象中,然后运行ServletContextDemo2就可以从ServletContext对象中取出数据了,这样就实现了数据共享,如下图所示:

  

  获取WEB应用的初始化参数

在web.xml文件中使用<context-param>标签配置WEB应用的初始化参数,如下所示:

 <?xml version="1.0" encoding="UTF-8"?>
 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
     http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
     <display-name></display-name>
     <!-- 配置WEB应用的初始化参数 -->
     <context-param>
         <param-name>url</param-name>
         <param-value>jdbc:mysql://localhost:3306/test</param-value>
     </context-param>
 
     <welcome-file-list>
         <welcome-file>index.jsp</welcome-file>
     </welcome-file-list>
 </web-app>

 获取Web应用的初始化参数,代码如下:

 import java.io.IOException;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse; 
 public class ServletContextDemo3 extends HttpServlet { 
     public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         ServletContext context = this.getServletContext();
         //获取整个web站点的初始化参数
         String contextInitParam = context.getInitParameter("url");
        response.getWriter().print(contextInitParam);
     }
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         doGet(request, response);
     } 
 }

运行结果:

  

用servletContext实现请求转发

ServletContextDemo4
 import java.io.IOException;
 import java.io.PrintWriter;
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse; 
 public class ServletContextDemo4 extends HttpServlet { 
     public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         String data = "<h1><font color='red'>abcdefghjkl</font></h1>";
        response.getOutputStream().write(data.getBytes());
         ServletContext context = this.getServletContext();//获取ServletContext对象
         RequestDispatcher rd = context.getRequestDispatcher("/servlet/ServletContextDemo5");//获取请求转发对象(RequestDispatcher)
         rd.forward(request, response);//调用forward方法实现请求转发
     } 
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
     }
 }
ServletContextDemo5
 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 ServletContextDemo5 extends HttpServlet { 
     public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         response.getOutputStream().write("servletDemo5".getBytes());
     } 
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         this.doGet(request, response);
     }
 }

 运行结果:

  

  访问的是ServletContextDemo4,浏览器显示的却是ServletContextDemo5的内容,这就是使用ServletContext实现了请求转发

 

利用ServletContext对象读取资源文件

  项目目录结构如下:

   

代码范例:使用servletContext读取资源文件

 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.text.MessageFormat;
 import java.util.Properties;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
  /**
  * 使用servletContext读取资源文件
  * 
  * @author gacl
  * 
  */
 public class ServletContextDemo6 extends HttpServlet {
 
     public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException { 
         /**
          * response.setContentType("text/html;charset=UTF-8");目的是控制浏览器用UTF-8进行解码;
          * 这样就不会出现中文乱码了
          */
         response.setHeader("content-type","text/html;charset=UTF-8");
         readSrcDirPropCfgFile(response);//读取src目录下的properties配置文件
         response.getWriter().println("<hr/>");
         readWebRootDirPropCfgFile(response);//读取WebRoot目录下的properties配置文件
         response.getWriter().println("<hr/>");
         readPropCfgFile(response);//读取src目录下的db.config包中的db3.properties配置文件
         response.getWriter().println("<hr/>");
         readPropCfgFile2(response);//读取src目录下的gacl.servlet.study包中的db4.properties配置文件
         
     }
 
     /**
      * 读取src目录下的gacl.servlet.study包中的db4.properties配置文件
      * @param response
      * @throws IOException
      */
     private void readPropCfgFile2(HttpServletResponse response)
             throws IOException {
         InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/gacl/servlet/study/db4.properties");
         Properties prop = new Properties();
         prop.load(in);
         String driver = prop.getProperty("driver");
         String url = prop.getProperty("url");
         String username = prop.getProperty("username");
         String password = prop.getProperty("password");
         response.getWriter().println("读取src目录下的gacl.servlet.study包中的db4.properties配置文件:");
         response.getWriter().println(
                 MessageFormat.format(
                         "driver={0},url={1},username={2},password={3}", 
                          driver,url, username, password));
     }
 
     /**
      * 读取src目录下的db.config包中的db3.properties配置文件
      * @param response
      * @throws FileNotFoundException
      * @throws IOException
      */
     private void readPropCfgFile(HttpServletResponse response)
             throws FileNotFoundException, IOException {
         //通过ServletContext获取web资源的绝对路径
         String path = this.getServletContext().getRealPath("/WEB-INF/classes/db/config/db3.properties");
         InputStream in = new FileInputStream(path);
         Properties prop = new Properties();
         prop.load(in);
         String driver = prop.getProperty("driver");
          String url = prop.getProperty("url");
         String username = prop.getProperty("username");
         String password = prop.getProperty("password");
         response.getWriter().println("读取src目录下的db.config包中的db3.properties配置文件:");
         response.getWriter().println(
                  MessageFormat.format(
                          "driver={0},url={1},username={2},password={3}", 
                         driver,url, username, password));
      }
  
      /**
      * 通过ServletContext对象读取WebRoot目录下的properties配置文件
      * @param response
       * @throws IOException
      */
     private void readWebRootDirPropCfgFile(HttpServletResponse response)
             throws IOException {
          /**
          * 通过ServletContext对象读取WebRoot目录下的properties配置文件
          * “/”代表的是项目根目录
          */
         InputStream in = this.getServletContext().getResourceAsStream("/db2.properties");
         Properties prop = new Properties();
         prop.load(in);
         String driver = prop.getProperty("driver");
         String url = prop.getProperty("url");
         String username = prop.getProperty("username");
         String password = prop.getProperty("password");
         response.getWriter().println("读取WebRoot目录下的db2.properties配置文件:");
         response.getWriter().print(
                MessageFormat.format(
                         "driver={0},url={1},username={2},password={3}", 
                         driver,url, username, password));
     }
 
     /**
      * 通过ServletContext对象读取src目录下的properties配置文件
      * @param response
      * @throws IOException
      */
    private void readSrcDirPropCfgFile(HttpServletResponse response) throws IOException {
         /**
          * 通过ServletContext对象读取src目录下的db1.properties配置文件
          */
         InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db1.properties");
         Properties prop = new Properties();
         prop.load(in);
         String driver = prop.getProperty("driver");
         String url = prop.getProperty("url");
         String username = prop.getProperty("username");
         String password = prop.getProperty("password");
         response.getWriter().println("读取src目录下的db1.properties配置文件:");
         response.getWriter().println(
                 MessageFormat.format(
                         "driver={0},url={1},username={2},password={3}", 
                         driver,url, username, password));
     } 
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         this.doGet(request, response);
     }
 
 }

运行结果如下:

  

代码范例:使用类装载器读取资源文件

  3 import java.io.FileOutputStream;
  4 import java.io.IOException;
  5 import java.io.InputStream;
  6 import java.io.OutputStream;
  7 import java.text.MessageFormat;
  8 import java.util.Properties;
  9 
 10 import javax.servlet.ServletException;
 11 import javax.servlet.http.HttpServlet;
 12 import javax.servlet.http.HttpServletRequest;
 13 import javax.servlet.http.HttpServletResponse;
 14 
 15 /**
 16  * 用类装载器读取资源文件
 17  * 通过类装载器读取资源文件的注意事项:不适合装载大文件,否则会导致jvm内存溢出
 18  * @author gacl
 19  *
 20  */
 21 public class ServletContextDemo7 extends HttpServlet {
 22 
 23     public void doGet(HttpServletRequest request, HttpServletResponse response)
 24             throws ServletException, IOException {
 25         /**
 26          * response.setContentType("text/html;charset=UTF-8");目的是控制浏览器用UTF-8进行解码;
 27          * 这样就不会出现中文乱码了
 28          */
 29         response.setHeader("content-type","text/html;charset=UTF-8");
 30         test1(response);
 31         response.getWriter().println("<hr/>");
 32         test2(response);
 33         response.getWriter().println("<hr/>");
 34         //test3();
 35         test4();
 36         
 37     }
 38     
 39     /**
 40      * 读取类路径下的资源文件
 41      * @param response
 42      * @throws IOException
 43      */
 44     private void test1(HttpServletResponse response) throws IOException {
 45         //获取到装载当前类的类装载器
 46         ClassLoader loader = ServletContextDemo7.class.getClassLoader();
 47         //用类装载器读取src目录下的db1.properties配置文件
 48         InputStream in = loader.getResourceAsStream("db1.properties");
 49         Properties prop = new Properties();
 50         prop.load(in);
 51         String driver = prop.getProperty("driver");
 52         String url = prop.getProperty("url");
 53         String username = prop.getProperty("username");
 54         String password = prop.getProperty("password");
 55         response.getWriter().println("用类装载器读取src目录下的db1.properties配置文件:");
 56         response.getWriter().println(
 57                 MessageFormat.format(
 58                         "driver={0},url={1},username={2},password={3}", 
 59                         driver,url, username, password));
 60     }
 61 
 62     /**
 63      * 读取类路径下面、包下面的资源文件
 64      * @param response
 65      * @throws IOException
 66      */
 67     private void test2(HttpServletResponse response) throws IOException {
 68         //获取到装载当前类的类装载器
 69         ClassLoader loader = ServletContextDemo7.class.getClassLoader();
 70         //用类装载器读取src目录下的gacl.servlet.study包中的db4.properties配置文件
 71         InputStream in = loader.getResourceAsStream("gacl/servlet/study/db4.properties");
 72         Properties prop = new Properties();
 73         prop.load(in);
 74         String driver = prop.getProperty("driver");
 75         String url = prop.getProperty("url");
 76         String username = prop.getProperty("username");
 77         String password = prop.getProperty("password");
 78         response.getWriter().println("用类装载器读取src目录下的gacl.servlet.study包中的db4.properties配置文件:");
 79         response.getWriter().println(
 80                 MessageFormat.format(
 81                         "driver={0},url={1},username={2},password={3}", 
 82                         driver,url, username, password));
 83     }
 84     
 85     /**
 86      * 通过类装载器读取资源文件的注意事项:不适合装载大文件,否则会导致jvm内存溢出
 87      */
 88     public void test3() {
 89         /**
 90          * 01.avi是一个150多M的文件,使用类加载器去读取这个大文件时会导致内存溢出:
 91          * java.lang.OutOfMemoryError: Java heap space
 92          */
 93         InputStream in = ServletContextDemo7.class.getClassLoader().getResourceAsStream("01.avi");
 94         System.out.println(in);
 95     }
 96     
 97     /**
 98      * 读取01.avi,并拷贝到e:\根目录下
 99      * 01.avi文件太大,只能用servletContext去读取
100      * @throws IOException
101      */
102     public void test4() throws IOException {
103         // path=G:\Java学习视频\JavaWeb学习视频\JavaWeb\day05视频\01.avi
104         // path=01.avi
105         String path = this.getServletContext().getRealPath("/WEB-INF/classes/01.avi");
106         /**
107          * path.lastIndexOf("\\") + 1是一个非常绝妙的写法
108          */
109         String filename = path.substring(path.lastIndexOf("\\") + 1);//获取文件名
110         InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/01.avi");
111         byte buffer[] = new byte[1024];
112         int len = 0;
113         OutputStream out = new FileOutputStream("e:\\" + filename);
114         while ((len = in.read(buffer)) > 0) {
115             out.write(buffer, 0, len);
116         }
117         out.close();
118         in.close();
119     }
120 
121     public void doPost(HttpServletRequest request, HttpServletResponse response)
122             throws ServletException, IOException {
123 
124         this.doGet(request, response);
125     }
126 
127 }

 运行结果如下:

  

 

在客户端缓存Servlet的输出

  对于不经常变化的数据,在servlet中可以为其设置合理的缓存时间值,以避免浏览器频繁向服务器发送请求,提升服务器的性能。例如:

 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 ServletDemo5 extends HttpServlet {      
public void doGet(HttpServletRequest request, HttpServletResponse response)             throws ServletException, IOException {
         String data = "abcddfwerwesfasfsadf";
         /**
          * 设置数据合理的缓存时间值,以避免浏览器频繁向服务器发送请求,提升服务器的性能
          * 这里是将数据的缓存时间设置为1天
          */
         response.setDateHeader("expires",System.currentTimeMillis() + 24 * 3600 * 1000);
         response.getOutputStream().write(data.getBytes());
     } 
     public void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         this.doGet(request, response);
     }
 }

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JavaWeb开发与代码的编写(二) 的相关文章

  • 华为OD机试 Python 最长公共后缀

    描述 你有一堆字符串 你的任务是找出这堆字符串共同拥有的那段尾字符 如果没有共同的尾 就回答 Zero 具体规定 字符串的数量至少为2 最多为1000 每个字符串的字符都是ASCII码里的 所以范围是 1 126 示范 比如 给你 abc
  • BF,KMP,BM三种字符串匹配算法性能比较

    三种最基本的字符串匹配算法是BF KMP以及BM BF算法是最简单直接的匹配算法 就是逐个比较 一旦匹配不上 就往后移动一位 继续比较 所以比较次数很都 关于KMP和BM的详细介绍可以参考下面的两个link 是讲得比较好的 KMP http
  • Xilinx原语使用方法

    以下链接为百度文库链接 没事的时候可以点击看看 学习学习 xilinx原语的使用方法 Xilinx原语的使用方法2
  • 面试官:你来设计一下抖音直播功能测试用例吧!

    实战案例 Q 微博发动态 设计一下测试点 虽说是发动态 但是测试时不能只是关注发动态这一操作的功能 发完动态之后 我们要确保动态要对外可见 对关注的人可见 单单测试发动态这个操作 实际上意义是不大的 毕竟只测发动态 不能实现测试闭环 所以测
  • [STM32F1]STM32F103滴答定时器定时不准?)

    STM32F103滴答定时器定时不准 前几天调了1块F103ZET6的开发板 用了个系统定时器 感觉不准 最终找到原因是因为选错了时钟源 今天来分享一下也算是给大家以后出现问题做个参考吧 说到时钟源 得说到STM32系统时钟的初始化 前面就
  • 深度学习_用LSTM+Attention与Self-Attention

    笔者在重新尝试用深度学习的各个模型对序列进行预测 就LSTM进行一些使用记录 一 一些优化 和keras一样的权重初始化 有时候我们torch训练的LSTM 没有keras好 可以将权重按keras的方式进行初始化 增加attention

随机推荐

  • Neo4j的下载与安装(完整详细版)

    简要 1 jdk安装 2 neo4j下载 3 neo4j环境配置 4 cmd启动 neo4j bat console 注意jdk的版本要11以上才可以和neo4j匹配 但我安装时用了16 0也报错了 然后重新下载了一个11的 就不报错了 具
  • base格式图片转文件存储

  • Docker 宿主机与容器直接文件移动命令

    1 将容器中的文件复制到宿主机 docker cp nginx test etc nginx usr local nginx 说明 nginx test 容器名称 etc nginx 容器目录存放目录 usr local nginx 宿主机
  • STM32使用FAT文件系统-代码简读

    FAT的一些基础知识 概念先看我这一篇 FAT32文件和目录的组织方式 fat32文件系统架构设计 暴躁的野生猿的博客 CSDN博客 fat文件系统的所有外部接口都在ff h中 物理驱动器 逻辑驱动器 一个物理驱动器就是一个真实的存储设备
  • mysql查询包含逗号的数据,并逗号拆分为多行展现

    在做系统开发的时候 有可能是由于之前的遗留问题 导致在数据入库的时候 将多个数据以逗号分隔的实行 存储在一条数据中 例如 ID VALUE 1 yang zheng song 2 zhao qian sun 3 jiang 现在因为新的需求
  • Anaconda创建并管理Python虚拟环境

    在使用Python进行一些开发或分析工作时 需要安装并使用大神们写的一些程序包 有的程序包只支持32位Python环境 有的支持64位Python环境 有的包支持最新版本 有的可能只支持以前版本 3 6或2 x 而且不同包之间还有版本依赖性
  • 基于深度学习OpenCV与python进行字符识别

    点击上方 小白学视觉 选择加 星标 或 置顶 重磅干货 第一时间送达 当我们在处理图像数据集时 总是会想有没有什么办法以简单的文本格式检索图像中的这些字符呢 今天我们就一起看看有没有什么简单的办法可以实现这一功能 对于字符识别 我们找到了一
  • 性能测试,服务器资源监控工具nmon常用操作

    在性能测试过程中 对服务端的各项资源使用情况进行监控是很重要的一环 这篇博客 介绍下服务端监控工具 nmon的使用方法 一 认识nmon 1 简介 nmon是一种在AIX与各种Linux操作系统上广泛使用的监控与分析工具 它能在系统运行过程
  • Linux驱动开发入门(二)VS code驱动开发配置

    开发环境配置 安装 配置 安装 VS code是一款功能强大的开源编辑器 能够在各个平台安装使用 如Linux Widows Mac 而我们Linux驱动直接运行在Linux上 所以下载安装在Linux平台 示例机系统为Ubuntu 20
  • OPC配置DCOM解决方案

    OPC配置DCOM解决方案 概述 随着对数据重要性的提升以及数据交换问题的重视 大家对免费的OPC server使用有所增加 下面跟大家分享一下解决OPC 配置DCOM困难问题 免去繁琐的工作量 直接导入变量解决OPC配置DCOM问题 适用
  • 使用k-means及k-prototype对混合型数据集进行聚类分析

    1 分析目标 本项目旨在使用聚类算法对110个城市进行分类与排序 以寻找客观真实的城市分层方法 支持业务运营与决策 2 数据集 数据集来源于某互联网公司 特征值标签已做脱敏处理 数据集尺寸为111行 5列 第一行为标题行 其余110行为实例
  • 小程序 调用微信支付

    首先调用小程序wx login登录接口 获取用户code 将code作为参数 传给后端 调用后端接口wechat pay prepay 获取后端 这五个返回值 nonceStr package2 paySign signType timeS
  • 理解Token没那么复杂,别被大佬们忽悠晕了

    区块链很火 Token很繁荣 甚至有些人开始高呼Token经济的时代已经到来了 Token经济真的来了吗 在我们看来 还言之过早 时下现状是 很多专家在谈Token 但大多数内容都是形而上 太飘渺 让人晦涩难懂 只适合业内人士的口味 不适合
  • 财报解读:上半年GMV超50亿元,交个朋友瞄准了更广阔的市场

    于今年7月曲线上市的交个朋友交出了首份 答卷 近日 交个朋友控股披露了2023年上半年财报 营收4 33亿元 同比增长215 5 经调整净利润约9463 5万元 同比暴增1798 全网合计GMV超50亿元 图源 交个朋友财报 交个朋友能取得
  • 如何用C++实现自己的Tensorflow

    原文 How To Write Your Own Tensorflow in C 作者 Ray Zhang 翻译 无阻我飞扬 摘要 TensorFlow是由谷歌基于DistBelief进行研发的第二代人工智能学习系统 其命名来源于本身的运行
  • 施乐s2110进入维修模式_施乐S2110维修手册(无密码)

    实例简介 施乐S2110维修手册 已清除密码版本的 防止密码忘了打不开 Docu Centre s2110 维修手册Ver10 发行 2017年5月 关于因设计变更等原因而修订维修手册 本维修手册用于下列产品 本维修手册的记载内容中出现相关
  • OpenCV Series : Target Box Outline Border

    角点 P1 0 255 000 000 P2 1 000 255 000 P3 2 000 000 255 P4 3 000 000 000 垂直矩形框 rect cv2 minAreaRect cnt targetColor roi co
  • 2.3.8 WPar(Workload Partition)工作负载分区

    最后更新2021 07 14 WPar是一种软分区 本质上与Power CPU无关 只要能运行AIX v6 1 就可以创建WPar 在AIX v6 1版安装的系统之内可以再生成若干AIX实例 所谓AIX实例是指完整的AIX运行环境 包括操作
  • OpenCV中LU分解实现矩阵求逆invert(DECOMP_LU)-定点化

    基于LU分解的矩阵求逆定点化版本 由于需要频繁移位 因此定点比浮点还耗时 浮点版本参考上一篇博客 http blog csdn net xidianzhimeng article details 51284206 话不过多 直接上代码 有不
  • JavaWeb开发与代码的编写(二)

    JavaWeb开发与代码的编写 二 Http协议 HTTP是hypertext transfer protocol 超文本传输协议 的简写 它是TCP IP协议的一个应用层协议 用于定义WEB浏览器与WEB服务器之间交换数据的过程 客户端连