Servlet操作与用法(保姆式教学)

Servlet介绍

什么是servlet

  • Servlet(Servlet Applet的缩写,全称 Java Servlet):适用于Java编写的服务器程序,其主要功能是在于交互式的浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet 是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet,理解为后者。
  • Servlet运行于支持Java的应用服务器中,从原理上将讲,Servlet可以响应任何类型的请求,但是绝大多数情况下Servlet只用于扩展基于HTTP协议的Web服务器
  • Servlet是一种实现动态页面的技术,是一组由Tomcat提供给程序员简单高效的开发一个web app

Servlet的主要工作

  • 允许程序员注册一个类,当Tomcat收到的某个特定的请求,执行这个类中的一些代码
  • 帮助程序员解析HTTP请求,把HTTP请求从一个字符串解析成一个HttpRequest对象
  • 帮助程序员构造HTTP响应,程序员只要给指定的HttpResponse对象填写一些属性字段,Servlet就会自动按照HTTP协议的方式构造出一个HTTP响应字符串,并通过Socket编写返回客户端

Servlet程序创建步骤

创建项目

使用IDEA创建一个Maven项目

创建好的Maven如下

通过上图我们可以了解到一些目录结构,这是Maven项目的标准结构,其中

  • src:用于存放源代码和测试代码的根目录
  • main:用于存放源代码的目录
  • test:用于存放测试代码的目录
  • java:用于存放Java代码的目录
  • resoures:用于存放依赖的资源文件
  • pom.xml:是Maven项目的核心配置文件,关于这个Maven项目的相关属性,都是在这个xml中进行配置的

引入依赖

Maven项目创建完成之后,就i会自动生成一个pom.xml文件,我们需要在这个文件中引入Servlet API依赖的jar包

  • 打开中央仓库,搜索Servlet,点击 Java Servlet API

  • 选择对应的Tomcat版本的Servlet

  • 将中央仓库提供的该版本的xml复制到项目的pom.xml中
  • 修改后的pom.xml如下

 一个项目中可以有多个依赖,每个以来都是一个<dependency>标签,引入的以来都要放在一个<dependencies>标签中,该标签用于放置项目以来的jar包,Maven会自动下载到该依赖带本地

创建目录

Web项目对于目录结构还有自己的要求只有Maven的标准目录还是不够的,需要再创建以下目录进行配置

  • 在main目录下,创建一个webapp目录:webapp目录就是用于部署到Tomcat的一个重要的目录,里面可以存放一些静态资源 
  • 在webapp目录下,创建一个WEB-INF目录
  • 在WEB-INF目录下,创建一个web.xml文件:Tomcat通过找到这个web.xml文件才能正确的处理webapp的动态资源

  • 编写 web.xml:Selvlet中的web.xml中的内容是不能为空的,里面的写法是固定的(这里的写法专属于serlet),用到的时候可以直接拷贝下面的代码
<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app><display-name>Archetype Created Web Application</display-name>
</web-app>

 编写代码

以下编写一个让相应返回一个自定义的字符串代码:

  • 创建一个TestServlet类,并且让他继承于HttpServlet

HttpServlet 这个类来自于pom.xml中引入的Servlet API依赖的jar包

  • 在TestServlet类中重写doGet方法

doGet是HttpServlet类中的方法,此处是在子类中重写了父类的doGet

  • 为了了解doGet方法的作用,我们可以看看它的源码

  • HttpServletRequest:表示HTTP请求,Tomcat按照HTTP请求的格式把字符串格式的的请求转换为一个HttpServletRequest对象,通过这个对象就可以获取请求中的信息
  • HttpServletResponse:表示HTTP响应,通过代码可以把响应的对象构造好,然后Tomcat将响应返回给浏览器

通过doGet的源码我们可以大致的了解,它的作用就是根据收到的请求通过响应返回一个405或者400,那么我们可以重写这个方法,根据收到的请求执行自己的业务逻辑,把结果构造成响应对象

  • 在doGet方法中,通过HttpServletResponse类的getWriter()方法往响应的body中写入文本格式的数据

resp.getWriter()会获取到一个流对象,通过这个流对象就可以写入一些数据,写入的数据会被构造成一个HTTP相应的body部分,Tomcat会把整个响应转换成字符串,通过Socket写回给浏览器

  • 需要给TesttServlet加上一个特定的注解@Webservlet("/test")

上述注解表示Tomcat收到请求中,URL的Servlet Path路径为/test的请求才会调用TestServlet这个类的代码,注解中的字符串表示着URL的Servlet Path

到这里程序的编写已经完成!但是你可能会议或上述代码不是通过main方法作为入口,这是因为main方法包含在Tomcat中,我们写的程序并不能单独执行,而是需要Tomcat才能执行起来(Tomcat的伪代码我们会具体的分析这个问题)

打包程序

在程序编写好之后,就可以使用Maven进行打包

  • 首先修改pom.xml,加入一些必要的配置(打包类型和打包后的包名)

  • packaging标签中用于设置打包类型(如果不进行修改,默认的类型是jar包,jar包是普通Java程序打包的结果,里面包含一i写.class文件;而部署在Tomcat中的压缩包一般为war包,war包里面是Java Web程序,里面除了.class文件以外还包含HTML、CSS、JavaScript、图片等...) 
  • finaName标签中用于设置打包好后的名字(包名很重要,它对应着请求中的URL的Context Path)
  • 执行打包操作(打开Maven窗口,展开Liftcycle,双击package进行打包) 

  • 打包成功之后,可以发现多了一个target目录,该目录下有一个testSevlet.war的压缩包

 部署程序

接下来我们就可以进行程序部署

  • 首先将大号的war包拷贝到Tomcat的webapps目录下

  • 启动Tomcat(bin目录下的startup.bat)

验证程序

此时通过浏览器访问 http://127.0.0.1:8080/testServlet/test 就可以看到程序实现的结果了

 注意:URL的路径分为两部分 Context Path 和 Servlet Path

  • Context Path:这个路径表示一个webapp,来与那与打包的包名
  • Servlet Path: 这个路径表示一个webapp中的一个页面,来源于对应的Servlet类@webServlet注解中的内容

