板块一 Servlet编程:第一节 Servlet的实现与生命周期 来自【汤米尼克的JAVAEE全套教程专栏】

板块一 Servlet编程:第一节 Servlet的实现与生命周期

  • 一、Servlet相关概念
    • Serlvet的本质
  • 二、中Web项目中实现Servlet规范
    • (1)在普通的Java类中继承HttpServlet类
    • (2)重写service方法
      • 编辑项目对外访问路径
  • 二、Servlet工作流程
  • 三、其它实现Servlet规范的方式
    • (1)继承自GenericServlet 类
    • (2)实现Servlet接口
    • (3)在HttpServlet中直接重写doGet()和doPost()方法
  • 四、Servlet生命周期

在上一节的内容中,我们已经系统的学习了HTTP的相关概念、知道了GET和POST请求在服务器上的运行原理、请求响应在服务器中究竟是怎样运行的,从这一节开始我们将系统的学习实现Servlet的完整过程

一、Servlet相关概念

Serlvet的本质

  • 当编写Java程序想要在网上实现聊天、发帖、这样一些的交互功能,普通Java技术是非常复杂的,试想要从底层搭建出一整个服务层的代码有多复杂?并且每个人搭建的底层还不一样,为了解决这个问题,sun公司就提供了Serlvet这种技术供我们使用。Servlet是Server与Applet的缩写,是服务端小程序的意思,本质上就是一个遵循Servlet开发的Java类,由服务器调用,在服务器端运行,它的创建、使用、销毁都由Servlet容器进行管理,其常见容器有很多,如Tomcat,Jetty,WebLogic Server,WebSphere,JBoss等等(在Tomcat的集成这一节中我们已经详尽的学习了Tomcat),更奇怪的是Servlet没有main()方法,本质上我们可以想象Servlet通过某种注入回调方法与Servlet容器取得联系,从而代替我们在Servlet中书写main()函数
    在这里插入图片描述
  • Servlet与HTTP紧密联系,可以处理与HTTP协议相关的所有内容,这是Servlet应用广泛的原因之一
  • 实际上在运行JSP时,服务器底层将JSP编译成一个Java类,这个类就是Servlet,因此可以说JSP就是Servlet(详见JSP追根溯源小节)
  • Servlet在 JAVA WEB项目中的位置,它就是我们常说的后端

在这里插入图片描述

  • 编程学习越往后越是如此,我们能做的其实很有限。大部分工作框架都已经帮我们做了。只
    要我们实现xx接口,它会帮我们创建实例,然后搬运(接口注入)到它合适的位置,然后一套既定
    的流程下来执行到创建我们需要的实例

二、中Web项目中实现Servlet规范

我们已经在板块零的第二节中创建了Java Web项目;又在板块零的第三节中成功把Tomcat集成到IDEA中,现在我们就在这个Web项目中实现Servlet规范

(1)在普通的Java类中继承HttpServlet类

在www.caijiyuan包中创建一个普通的Java类
helloServlet.java
在这里插入图片描述
此类继承HttpServlet类,还记得继承怎么写吗:extends
在这里插入图片描述

(2)重写service方法