安装Smart Tomcat进行部署

为了简化上述操作流程,其实是有一些更加简单的方式:

  • 对于创建项目、引入依赖、创建目录这三个步骤,其实可以使用项目模板来快速生成的,但是由于项目模板加载速度比较慢,因此这里不是很推荐
  • 对于打包和部署程序这两个步骤,其实可以使用Smart Tomcat插件来快速实现,以下将来介绍使用方式

安装Smart Tomcat

  • 在插件商店中搜索 Smart Tomcat进行安装

配置Smart Tomcat

  • 点击Edit Configurations(新版本),老版本可能是Add Configuration

  • 点击左上角的+号,并选择Smart Tomcat

  • 主要修改这三个参数

  • name:这一栏可以随意填写
  • Tomcat Servlet:表示Tomcat所在的目录
  • Deployment Directory:表示项目发布的目录
  • Context Path:表示项目路径,默认值就是项目名称
  • Servlet Port:表示服务端口
  • Admin Port:表示管理端口
  • VM options:表示JVM参数
  •  配置好的Smart Tomcat之后,Edit Configurations就会显示成name的名字,并且右面多一个三角形运行符号

使用Smart Tomcat

  • 点击三角号就可以进行正常的运行

  • 点击蓝色的链接就会跳转到项目路径,再增加Servlet Path就可以显示出该程序的结果 

Servlet运行原理

 在servlet代码中,我们并没有写main方法,那么对应的doGet方法是如何被调用的呢?响应又是如何返回给服务器的呢?

Servlet的架构

我们自己实现的Servlet实在Tomcat基础上运行的,下图显示了Web应用程序中的位置

当浏览器给服务器发送请求的时候,Tomcat作为HTTP服务器,就可以接收到这个请求,Tomcat的工作就是解析HTTP请求,并把请求交给Servlet的代码来进行进一步的处理,Servlet的代码根据请求计算生成响应对象,Tomcat再把这个响应对象构造成HTTP响应,返回给浏览器,并且Servlet的代码也经常会和数据库进行数据的传递。

Tomcat的伪代码

下面是通过Tomcat的伪代码的形式来描述Tomcat 初始化和处理请求两部分核心逻辑

Tomcat初始化流程

class Tomcat {// 用来存储所有的 Servlet 对象private List<Servlet> instanceList = new ArrayList<>();public void start() {// 根据约定,读取 WEB-INF/web.xml 配置文件// 并解析被 @WebServlet 注解修饰的类// 假定这个数组里就包含了我们解析到的所有被 @WebServlet 注解修饰的类.Class<Servlet>[] allServletClasses = ...;// 这里要做的的是实例化出所有的 Servlet 对象出来;for (Class<Servlet> cls : allServletClasses) {// 这里是利用 java 中的反射特性做的// 实际上还得涉及一个类的加载问题,因为我们的类字节码文件,是按照约定的// 方式全部在 WEB-INF/classes 文件夹下存放的,所以 tomcat 内部是// 实现了一个自定义的类加载器(ClassLoader),用来负责这部分工作。Servlet ins = cls.newInstance();instanceList.add(ins);}// 调用每个 Servlet 对象的 init() 方法,这个方法在对象的生命中只会被调用这一次for (Servlet ins : instanceList) {ins.init();}// 启动一个 HTTP 服务器,并用线程池的方式分别处理每一个 RequestServerSocket serverSocket = new ServerSocket(8080);// 实际上 tomcat 不是用的固定线程池,这里只是为了说明情况ExecuteService pool = Executors.newFixedThreadPool(100);while (true) {Socket socket = ServerSocket.accept();// 每个请求都是用一个线程独立支持,这里体现了 Servlet 是运行在多线程环境下的pool.execute(new Runnable() {doHttpRequest(socket);});}// 调用每个 Servlet 对象的 destroy() 方法,这个方法在对象的生命中只会被调用这一次for (Servlet ins : instanceList) {ins.destroy();}}public static void main(String[] args) {new Tomcat().start();}
}
  • Tomcat的代码内置了main方法,当我们启动Tomcat的时候,就是从Tomcat的main方法开始执行的
  • 被@WebServlet注释修饰的类就会在Tomcat启动的时候就会被获取到,并且集中管理
  • Tomcat通过反射这样的语法机制来创建被@WebServlet注释修饰的类的实例
  • 这些实例被创建完之后,就会调用其中的init方法进行初始化
  • 这些实例被销毁之前,就会调用其中的destory方法进行收尾工作
  • Tomcat内部也是通过Socket API进行网络通信
  • Tomcat为了能够同时处理多个HTTP请求,采取了多线程的方式实现,因此Servlet是运行在多线程环境下的
  • Tomcat处理请求流程