在上一节中我们详细学习了请求响应在服务器中究竟是怎样运行的,而在Servlet中请求响应就是通过HttpServlet类中的service方法实现的,service方法有两个形参(Request,Rrsponse)分别对应请求、响应
在HttpServlet类中,IDEA重写方法的快捷键是Ctrl+O在这里插入图片描述
但我们发现有两个service,它们的区别是什么?
先来看第一个,HttpServlet的service()方法

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取http request的method参数,其实就是html的form标签  //中method属性对应的字符串 String method = req.getMethod();long errMsg;//判断请求方式if(method.equals("GET")) {//获取最后被修改时间 errMsg = this.getLastModified(req);if(errMsg == -1L) {/**如果servlet不支持http request header的if-modified-since属性 * 则继续处理 **/  this.doGet(req, resp);} else {//如果支持这个属性 long ifModifiedSince;try {ifModifiedSince = req.getDateHeader("If-Modified-Since");} catch (IllegalArgumentException var9) {ifModifiedSince = -1L;}/** * 如果客户端的文件最后修改时间和服务器端的文件最后修改时间一致则返回304不需要修改状态 * 这样服务器就不返回html,浏览器读取本地缓存文件,否则重新获取服务器端的对应html文件 **/  if(ifModifiedSince < errMsg / 1000L * 1000L) {this.maybeSetLastModified(resp, errMsg);this.doGet(req, resp);} else {resp.setStatus(304);}}} else if(method.equals("HEAD")) {errMsg = this.getLastModified(req);this.maybeSetLastModified(resp, errMsg);this.doHead(req, resp);} else if(method.equals("POST")) {this.doPost(req, resp);} else if(method.equals("PUT")) {this.doPut(req, resp);} else if(method.equals("DELETE")) {this.doDelete(req, resp);} else if(method.equals("OPTIONS")) {this.doOptions(req, resp);} else if(method.equals("TRACE")) {this.doTrace(req, resp);} else {//如果请求不是以上的所有请求方式,该方法就会响应501错误,也就是不支持这种请求String errMsg1 = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[]{method};errMsg1 = MessageFormat.format(errMsg1, errArgs);resp.sendError(501, errMsg1);}}

这是第二个ServletRequest

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {HttpServletRequest request;HttpServletResponse response;try {request = (HttpServletRequest)req;response = (HttpServletResponse)res;} catch (ClassCastException var6) {throw new ServletException("non-HTTP request or response");}this.service(request, response);
}

原来只有第二个 ServletRequest service方法是由Tomcat自动调用,它将接收的客户端请求转交给HttpServlet中的第一个HttpServletRequest protected service方法,此保护类型的service方法再把将请求分发给doPost()doGet()方法进行下一步处理
因此这里就Request/Response俩个形参而言,重写调用第一个或第二个service方法的效果应该是一样的,此处我们直接重写第一个,也就是 HttpServletRequest protected service
在这里插入图片描述

编辑项目对外访问路径

我们还应将项目对外访问路径(就是在服务器中此项目的站点名)更改为自己想要的样子
在这里插入图片描述此处我更改为与包名一致/www.caijiyuan
在这里插入图片描述

注意:我们还应在类前设置注解@WebServlet("/helloServlet"),告诉服务器当前资源在站点下的真实路径

helloServlet.java中写入测试内容

package www.caijiyuan;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@WebServlet("/helloServlet")
public class helloServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        super.service(req, resp);//打印内容在控制台System.out.println("Hello Servlet with terminal");//通过流输出数据到浏览器resp.getWriter().write( "Hello Servlet with brower");}
}

启动服务器在浏览器中访问得

在这里插入图片描述
在控制台得
在这里插入图片描述

二、Servlet工作流程

那么HttpServletRequest request请求是在服务器中是怎样接受到的呢?
即是上一节中详解过的请求头的功劳

在这里插入图片描述
当请求进入服务器时

  1. 服务器会通过请求头键值对中的Host中的localhost找到主机本机
  2. 通过8080端口确定本机中占用端口的程序Tomcat
  3. 通过请求行中的"/www.caijiyuan"确定是Tomcat下的哪个站点
  4. 通过"/helloServlet"确定是当前站点下的那个资源路径

找到资源路径后,如果服务器是第一次被访问就会创建一个Servlet否则就会调用service方法生成request对象来处理会话中的请求;接受到请求后经过设定的代码生成response对象保存响应内容返回给客户端(浏览器),这就是Servlet工作的流程

三、其它实现Servlet规范的方式

(1)继承自GenericServlet 类

对于一个 Servlet 类,我们日常最常用的方法是继承自 HttpServlet 类,实际上,HttpServlet是扩展了GenericServlet 类。GenericServlet 类实现了Servlet,ServletConfig和Serializable接口。它主要完成了这些任务:

  • 将 init() 中的 ServletConfig 赋给一个类级变量,可以由 getServletConfig 获得;
  • 为 Servlet 所有方法提供默认实现的接口(除了service方法);
  • 可以直接调用 ServletConfig 中的方法;
    它的基本结构如下:
abstract class GenericServlet implements Servlet,ServletConfig{//GenericServlet通过将ServletConfig赋给类级变量private trServletConfig servletConfig;public void init(ServletConfig servletConfig) throws ServletException {this.servletConfig=servletConfig;/*自定义init()的原因是:如果子类要初始化必须覆盖父类的init() 而使它无效 这样this.servletConfig=servletConfig不起作用 这样就会导致空指针异常 这样如果子类要初始化,可以直接覆盖不带参数的init()方法 */this.init();}//自定义的init()方法,可以由子类覆盖  //init()不是生命周期方法public void init(){}//实现service()空方法,并且声明为抽象方法,强制子类必须实现service()方法 public abstract void service(ServletRequest request,ServletResponse response) throws ServletException,java.io.IOException{}//实现空的destroy方法public void destroy(){ }
}

可以看到如果继承这个类的话,我们必须重写 service() 方法来对处理请求
继承自GenericServlet 类实现Servlet

package www.caijiyuan;import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;@WebServlet("/Servlet_Gener")
public class Servlet_Gener extends GenericServlet {@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {//打印内容在控制台System.out.println("Hello Servlet with terminal");//通过流输出数据到浏览器servletResponse.getWriter().write( "Hello Servlet with brower");}
}

启动服务器后在浏览器中访问得

在这里插入图片描述

(2)实现Servlet接口

除了两个继承抽象类实现Servlet的接口,还有一个通过实现interface Servlet来实现Servlet规范的方法

在这里插入图片描述

实例
创建类Servlet_imp.java ,使其实现Servlet接口
在这里插入图片描述
IDEA中使用快捷键Alt+Shift+Enter实现方法,在service()方法中添加测试代码,并且在类前添上@WebServlet()注释

package www.caijiyuan;import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;@WebServlet("/Servlet_imp")
public class Servlet_imp implements Servlet {@Overridepublic void init(ServletConfig servletConfig) throws ServletException {}@Overridepublic ServletConfig getServletConfig() {return null;}@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {//通过流输出数据到浏览器servletResponse.getWriter().write( "Hello Servlet with brower");}@Overridepublic String getServletInfo() {return null;}@Overridepublic void destroy() {}
}

访问浏览器同样可以得到
在这里插入图片描述

(3)在HttpServlet中直接重写doGet()和doPost()方法

我们在调用HttpServlet中service方法时,底层实际上是判断GET请求还是POST请求从而分别调用doGet()和doPost()方法

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String method = req.getMethod();long lastModified;if (method.equals("GET")) {lastModified = this.getLastModified(req);if (lastModified == -1L) {this.doGet(req, resp);} else {long ifModifiedSince;try {ifModifiedSince = req.getDateHeader("If-Modified-Since");} catch (IllegalArgumentException var9) {ifModifiedSince = -1L;}if (ifModifiedSince < lastModified / 1000L * 1000L) {this.maybeSetLastModified(resp, lastModified);this.doGet(req, resp);} else {resp.setStatus(304);}}} else if (method.equals("HEAD")) {lastModified = this.getLastModified(req);this.maybeSetLastModified(resp, lastModified);this.doHead(req, resp);} else if (method.equals("POST")) {this.doPost(req, resp);} else if (method.equals("PUT")) {this.doPut(req, resp);} else if (method.equals("DELETE")) {this.doDelete(req, resp);} else if (method.equals("OPTIONS")) {this.doOptions(req, resp);} else if (method.equals("TRACE")) {this.doTrace(req, resp);} else {String errMsg = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[]{method};errMsg = MessageFormat.format(errMsg, errArgs);resp.sendError(501, errMsg);}}

实例
创建类Servlet_do.java使其继承自HttpServlet,然后重写doGet()和doPost()方法
在这里插入图片描述
通过上一节我们已经知道了浏览器访问地址是使用GET方法,所以我们在doGet()中添加测试代码,并且在类前添上@WebServlet()注释

package www.caijiyuan;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@WebServlet("/Servlet_do")
public class Servlet_do extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        super.doGet(req, resp);//通过流输出数据到浏览器resp.getWriter().write( "Hello Servlet with brower");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        super.doPost(req, resp);}
}