class Tomcat {void doHttpRequest(Socket socket) {// 参照我们之前学习的 HTTP 服务器类似的原理,进行 HTTP 协议的请求解析和响应构建HttpServletRequest req = HttpServletRequest.parse(socket);HttpServletRequest resp = HttpServletRequest.build(socket);// 判断 URL 对应的文件是否可以直接在我们的根路径上找到对应的文件,如果找到,就是静态内容// 直接使用 IO 进行内容输出if (file.exists()) {// 返回静态内容return;}// 走到这里的逻辑都是动态内容了// 找到要处理本次请求的 Servlet 对象Servlet ins = findInstance(req.getURL());// 调用 Servlet 对象的 service 方法// 这里就会最终调用到我们自己写的 HttpServlet 的子类里的方法了try {ins.service(req, resp);} catch (Exception e) {// 返回 500 页面,表示服务器内部错误}}
}
  • Tomcat从socket中读到的HTTP请求是一个字符串,然后Tomcat会按照HTTP协议的格式解析成一个HttpServletRequest对象
  • Tomcat会根据URL中的Path判定这个请求是请求一个静态资源还是动态资源,如果是静态资源,直接找到对应的文件,把文件的容通过Socket返回;如果是动态资源,才会执行Servlet相关逻辑
  • Tomcat会根据URL中的Context Path和Servlet Path确定要调用哪个Servlet实例的service方法
  • 通过service方法,就会进一步调用我们重写的doGet或者doPost方法
  • Servlet的service方法的实现
class Servlet {public void service(HttpServletRequest req, HttpServletResponse resp) {String method = req.getMethod();if (method.equals("GET")) {doGet(req, resp);} else if (method.equals("POST")) {doPost(req, resp);} else if (method.equals("PUT")) {doPut(req, resp);} else if (method.equals("DELETE")) {doDelete(req, resp);}......}
}
  • Servlet的service方法内部会根据当前的请求方式,决定调用其中的某个do...方法
  • 在调用do...方法的时候,就会触发多态机制,从而执行到我们自己写好的子类do...方法

Servlet API 详解

对于Servlet主要介绍三个类,分别是HttpServlet、HttpServletRequest和HttpServletResponse

其中HttpSerletRequest和HttpServletResponse是Servlet规范中规定的两个接口,HttpServlet中并灭有实现这两个接口的成员变量,它们知识HttpServlet的service和do...等方法的参数,这两个接口类的实例化是在Servlet容器中实现的。

HttpServlet

核心方法

                                        方法名称                                                调用时机

                                            init

                         在HttpServlet实例化之后被调用一次
                                         destory                      在HttpServlet实例不再使用的时候调用一次
                                        service                                    收到HTTP请求的时候调用
                                        doGet                    收到GET请求的时候调用(由service方法调用)
                                       doPost                   收到POST请求的时候调用(由Service方法调用)
                   doPut/doDelete/doOptions...                    收到其他请求的时候调用(由Service方法调用)

Servlet的生命周期:Servlet的生命周期就是Servlet对象从创建到销毁的过程,下面就来介绍其生命周期的过程

  • Servlet对象由Tomcat来进行实例化的,并且在实例化完毕之后调用init方法(只调用一次
  • Tomcat对于收到的请求,都会通过对应的Servlet的service方法来进行处理(可以调用多次
  • Tomcat结束之前,会调用Servlet的destory方法来进行回收资源(最多调用一次

注意:init和service能够保证在各自的合适时机被Tomcat调用,但是destory不一定,它是否能够被调用取决于Tomcat是如何结束的

  • 如果是直接杀死进程,那么就来不及调用destory
  • 如果是通过Tomcat的管理端口(默认是8005)进行关闭,就能够调用destory

处理GET请求示例:

  • 直接通过URL发送一个GET方法的请求,来对这个请求进行处理
@WebServlet("/get")
public class TestServlet extends HttpServlet{@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/plain");resp.setCharacterEncoding("UTF-8");resp.getWriter().write("响应了一个Get请求");}
}

处理POST请求示例:

由于通过浏览器URL发送的请求是GET方法的请求,因此我们需要通过其他方式来发送一个POST请求用于处理。发送POST请求的方式有Ajax、form表单或者Socket API 构造,如果单纯用于测试就比较麻烦,所以我们可以用一个软件postman,是一个强大的API调试、Http请求的工具

@WebServlet("/post")
public class TestServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("post");}
}

HttpServletRequest

核心方法 

                                             方法                                                 作用
                        String getProtocol()                        返回协议的名称和版本号

                        String getMethod()

                        返回请求的HTTP方法名称

                         String getRequesURL()

                        返回请求的URL,不带查询字符串
                        String getRequestURI()         返回URL的一部分,不带协名,端口号,查询字符串
                        String getContextPath()

                        返回指示请求URL中的Context Path 部分

                        String getServletPath()                        返回首行中的Servlet Path 部分
                        String getQueryString()                             返回首行后面的查询字符串
                        Enumeration getParameterNames()     返回一个String 对象的枚举,包括在该请求中的参数名称
                        String getParameter(String name)   以字符串形式返回请求参数的值,如果参数不存在则返回null
                String[] getParameterValues(String name)返回一个字符串对象的数组,包括所有给定的请求参数,如果参数不存在返回null
                        Enumeration getHeaderNames()                    返回一个枚举,包括该请求中所有的头名
                        String getHeader(String name)                        以字符串形式返回指定的请求头的值
                        String getCharacterEncoding()                        返回请求正文中使用的字符编码的名称
                        String getContentType()        返回请求正文的 MIME 类型,如果不知道类型则返回 null
                        int getContentLength()以字节位单位返回请求正文的长度,并提供输入流,如果长度未知则返回-1
                        InputStream getInputStream()        用于读取请求的正文内容,返回一个 InputStream 对象
  •  示例一:通过上诉方法返回一个页面是该请求的具体HTTP请求格式
@WebServlet("/showRequest")
public class TestServlet extends HttpServlet{@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//此处返回一个HTMl,在HTML中显示 HttpRequestServlet 类中的核心方法//把这些API 的返回结果 通过 StringBuilder进行拼接resp.setContentType("text/html;charset=utf-8");StringBuilder html = new StringBuilder();html.append(req.getMethod());html.append(" ");html.append(req.getRequestURL());html.append("?");html.append(req.getQueryString());html.append(" ");html.append(req.getProtocol());html.append("</br>");Enumeration<String> headerNames = req.getHeaderNames();while(headerNames.hasMoreElements()){String headName = headerNames.nextElement();String header = req.getHeader(headName);html.append(headName);html.append(": ");html.append(header);html.append("</br>");}html.append("</br>");resp.getWriter().write(html.toString());}
}

  •  实例二:处理HTTP请求的body中的数据格式

如果body的内容格式是x-www-form-urlencoded(这是form表单提交的数据格式,此时body的格式就类似于 query string (是键值对的结构,键值对之间使用&分割,键与值之间使用=进行分割),使用getParameter(获取键值对)进行处理

  • 此处是要获取body的数据,由于GET方法一般没有body,这里使用POST方法演示
  • 约定body的数据格式是:x-www-form-urlencoded
  • 约定body的数据内容是:username=123&password=111
@WebServlet("/postParameter")
public class TestServlet extends HttpServlet{@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html;charset=utf-8");String username = req.getParameter("username");String password = req.getParameter("password");resp.getWriter().write("username=" + username + "</br>" +"passwd=" + password);}
}

如果body的内容格式是json,首先将整个body都读取出来,再借助第三方库方法按照json的格式来进行解析,Java标准库没有内置对于json解析的方法

  • 此处是要获取body的数据,由于GET方法一般没有body,这里使用POST方法进行演示
  • 约定body的数据格式为:json
  • 约定body的数据内容为:

{

username:123,

password:111

}

  • 此处使用jackson第三方库,使用之前需要去Maven的中央仓库将jackson的依赖引入pom.xml中

jacjkson中的核心类是ObjectMapper,通过这个类的readValue(Stringcontent,Class<T> valueType)方法,就可以将json字符串转化为一个类的对象(第一个参数是json字符串,第二个参数是类对象),ObjectMapper会遍历定义的类中的每一个成员的名称,去json字符串中的key中查找,如果找到了就将对应的值返回给该成员

/自定义的将json字符串转化的类
class UserInfo{public String username;public String password;
}
@WebServlet("/jsonParameter")
public class TestServlet extends HttpServlet{@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html;charset=utf-8");//将整个body中的数据读取出来String body = readBody(req);//按照 json、格式进行解析ObjectMapper objectMapper = new ObjectMapper();UserInfo userInfo = objectMapper.readValue(body,UserInfo.class);resp.getWriter().write("username=" + userInfo.username + "</br>" + "passwd=" + userInfo.password);}private String readBody(HttpServletRequest req) throws IOException {int contextLength = req.getContentLength();//准备一个字节数组,来存放body内容byte[] buffer = new byte[contextLength];//获取·到InputStream对象InputStream inputStream = req.getInputStream();inputStream.read(buffer);//将存放在body内容的字节数组转换成字符串return new String(buffer,"utf-8");}
}

HttpServletResponse

核心方法

                           方法                                                       作用
        void setStatus(int sc)

                                           为该响应设置状态码

  void setHeader(String name,String value )设置一个带有给定的名称和值的header,如果name已经存在,则会覆盖该值
  void addHeader(String name,String value)添加一个带有给定的名称和值的header,如果name已经存在,不覆盖旧值,而是添加一个新的键值对
          void setContentType(String type)                                设置被发送到客户端的响应的内容类型
   void setCharacterEncoding(String charset)                        设置被发送到客户端的响应的字符编码,例如utf-8
           void sendRedirect(String location)                                        设置Location字段,实现重定向
                     PrintWriter getWriter()                                   用于往body中写入文本格式的数据
        OutputStream getOutputStream()                                  用于往body中写入二进制格式数据

 示例一:通过代码,构造出不同的响应状态码

@WebServlet("/status")
public class TestServlet extends HttpServlet{@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {int status = 404;resp.setStatus(status);resp.getWriter().write("status"+status);}
}

Fiddler抓包结果: 

  • 实例二:在响应报头设置一个Refresh字段,实现字段刷新程序
@WebServlet("/autoRefresh")
public class TestServlet extends HttpServlet{@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//给响应一个Refresh的header,每隔一秒刷新一次resp.setHeader("Refresh","1");//返回一个当前的时间,用来显示刷新效果resp.getWriter().write("timestamp: "+System.currentTimeMillis());}
}

  • 实例三:实现重定向操作

方法一:在响应报头设置状态码和Location来实现重定向

@WebServlet("/redirect")
public class TestServlet extends HttpServlet{@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//将状态码设置为3XXresp.setStatus(302);//设置一个Locationresp.setHeader("Location","https://blog.csdn.net/loss_rose777?type=blog");}
}

方法二:直接使用sendRedirect()方法来实现重定向

@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.sendRedirect("https://blog.csdn.net/weixin_51367845?spm=1000.2115.3001.5343");}
}

实现服务器版表白墙程序

 基本介绍

下面是表白墙的内容展示

下面是程序代码:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>*{margin: 0px;padding: 0px;}.container{width: 400px;margin: 0 auto;}h1{text-align: center;padding-bottom: 20px;}p{text-align: center;color: gray;line-height:40px;}.button{text-align: center;}.edit{margin-bottom: 20px;width: 200px;height: 30px;}.n1{margin-right: 60px;}.n2{margin-right: 41px;}.n3{margin-right: 60px;}.button{height: 40px;width: 300px;background-color: orange;color: white;border: none;}span{font-size: 20px;}</style>
</head><body><div class="container"><h1>表白墙</h1><p>输入后点击提交,会将信息显示在表格中</p><div><span class="n1">谁:</span><input type="text" class="edit"></div><div><span class="n2">对谁:</span><input type="text" class="edit"></div><div><span class="n3">说:</span><input type="text" class="edit"></div><div><input type="button" value="提交" class="button" onclick="submit()"></div></div>
</body><script>let edits = document.querySelectorAll('.edit')function submit(){let n1 = edits[0].valuelet n2 = edits[1].valuelet n3 = edits[2].valuelet div = document.createElement('div')div.className = "div1"div.innerHTML = n1+"对"+n2+"说"+n3let container = document.querySelector('.container')container.appendChild(div)alert(n1+"对"+n2+"说"+n3)}</script>
</html>

准备操作

1、创建一个Servlet项目

2、将之前写好的纯前端的表白前代码拷贝到webapp目录下

3、约定好前后端交互接口,该程序只需要约定两个接口