访问浏览器同样可以得到
在这里插入图片描述

以上三种方式都可以实现Servlet规范,使得普通Java类升级成Servlet类,但最简便的方式还是第一种,也就是继承自HttpServlet类

四、Servlet生命周期

有了以上的知识储备,我们就可以梳理一下Servlet的整个生命周期了:由于Servlet没有main()方法,不能独立运行,它的运行完全由Servlet引擎来控制和调度,所谓生命周期,指的就是Servlet容器何时创建 Servlet实例、何时调用其方法进行请求的处理、何时并销毁其实例的整个过程。

  • 实例和初始化时机:当请求到达容器时,容器查找该 Servlet对象是否存在,如果不存在,则会创建实例并进行初始化,如果存在则会直接调用service()方法
  • 就绪/调用/服务阶段:当有请求到达容器,容器调用Servlet对象的 service()方法,此方法在整个生命周期中可以被多次调用;HttpServlet的 service()方法则会依据请求方式来调用doGet()或者doPost()方法。但是,这两个do方法默认情况下,会抛出异常,需要子类去override
  • 销毁时机:当容器关闭时(应用程序停止时),会将程序中的Servlet实例进行销毁。上述的生命周期可以通过Servlet 中的生命周期方法来观察。在Servlet 中有三个生命周期方法,不由用户手动调用,而是在特定的时机由容器自动调用

观察这三个生命周期方法即可观察到Servlet的生命周期

init方法,在Servlet 实例创建之后执行(证明该Servlet有实例创建了)

public void init() throws ServletException {System.out.println("创建实例调用");
}

service 方法,每次有请求到达某个Servlet 方法时执行,用来处理请求(证明该Servlet 进行服务了)

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("service方法调用了");
}

destroy 方法,Servlet实例销毁时执行(证明该Servlet的实例被销毁了)

public void destroy() {System.out.println("实例被销毁了");
}

实例

启动服务器在浏览器中访问项目得
在这里插入图片描述
在控制台输出,即是在浏览器访问项目时调用了init()方法(只会被调用一次init()),并且紧接着接受请求调用了service方法
在这里插入图片描述
接着关闭Tomcat服务器,在控制台输出
在这里插入图片描述
Servlet的生命周期,可以更详细的分为四步:Servlet类加载 – >实例化 – >服务 – >销毁
下面我们描述一下Tomcat与Servlet是如何工作的,看看下面的时序图:
在这里插入图片描述

  1. Web Client 向Servlet容器(Tomcat)发出Http请求
  2. Servlet 容器接收Web Client的请求
  3. Servlet 容器创建一个HttpServletRequest对象,将Web Client请求的信息封装到这个对象中
  4. Servlet 容器创建一个HttpServletResponse 对象
  5. Servlet 容器调HttpServlet对象service 方法,把Request与Response作为参数,传给HttpServlet
  6. HttpServlet 调用HttpServletRequest对象的有关方法,获取Http请求信息
  7. HttpServlet 调用HttpServletResponse对象的有关方法,生成响应数据
  8. Servlet 容器把HttpServlet的响应结果传给Web Client