  • 从服务器获取全部留言
  • 约定请求:方法时GET,请求路径是/message
  • 约定响应:版本号为HTTP/1.1,状态码是200 OK,采用的是JSON格式
  • JSON具体请求格式:
[{from:"",to:"",message:""}
]
  • 通过客户端给服务器新增一个留言
  • 约定请求:方法为POST,请求路径为/message
  • 约定响应:版本号为HTTP/1.1,状态码为 200 OK,提交成功响应页面显示“提交成功”

4、创建一个MessageServlet类。@WebServlet注解为 /message,对应着约定的请求路径,通过上方的约定完成服务器代码

5、更改前端代码

代码实现

后端代码实现:

import com.fasterxml.jackson.databind.ObjectMapper;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;
import java.util.ArrayList;
import java.util.List;//这个类表示一条消息的详细信息
class Message{public String from;public String to;public String message;
}
@WebServlet("/message")
public class messageServlet extends HttpServlet {//通过这个数组来表示所有消息private List<Message> messages = new ArrayList<>();//获取服务器所有消息操作@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json;charset=utf-8");//获取到消息列表//此处需要将message数组中的数据转换成json格式返回给浏览器ObjectMapper objectMapper = new ObjectMapper();//通过ObjectMapper 的 writeValueAsString() 方法就可以将一个对象转换成一个json字符串String jsonString = objectMapper.writeValueAsString(messages);resp.getWriter().write(jsonString);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html;charset=utf-8");ObjectMapper objectMapper = new ObjectMapper();Message message = objectMapper.readValue(req.getInputStream(),Message.class);messages.add(message);resp.getWriter().write("提交成功!");}
}

前端代码实现:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>表白墙</title><!-- 引入Jquery --><script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script><style>* {margin: 0px;padding: 0px;}.container {width: 400px;margin: 0 auto;}h1 {text-align: center;padding-bottom: 20px;}p {text-align: center;color: gray;line-height: 40px;}.button {text-align: center;}.edit {margin-bottom: 20px;width: 200px;height: 30px;}.n1 {margin-right: 60px;}.n2 {margin-right: 41px;}.n3 {margin-right: 60px;}.button {height: 40px;width: 300px;background-color: orange;color: white;border: none;}span {font-size: 20px;}</style>
</head><body><div class="container"><h1>表白墙</h1><p>输入后点击提交,会将信息显示在表格中</p><div><span class="n1">谁:</span><input type="text" class="edit"></div><div><span class="n2">对谁:</span><input type="text" class="edit"></div><div><span class="n3">说:</span><input type="text" class="edit"></div><div><input type="button" value="提交" class="button"></div></div>
</body>
<script>let edits = document.querySelectorAll('.edit')let Button = document.querySelector('.button');Button.onclick = function () {//1、获取到输入框中的三个请求let n1 = edits[0].valuelet n2 = edits[1].valuelet n3 = edits[2].valueif (n1 == '' || n2 == '' || n3 == '') {return;}//2、构造新的divlet div = document.createElement('div')div.className = "div1"div.innerHTML = n1 + "对" + n2 + "说" + n3let container = document.querySelector('.container')container.appendChild(div)//3、清空之前输入框的内容for (let input of edits) {input.value = '';}//4、通过ajax构造post请求,把这个请求提交给服务器//构造一个js对象let body = {from: n1,to: n2,message: n3};$.ajax({type: 'post',contentType: "application/json;charset=utf-8",//设置服务器地址的所在位置 使用相对路径表示url: 'message',//完成json对象和json格式字符串转换data: JSON.stringify(body),success: function (body) {//这是响应成功之后,要调用的回调函数console.log("消息发送给服务器成功!")}});}//在页面加载的时候,希望能够从服务器获取到所有的消息,并显示到网页中$.ajax({type: 'get',url: 'message',success: function (body) {//body是收到响应的正文 由于在响应中我们设置的是 application/json 所以此时收到的body会被jquery自动把它//从字符串转换为js对象数组,此处就不需要手动进行JSON.parsefor (let message of body) {//构造新的divlet div = document.createElement('div')div.className = "div1"div.innerHTML = message.from + "对" + message.to + "说" + message.messagelet container = document.querySelector('.container')container.appendChild(div)}}});
</script></html>

 

 持久化存储

上述代码,我们实现了,前端与后端的交互,以及服务器的部署,但是我么可以保证在页面刷新的时候不会将数据清楚,但是当我们服务器关闭的时候,数据还是会消失,为了解决这个问题,就需要让数据能够持久化存储

持久化存储:是把数据保存到可以永久保存的存储设备中(如硬盘),是一种将程序数据在持久状态和瞬时状态间转换的机制

持久化存储机制包括:JDBC和文件IO 

1、建立数据库

drop database if exits messagewall;
create database messagewall;use messagewall;drop table if exits message;
create table message (`from` varchar(50),`to` varchar(50),`message` varchar(1024)
);

2、在pom.xml中引入mysql的jar包

 3、将代码与数据库进行连接(主要是将list数组删除,添加一个save和load方法)

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysql.cj.jdbc.MysqlDataSource;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 javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;//这个类表示一条消息的详细信息
class Message{public String from;public String to;public String message;@Overridepublic String toString() {return "Message{" +"from='" + from + '\'' +", to='" + to + '\'' +", message='" + message + '\'' +'}';}
}
@WebServlet("/message")
public class messageServlet extends HttpServlet {//通过这个数组来表示所有消息//private List<Message> messages = new ArrayList<>();//获取服务器所有消息操作@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json;charset=utf-8");//获取到消息列表//此处需要将message数组中的数据转换成json格式返回给浏览器ObjectMapper objectMapper = new ObjectMapper();List<Message> messages = null;try {messages = load();} catch (SQLException e) {throw new RuntimeException(e);}//通过ObjectMapper 的 writeValueAsString() 方法就可以将一个对象转换成一个json字符串String jsonString = objectMapper.writeValueAsString(messages);resp.getWriter().write(jsonString);}//这个方法用来往数据库中存一条数据private List<Message> load() throws SQLException {List<Message> messages = new ArrayList<>();DataSource dataSource = new MysqlDataSource();((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/message?characterEncoding=utf-8&useSSL=false");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("");Connection connection = dataSource.getConnection();String sql = "select* from message";PreparedStatement statement = connection.prepareStatement(sql);ResultSet resultSet = statement.executeQuery();while(resultSet.next()){Message message = new Message();message.from = resultSet.getString("from");message.to = resultSet.getString("to");message.message = resultSet.getString("message");messages.add(message);}resultSet.close();statement.close();connection.close();return messages;}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html;charset=utf-8");ObjectMapper objectMapper = new ObjectMapper();Message message = objectMapper.readValue(req.getInputStream(),Message.class);try {save(message);} catch (SQLException e) {throw new RuntimeException(e);}System.out.println("消息提交成功! message=" + message);resp.getWriter().write("提交成功!");}private void save(Message message) throws SQLException {DataSource dataSource = new MysqlDataSource();((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/message?characterEncoding=utf-8&useSSL=false");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("");Connection connection = dataSource.getConnection();String sql = "insert into message value(?,?,?)";PreparedStatement statement = connection.prepareStatement(sql);statement.setString(1,message.from);statement.setString(2, message.to);statement.setString(3, message.message);statement.executeUpdate();statement.close();connection.close();}
}

Cookie和Session

Cookie

Cookie介绍我们已经在上一篇Http中说的很详细了Cookie介绍​​​​​​

 在了解Cookie之后,我们发现Cookie是不能够用于存储和用户直接相关的信息的,一是Cookie的存储空间有限,二是发送请求时占用的带宽很多,三是不安全。即这些数据不适合保存到客户端,保险粗在服务器是最合适的,通过会话(Session)的方式就能够保存这些数据。

Session会话机制介绍 

基本介绍:

在计算机中,尤其是网络应用中,Session称为“会话控制”。Session对象存储特定用户会话所需的属性及配置信息,当用户在应用程序的Web页面跳转时,存储在Session对象中变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来程序的Web页时,如果该用户还没有会话,则Web服务器将自动会创建一个Session对象,当会话过期或放弃后,服务器会终止该会话。Session对象最常见的一个用法就是存储用户首选项,例如,如果用户知名不喜欢查看图形的时候,就可以将该信息储存在Session对象中,注意会话状态仅在支持Cookie的浏览器中保存。

会话本质:

会话本质就是一个哈希表,其中存储一些键值对结构,key叫做sessionnId,是一个不随机的,不重复的,唯一的字符串,value就是要保存的身份信息,通过HttpSession对象来保存,key和value都是Servlet自动创建的。

每个用户登录都会产生一个会话,服务器会以哈希表的方式将这些会话管理起来

一个会话的详细数据通过一个HttpSession对象来存储,并且HttpSession对象中存储的数据也是键值对结构,key和value都是程序员自定义的

 接着Cookie不适合用于存储用户相关的直接信息来讲,由于客户端不适合存储这些数据,服务器这边可以通过Session会话方式来进行保存。下面将会以用户的登录流程来介绍Session会话机制

  • 当用户登录成功之后,服务器会在Session中生成一个新的记录,并把sessionId返回给客户端(例如Http响应中可以通过Set-Cookie字段返回,其中Cookie的key为"JSESSION",value为服务器生成的sessionId的具体的值)
  • 然后客户端只需要保存这个sessionId,当后续再给服务器发送请求的时,请求中会带上sessionIdd(例如HTTP请求中会带上Cookie字段用于传递Session)
  • 服务器收到请求之后,就会根据请求中的sessionId在Session中查询对应用户的身份信息,在后续操作

Session会话机制的好处

  • 是客户端很轻量,不在保存太多的数据
  • 客户端和服务器之间传输的数据量少,节省带宽
  • 数据都在服务器存储,即使客户端出现问题,数据也不会丢失

注意:Servlet的session默认是保存在服务器内存中的,如果重启服务器Session数据会消失 

Cookie和Session的区别

  • Cookie是客户端存储数据的一种机制,可以存储身份信息,也可以存储其他信息,是键值对结构的
  • Session是服务器存储数据的一种机制,主要适用于存储身份相关的信息,是键值对结构的
  • Cookie和Session经常配合使用,但是不是必须的
    • Cookie也完全可以保存一些数据在客户端,这些数据不一定是用户的身份信息,不一定是sessionId
    • Session中的sessionId也不需要非得通过Cookie和Set-Cookie来传递

Servlet中Cookie和Session的核心方法

HttpServletRequest类中相关的方法

                                             方法                                                作用
                        HttpSession getSession(参数)在服务器中获取会话,参数如果是true,当不存在会话时,会创建一个会话(包括生成一个新的sessionId和HttpSession对象),并通过Set-Cookie将sessionId返回给客户端;参数如果是false,当不存在会话时会返回null。如果存在sessionIOd且合法,就根据这个sessionId找到对应的HttpSession对象并返回
                                Cookie[] getCookies()返回一个数组,包含客户端发送请求的所有Cookie对象,会自动把Cookie中的格式解析成键值对

HttpServletresponse类中的方法 

                                            方法                                                作用
                     void addCookie(Cookie cookie)                                把指定的cookie添加到响应中

 HttpSession类中相关方法

  • HttpSession是Java平台对session机制的实现规范,因为它仅仅是个接口,具体实现为每个Web应用服务器的提供商
  • 服务器会为每一个用户创建一个和独立的HttpSession,表示为一个会话,并且为一个会话,并且一个HttpSession对象中包含多个键值对,可以往HttpSession中存储需要的数据
                                          方法                                                  作用
                    Object getAttribute(String name)该方法返回在Session会话中会有指定名称的对象,如果没有指定名称的对象,返回null
          void setAttribute(String name,Object value)        该方法使用指定名称绑定一个对象到该Session会话中
                                boolean isNew()                        判断当前会话是否是先创建的

Cookie类中的相关方法

  • 这个类描述了一个Cookie,通过Cookie类创建的对象,每一个对象就是一个键值对
  • HTTP的Cookie字段中实际上存储的是多个键值对,每一个键值对在Servlet中都对应一个Cookie对象
                                          方法                                               作用
                                String getName()该方法返回的Cookie名称(这个值是Set-Cookie字段设置给浏览器的,创建之后不会改变)
                                String getValue()

                        该方法获取与Cookie关联的值

                     void setValue(String newValue)

                        该方法设置与Cookie关联的值

实现用户登录功能

接下来将使用上述的Session和Cookie相关的方法来实现一个用户登录功能,并且可以记录访问页面次数

登录功能实现思路:

  1. 读取用户提交的用户和代码
  2. 对用密码进行校验
  3. 判断是否登录成功
  4. 创建会话,保存自定义信息
  5. 重定向到指定页面

登录功能实现流程:

  • 实现一个登录页面
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><form action="login" method="post"><input type="text" name="username"><br><input type="password" name="password"><br><input type="submit" value="登录"></form>
</body>
</html>

  • 实现一个Servlet来处理上面的登录请求
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 javax.servlet.http.HttpSession;
import java.io.IOException;@WebServlet("/login")
public class loginServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html;charset=utf-8");//1、从请求中获取到用户名和密码String username = req.getParameter("username");String password = req.getParameter("password");//2、对账号密码进行校验if(username==null||username.isEmpty()||password==null||password.isEmpty()){resp.getWriter().write("账号或密码不可以为空!");return;}//3、判断是否登录成功(假设用户名为 admin,密码为 1234。不过账号密码应该用数据库存储,这里只是用来测试)if(!username.equals("admin") || !password.equals("1234")){resp.getWriter().write("<h3>账号或密码错误!</h3>");return;}//4、登录成功,创建一个会话,用来记录当前用户信息HttpSession session = req.getSession(true);//通过这个操作,程序员就可以给session增加自定义信息,如访问次数session.setAttribute("visitCount",0);//5、把登录成功的结果返回给客户端(这里的反馈不是简单的提示登陆成功,而是直接跳转到指定页面)resp.sendRedirect("index");}
}
  • 通过实现一个Servlet来表示登录成功后重定向页面
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 javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/index")
public class IndexServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html;charset=utf8");//只有登陆成功参数才是true,这里是拿参数,所以填falseHttpSession session = req.getSession(false);//判断当前用户是否登录if(session == null){//返回登录界面resp.sendRedirect("login.html");return;}//表示用户登录过,获取会话中的访问次数Integer visitCount = (Integer) session.getAttribute("visitCount");visitCount+=1;session.setAttribute("visitCount",visitCount);resp.getWriter().write("<h3>visitCount = " + visitCount + "</h3>");}
}