以上就是此小节的所有内容,我们系统的学习了Servlet的概念、实现Servlet规范的多个方法、Servlet的工作流程、生命周期等,为之后Servlet具体对象学习打下了坚实的基础。从下一节开始我们将学习service方法两个形参之一:HttpServletRequest实例的具体操作

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

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

相关文章

猫头虎分享已解决Bug || Kubernetes Error: Pods ‘pod-name‘ Not Found

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

PKI - 借助Nginx实现_客户端使用自签证书供服务端验证

文章目录 Pre概述在 Nginx 中实现客户端使用自签名证书供服务器验证1. 生成客户端密钥对2. 生成自签名客户端证书3. 配置 Nginx4. 重启 Nginx 修5. 验证 在浏览器中安装客户端证书以便进行访问 Pre PKI - 借助Nginx 实现Https 服务端单向认证、服务端客户端双向认证 PKI - 数…

前端JavaScript篇之ajax、axios、fetch的区别

目录 ajax、axios、fetch的区别AjaxAxiosFetch总结注意 ajax、axios、fetch的区别 在Web开发中&#xff0c;ajax、axios和fetch都是用于与服务器进行异步通信的技术&#xff0c;但它们在实现方式和功能上有所不同。 Ajax 定义与特点&#xff1a;Ajax是一种在无需重新加载整个…

2.11 运算符

1、选择题 1.1、若有以下程序 main() { char a1,b2; printf("%c,",b); printf("%d\n",b-a); } 程序运行后的输出结果是 C A&#xff09;3,2 B&#xff09;50,2 C&#xff09;2,2 D&#xff09;2,50 解析&#xff1a;b是先赋值后自加&#…

【数学建模】【2024年】【第40届】【MCM/ICM】【C题 网球运动中的“动量”】【解题思路】

一、题目 &#xff08;一&#xff09; 赛题原文 2024 MCM Problem C: Momentum in Tennis In the 2023 Wimbledon Gentlemen’s final, 20-year-old Spanish rising star Carlos Alcaraz defeated 36-year-old Novak Djokovic. The loss was Djokovic’s first at Wimbledon…

Java多线程:生产者-消费者模型

&#x1f451;专栏内容&#xff1a;Java⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、阻塞队列1、标准库阻塞队列2、手动实现阻塞队列 二、生产者-消费者模型1、使用标准库实现2、手动阻塞队列实现 一、阻塞队列…

ARP欺骗攻击利用之抓取https协议的用户名与密码

1.首先安装sslstrip 命令执行&#xff1a;apt-get install sslstrip 2.启动arp欺骗 arpspoof -i ech0 -t 192.168.159.148 192.168.159.2 arpspoof -i ech0(网卡) -t 目标机ip 本地局域网关 3.命令行输入: vim /etc/ettercap/etter.conf进入配置文件 找到下红框的内容&a…

【Linux】学习-深入了解文件的读与写

深入了解语言级别(C语言)文件操作的"读"与"写" 在学习前&#xff0c;我们先要知道在Linux下的一个原则&#xff1a;一切皆是文件 如何理解呢&#xff1f;举个外设的例子&#xff0c;比如键盘和显示器&#xff0c;这两个外设也可以其实本质上也是文件&…

Qt Windows和Android使用MuPDF预览PDF文件

文章目录 1. Windows MuPDF编译2. Android MuPDF编译3. 引用 MuPDF 库4. 解析本地PDF文件 1. Windows MuPDF编译 使用如下命令将MuPDF的源码克隆到本地 git clone --recursive git://git.ghostscript.com/mupdf.git直接用VS&#xff0c;打开 mupdf/platform/win32/mupdf.sln …

docker 部署 mongodb 集群【建议收藏】

一、简洁搭建mognodb副本集 环境说明 我都是在云服务器上搭建的&#xff0c;CentOS7&#xff0c;Docker环境&#xff0c;版本忘记了。我就直接在同一台服务器上搭建三个mongodb即可。 1、基本信息如下 服务器地址 www.it307.top 副本集名称 rs 容器节点及端口映射 ​ m0…