实现效果如下:

上传文件操作

上传文件是日常开发中的一类常见的需求,在Servlet中也进行支持

Servlet中上传文件的核心操作

HttpServletrequest类中的核心方法

方法作用
Part getPart(String name)获取请求中给定name的文件
Collection<Part> getParts()获取所有的文件

Part类中的相关方法

方法作用
String getSubmittedFileName()获取提交的文件名
String getContentType()获取提交文件类型
long getSize()

获取文件大小,单位为字节

void write(String path)把提交的文件数据写入磁盘文件

上传文件操作实现

1、写一个前端页面,用于上传文件

  • 上传一个文件一啊不能使用post请求的表单实现
  • 通过from表单构造上传文件,要加上以恶搞enctype字段,它表示的是body中的数据格式,它的默认值为:x-www-form-urlencoded,这里要修改成:multipart/form-data,他是上传文件或者图片的数据格式
  • 第一个input中的type的值为file,它表示第一个输入框为文件选择框,naem的值与后端中通过getPart获取指定文件的操作有关
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><form action="upload" method="post" enctype="multipart/form-data"><input type="file" name="Myfile" id=""><input type="submit" value="上传"></form>
</body>
</html>

2、写一个Servlet用于处理上传的文件

  • 上传文件操作还需要给Servlet嘉善一个@MultipartConfig注解,否则服务器代码无法受用getPart()方法
  • getPart()方法中的参数和form表单input="file"标签的name属性对应
  • 客户端一次可以提交多个文件,getPart()方法根据name属性来获取不同的文件
  • 写入磁盘文件操作的类路径之间可以使用两个反斜杠\\,也可以使用一个正斜杠/
  • 写入磁盘文件操作的路径最后为保存后的文件名,包括文件后缀
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;@MultipartConfig
@WebServlet("/upload")
public class fileServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html;charset=utf-8");//通过getPart方法获取到前端传来的文件Part part = req.getPart("MyFile");//获取文件名String fileName = part.getSubmittedFileName();System.out.println("文件名:"+fileName);//获取提交的文件类型String fileType = part.getContentType();System.out.println("文件类型:"+fileType);//获取文件的大小long fileSize = part.getSize();System.out.println("文件大小为:"+fileSize);part.write("D:\\test\\text.mp4");resp.getWriter().write("上传成功!");}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/88760.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Xmake v2.8.3 发布,改进 Wasm 并支持 Xmake 源码调试

Xmake 是一个基于 Lua 的轻量级跨平台构建工具。 它非常的轻量&#xff0c;没有任何依赖&#xff0c;因为它内置了 Lua 运行时。 它使用 xmake.lua 维护项目构建&#xff0c;相比 makefile/CMakeLists.txt&#xff0c;配置语法更加简洁直观&#xff0c;对新手非常友好&#x…

ARM day1

1.复习今日内容 2.搭建汇编环境 下发资料-》工具软件 -》汇编环境搭建 3.安装Ubuntu下的交叉编译工具链 思维导图&#xff1a;

latexocr安装过程中遇到的问题解决办法

环境要求&#xff1a;需要Python版本3.7&#xff0c;并安装相应依赖文件 具体的详细安装步骤可见我上次写的博文&#xff1a;Mathpix替代者|科研人必备公式识别插件|latexocr安装教程 ‘latexocr‘ 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件的相关解决办…

十五、异常(2)

本章概要 自定义异常 异常与记录日志 异常声明 自定义异常 不必拘泥于 Java 已有的异常类型。Java异常体系不可能预见你将报告的所有错误&#xff0c;所以你可以创建自己的异常类&#xff0c;来表示你的程序中可能遇到的问题。 要自己定义异常类&#xff0c;必须从已有的异…