数据结构——6.1 图的基本概念

第六章 图 6.1 图的基本概念 概念 图的概念&#xff1a;G由点集V和边集E构成&#xff0c;记为G(V,E)&#xff0c;边集可以为空&#xff0c;但是点集不能为空 注意&#xff1a;线性表可以是空表&#xff0c;树可以是空树&#xff0c;但图不可以是空&#xff0c;即V一定是非空集…

leetcode:63.不同路径二

dp数组含义&#xff1a;由初始位置到最终位置路径个数 递推公式&#xff1a;如果没有障碍再进行递推公式 初始化&#xff1a;1.若起始位置和终止位置有障碍路径个数为0 2.dp[i][0] 1和dp[0][j] 1的for循环条件都需要加上一个and dp[i][0] 0和and dp[0][j] 0. 3.遍历顺序…

案例:三台主机实现 级联复制

介绍&#xff1a;级联复制架构 级联复制架构 是一种特殊的主从结构&#xff0c;之前聊到的几种主从结构都只有两层&#xff0c;但级联复制架构中会有三层&#xff0c;关系如下&#xff1a; 也就是在级联复制架构中&#xff0c;存在两层从库&#xff0c;这实际上属于一主多从架…

Deepin基本环境查看(九)【被封印的创世神】

文章目录 - 相关文章目录1、概述2、Deepin中的创世神和管理员1&#xff09;创世神root2&#xff09;root被封印原因3&#xff09;其他的神灵【管理员】 3、神殿管理【su与sudo】1&#xff09;su&#xff08;Switch User&#xff09;2&#xff09;sudo&#xff08;Superuser Do&…

Open CASCADE学习|环形弹簧建模

目录 Draw Test Harness&#xff1a; C&#xff1a; 环形弹簧&#xff0c;也称为弓簧&#xff0c;是由拉伸弹簧和连接弹簧构成的。在结构上&#xff0c;环形弹簧通常包括端环、外环和内环&#xff0c;其主要参数包括弹簧的内径、外径和自由高度。环形弹簧的一个显著特点是&am…

计算机毕业设计SSM基于的冷链食品物流信息管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; vue mybatis Maven mysql5.7或8.0等等组成&#xff0c;B…

网络原理(一)

&#x1f495;"Echo"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;网络原理(一) 一. 应用层 应用层是和程序员联系最密切的一层,对于应用层来说,程序员可以自定义应用层协议,应用层的协议一般要约定好以下两部分内容: 根据需求,明确要传输哪些信…

[算法学习]

矩阵乘法 只有当左矩阵列数等于右矩阵行数&#xff0c;才能相乘N*M的矩阵和M*K的矩阵做乘法后矩阵大小为N*k矩阵乘法规则&#xff1a;第一个矩阵A的第 i 行与第二个矩阵的第 j 列的各M个元素对应相乘再相加得到新矩阵C[i][j]的值 整除 同余 同余的性质 线性运算&#xff0c;…

Android:Cordova,JavaScript操作设备功能

Cordova学习 Cordova提供了一组设备相关的API,通过这组API,移动应用能够以JavaScript访问原生的设备功能,如摄像头、麦克风等。 Cordova还提供了一组统一的JavaScript类库,以及为这些类库所用的设备相关的原生后台代码。 Cordova是PhoneGap贡献给Apache后的开源项目,是从…

最适合新手的SpringBoot+SSM项目《苍穹外卖》实战—(一)项目概述

黑马程序员最新Java项目实战《苍穹外卖》&#xff0c;最适合新手的SpringBootSSM的企业级Java项目实战。 项目简介 《苍穹外卖》项目的定位是一款为餐饮企业&#xff08;餐厅、饭店&#xff09;定制的软件产品。该项目是一个在线外卖订购系统&#xff0c;顾客可以通过网站或者…