什么是 Redis?

Redis 是一种基于内存的数据库&#xff0c;对数据的读写操作都是在内存中完成的&#xff0c;因此读写速度非常快&#xff0c;常用于缓存&#xff0c;消息队列&#xff0c;分布式锁等场景。 Redis 提供了多种数据类型来支持不同的业务场景&#xff0c;比如 String(字符串)、Has…

C语言环境搭建(Win)

一、C语言简介 1、 C语言简介 C语言是一门通用的、面向过程式的编译型语言&#xff0c;它的运行速度极快&#xff0c;仅次于汇编语言。 C语言是计算机产业的核心程序设计语言&#xff0c;操作系统、硬件驱动、关键组件、数据库等都离不开C语言&#xff0c;广泛应用于底层开发。…

CompletableFuture-链式语法和join方法介绍

2.4 案例精讲-从电商网站的比价需求展开 2.4.1 函数式编程已成为主流 Lambda表达式Stream流式调用Chain链式调用Java8函数式编程 函数式接口&#xff1a; 小结&#xff1a; 函数式接口&#xff1a; Java8新特性_四大内置核心函数式接口_java8 内置核心接口_ZHOU_VIP的博客-…

Nginx之memcached_module模块解读

目录 基本介绍 安装添加模块 模块配置指令 基本介绍 nginx的memcached_module模块可以直接从memcached服务器中读取内容后输出&#xff0c;后续的请求不再经过应用程序处理&#xff0c;如php-fpm、django&#xff0c;大大的提升动态页面的速度。nginx只负责从memcach…

学习路之PHP--lumen安装配置

一、下载lumen源码 composer create-project --prefer-dist laravel/lumen blog 安装lumen-generator composer require flipbox/lumen-generator 二、配置 bootstrap\app.php 97行 $app->register(Flipbox\LumenGenerator\LumenGeneratorServiceProvider::class);三、生成…

【IDEA】IDEA 单行注释开头添加空格

操作 打开 IDEA 的 Settings 对话框&#xff08;快捷键为CtrlAltS&#xff09;&#xff1b;在左侧面板中选择Editor -> Code Style -> Java&#xff1b;在右侧面板中选择Code Generation选项卡&#xff1b;将Line comment at first column选项设置为false使注释加在行开…

ICCV 2023|Occ2Net,一种基于3D 占据估计的有效且稳健的带有遮挡区域的图像匹配方法...

本文为大家介绍一篇入选ICCV 2023的论文&#xff0c;《Occ2Net: Robust Image Matching Based on 3D Occupancy Estimation for Occluded Regions》&#xff0c; 一种基于3D 占据估计的有效且稳健的带有遮挡区域的图像匹配方法。 论文链接&#xff1a;https://arxiv.org/abs/23…

学生宿舍管理系统(前端java+后端Vue)实现-含前端与后端程序

界面介绍 登录 ###宿舍管理 ###菜单管理 ###角色管理 ###班级管理

TensorFlow入门(五、指定GPU运算)

一般情况下,下载的TensorFlow版本如果是GPU版本,在运行过程中TensorFlow能自动检测。如果检测到GPU,TensorFlow会默认利用找到的第一个GPU来执行操作。如果机器上有超过一个可用的GPU,除第一个之外的其他GPU默认是不参与计算的。如果想让TensorFlow使用这些GPU执行操作,需要将运…

源码:TMS FlexCel Studio for .NET 7.19

TMS FlexCel Studio for .NET 是100% 托管代码 Excel 文件操作引擎以及 Excel 和 PDF 报告生成&#xff0c;适用于 .NET、Xamarin.iOS、Xamarin.Android、Xamarin.Mac、Windows Phone 和 Windows Store 功能概述 使用 FlexCel Studio for .NET 创建可动态快速读写 Excel 文件的…

多线程(虚拟地址空间)

代码展示线程 既然我们提到了&#xff0c;线程隶属于进程&#xff0c;是进程的一个执行分支 真的是这样吗&#xff1f; 我们还需要用代码来验证 初步思路是创建三个线程&#xff0c;其中main函数里面的为主线程 不断循环&#xff0c;并且打印相应的pid 假如它们属于不同的进程…

Java 大厂八股文面试专题-JVM相关面试题 类加载器

Java 大厂八股文面试专题-设计模式 工厂方法模式、策略模式、责任链模式-CSDN博客 JVM相关面试题 1 JVM组成 1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f; 难易程度&#xff1a;☆☆☆ 出现频率&#xff1a;☆☆☆☆ JVM是什么 Java Virtual Machine Java程序…

JDK、JRE 和 JVM 的区别和联系

三者关系 就这三者的关系而言&#xff0c;jvm是jre的子集&#xff0c;jre是jdk的子集&#xff0c;具体关系如下图&#xff1a; Java的执行流程 对于一个Java程序&#xff0c;其执行流程大致如下&#xff1a; 开发人员使用JDK编写和编译Java源代码&#xff0c;生成Java字节码文…

spring源码解析——IOC-开启 bean 的加载

概述 前面我们已经分析了spring对于xml配置文件的解析&#xff0c;将分析的信息组装成 BeanDefinition&#xff0c;并将其保存注册到相应的 BeanDefinitionRegistry 中。至此&#xff0c;Spring IOC 的初始化工作完成。接下来我们将对bean的加载进行探索。 BeanFactory 当我…

C语言 数据类型

变量声明 格式&#xff08;变量类型变量名称&#xff09; 变量类型&#xff1a;整数类型&#xff08;int&#xff09;&#xff0c;浮点数类型&#xff08;float&#xff09; float类型可以存储带小数的数字。 用printf()打印变量&#xff0c;使用%d来处理整数值&#xff0c…

学习记忆——宫殿篇——记忆宫殿——记忆桩——风景

河边街道窗框空间房顶楼房水塔山顶塔桥舟桥楼观景台 车顶架碧水池&#xff08;喷泉&#xff09;塔腰楼顶房檐碑石狮箱车叉牌摩托灯