JavaWeb笔记(五)后端

JavaWeb笔记(五)后端,第1张

JavaWeb笔记(五)后端 JavaWeb后端

经过前面的学习,现在终于可以正式进入到后端的学习当中,不过,我们还是需要再系统地讲解一下HTTP通信基础知识,它是我们学习JavaWeb的基础知识,我们之前已经学习过TCP通信,而HTTP实际上是基于TCP协议之上的应用层协议,因此理解它并不难理解。

打好基础是关键!为什么要去花费时间来讲解计算机网络基础,我们学习一门技术,如果仅仅是知道如何使用却不知道其原理,那么就成了彻头彻尾的“码农”,只知道搬运代码实现功能,却不知道这行代码的执行流程,在遇到一些问题的时候就不知道如何解决,无论是知识层面还是应用层面都得不到提升。

无论怎么样,我们都要明确,我们学习JavaWeb的最终目的是为了搭建一个网站,并且让用户能访问我们的网站并在我们的网站上做一些事情。

计算机网络基础

在计算机网络(谢希仁 第七版 第264页)中,是这样描述万维网的:

万维网(World Wide Web)并非是某种特殊的计算机网络,万维网是一个大规模的联机式信息储藏所,英文简称Web,万维网用**链接**的方法,能够非常方便地从互联网上的一个站点访问另一个站点,从而主动地按需求获取丰富的信息。

这句话说的非常官方,但是也蕴藏着许多的信息,首先它指明,我们的互联网上存在许许多多的服务器,而我们通过访问这些服务器就能快速获取服务器为我们提供的信息(比如打开百度就能展示搜索、打开小破站能刷视频、打开微博能查看实时热点)而这些服务器就是由不同的公司在运营。

其次,我们通过浏览器,只需要输入对应的网址或是点击页面中的一个链接,就能够快速地跳转到另一个页面,从而按我们的意愿来访问服务器。

而书中是这样描述万维网的工作方式:

万维网以客户服务器的方式工作,浏览器就是安装在用户主机上的万维网客户程序,万维网文档所驻留的主机则运行服务器程序,因此这台主机也称为万维网服务器。客户程序向服务器程序发出请求,服务器程序向客户程序送回客户所要的万维网文档,在一个客户程序主窗口上显示出的万维网文档称为页面。

上面提到的客户程序其实就是我们电脑上安装的浏览器,而服务端就是我们即将要去学习的Web服务器,也就是说,我们要明白如何搭建一个Web服务器并向用户发送我们提供的Web页面,在浏览器中显示的,一般就是HTML文档被解析后的样子。

那么,我们的服务器可能不止一个页面,可能会有很多个页面,那么客户端如何知道该去访问哪个服务器的哪个页面呢?这个时候就需要用到URL统一资源定位符。互联网上所有的资源,都有一个唯一确定的URL,比如http://www.baidu.com

URL的格式为:

<协议>://<主机>:<端口>/<路径>

协议是指采用什么协议来访问服务器,不同的协议决定了服务器返回信息的格式,我们一般使用HTTP协议。

主机可以是一个域名,也可以是一个IP地址(实际上域名最后会被解析为IP地址进行访问)

端口是当前服务器上Web应用程序开启的端口,我们前面学习TCP通信的时候已经介绍过了,HTTP协议默认使用80端口,因此有时候可以省略。

路径就是我们希望去访问此服务器上的某个文件,不同的路径代表访问不同的资源。

我们接着来了解一下什么是HTTP协议:

HTTP是面向事务的应用层协议,它是万维网上能够可靠交换文件的重要基础。HTTP不仅传送完成超文本跳转所需的必须信息,而且也传送任何可从互联网上得到的信息,如文本、超文本、声音和图像。

实际上我们之前访问百度、访问自己的网站,所有的传输都是以HTTP作为协议进行的。

我们来看看HTTP的传输原理:

HTTP使用了面向连接的TCP作为运输层协议,保证了数据的可靠传输。HTTP不必考虑数据在传输过程中被丢弃后又怎样被重传。但是HTTP协议本身是无连接的。也就是说,HTTP虽然使用了TCP连接,但是通信的双方在交换HTTP报文之前不需要先建立HTTP连接。1997年以前使用的是HTTP/1.0协议,之后就是HTTP/1.1协议了。

那么既然HTTP是基于TCP进行通信的,我们首先来回顾一下TCP的通信原理:

TCP协议实际上是经历了三次握手再进行通信,也就是说保证整个通信是稳定的,才可以进行数据交换,并且在连接已经建立的过程中,双方随时可以互相发送数据,直到有一方主动关闭连接,这时在进行四次挥手,完成整个TCP通信。

而HTTP和TCP并不是一个层次的通信协议,TCP是传输层协议,而HTTP是应用层协议,因此,实际上HTTP的内容会作为TCP协议的报文被封装,并继续向下一层进行传递,而传输到客户端时,会依次进行解包,还原为最开始的HTTP数据。

HTTP使用TCP协议是为了使得数据传输更加可靠,既然它是依靠TCP协议进行数据传输,那么为什么说它本身是无连接的呢?我们来看一下HTTP的传输过程:

用户在点击鼠标链接某个万维网文档时,HTTP协议首先要和服务器建立TCP连接。这需要使用三报文握手。当建立TCP连接的三报文握手的前两部分完成后(即经过了一个RTT时间后),万维网客户就把HTTP请求报文作为建立TCP连接的三报文握手中的第三个报文的数据,发送给万维网服务器。服务器收到HTTP请求报文后,就把所请求的文档作为响应报文返回给客户。

因此,我们的浏览器请求一个页面,需要两倍的往返时间。

最后,我们再来了解一下HTTP的报文结构:

由客户端向服务端发送是报文称为请求报文,而服务端返回给客户端的称为响应报文,实际上,整个报文全部是以文本形式发送的,通过使用空格和换行来完成分段。

现在,我们已经了解了HTTP协议的全部基础知识,那么什么是Web服务器呢,实际上,它就是一个软件,但是它已经封装了所有的HTTP协议层面的 *** 作,我们无需关心如何使用HTTP协议通信,而是直接基于服务器软件进行开发,我们只需要关心我们的页面数据如何展示、前后端如何交互即可。

认识Tomcat服务器

Tomcat(汤姆猫)就是一个典型的Web应用服务器软件,通过运行Tomcat服务器,我们就可以快速部署我们的Web项目,并交由Tomcat进行管理,我们只需要直接通过浏览器访问我们的项目即可。

那么首先,我们需要进行一个简单的环境搭建,我们需要在Tomcat官网下载最新的Tomcat服务端程序:https://tomcat.apache.org/download-10.cgi(下载速度可能有点慢)

  • 下载:64-bit Windows zip

下载完成后,解压,并放入桌面,接下来需要配置一下环境变量,打开高级系统设置,打开环境变量,添加一个新的系统变量,变量名称为JRE_HOME,填写JDK的安装目录+/jre,比如Zulujdk默认就是:C:Program FilesZuluzulu-8jre

设置完成后,我们进入tomcat文件夹bin目录下,并在当前位置打开CMD窗口,将startup.sh拖入窗口按回车运行,如果环境变量配置有误,会提示,若没问题,服务器则正常启动。

如果出现乱码,说明编码格式配置有问题,我们修改一下服务器的配置文件,打开conf文件夹,找到logging.properties文件,这就是日志的配置文件(我们在前面已经给大家讲解过了)将ConsoleHandler的默认编码格式修改为GBK编码格式:

java.util.logging.ConsoleHandler.encoding = GBK

现在重新启动服务器,就可以正常显示中文了。

服务器启动成功之后,不要关闭,我们打开浏览器,在浏览器中访问:http://localhost:8080/,Tomcat服务器默认是使用8080端口(可以在配置文件中修改),访问成功说明我们的Tomcat环境已经部署成功了。

整个Tomcat目录下,我们已经认识了bin目录(所有可执行文件,包括启动和关闭服务器的脚本)以及conf目录(服务器配置文件目录),那么我们接着来看其他的文件夹:

  • lib目录:Tomcat服务端运行的一些依赖,不用关心。
  • logs目录:所有的日志信息都在这里。
  • temp目录:存放运行时产生的一些临时文件,不用关心。
  • work目录:工作目录,Tomcat会将jsp文件转换为java文件(我们后面会讲到,这里暂时不提及)
  • webapp目录:所有的Web项目都在这里,每个文件夹都是一个Web应用程序:

我们发现,官方已经给我们预设了一些项目了,访问后默认使用的项目为ROOT项目,也就是我们默认打开的网站。

我们也可以访问example项目,只需要在后面填写路径即可:http://localhost:8080/examples/,或是docs项目(这个是Tomcat的一些文档)http://localhost:8080/docs/

Tomcat还自带管理页面,我们打开:http://localhost:8080/manager,提示需要用户名和密码,由于不知道是什么,我们先点击取消,页面中出现如下内容:

You are not authorized to view this page. If you have not changed any configuration files, please examine the file conf/tomcat-users.xml in your installation. That file must contain the credentials to let you use this webapp.

For example, to add the manager-gui role to a user named tomcat with a password of s3cret, add the following to the config file listed above.



Note that for Tomcat 7 onwards, the roles required to use the manager application were changed from the single manager role to the following four roles. You will need to assign the role(s) required for the functionality you wish to access.

  • manager-gui - allows access to the HTML GUI and the status pages
  • manager-script - allows access to the text interface and the status pages
  • manager-jmx - allows access to the JMX proxy and the status pages
  • manager-status - allows access to the status pages only

The HTML interface is protected against CSRF but the text and JMX interfaces are not. To maintain the CSRF protection:

  • Users with the manager-gui role should not be granted either the manager-script or manager-jmx roles.
  • If the text or jmx interfaces are accessed through a browser (e.g. for testing since these interfaces are intended for tools not humans) then the browser must be closed afterwards to terminate the session.

For more information - please see the Manager App How-To.

现在我们按照上面的提示,去配置文件中进行修改:

  
  

现在再次打开管理页面,已经可以成功使用此用户进行登陆了。登录后,展示给我们的是一个图形化界面,我们可以快速预览当前服务器的一些信息,包括已经在运行的Web应用程序,甚至还可以查看当前的Web应用程序有没有出现内存泄露。

同样的,还有一个虚拟主机管理页面,用于一台主机搭建多个Web站点,一般情况下使用不到,这里就不做演示了。

我们可以将我们自己的项目也放到webapp文件夹中,这样就可以直接访问到了,我们在webapp目录下新建test文件夹,将我们之前编写的前端代码全部放入其中(包括html文件、js、css、icon等),重启服务器。

我们可以直接通过 http://localhost:8080/test/ 来进行访问。


使用Maven创建Web项目

虽然我们已经可以在Tomcat上部署我们的前端页面了,但是依然只是一个静态页面(每次访问都是同样的样子),那么如何向服务器请求一个动态的页面呢(比如显示我们访问当前页面的时间)这时就需要我们编写一个Web应用程序来实现了,我们需要在用户向服务器发起页面请求时,进行一些处理,再将结果发送给用户的浏览器。

**注意:**这里需要使用终极版IDEA,如果你的还是社区版,就很难受了。

我们打开IDEA,新建一个项目,选择Java Enterprise(社区版没有此选项!)项目名称随便,项目模板选择Web应用程序,然后我们需要配置Web应用程序服务器,将我们的Tomcat服务器集成到IDEA中。配置很简单,首先点击新建,然后设置Tomcat主目录即可,配置完成后,点击下一步即可,依赖项使用默认即可,然后点击完成,之后IDEA会自动帮助我们创建Maven项目。

创建完成后,直接点击右上角即可运行此项目了,但是我们发现,有一个Servlet页面不生效。

需要注意的是,Tomcat10以上的版本比较新,Servlet API包名发生了一些变化,因此我们需要修改一下依赖:


    jakarta.servlet
    jakarta.servlet-api
    5.0.0
    provided

注意包名全部从javax改为jakarta,我们需要手动修改一下。

感兴趣的可以了解一下为什么名称被修改了:

Eclipse基金会在2019年对 Java EE 标准的每个规范进行了重命名,阐明了每个规范在Jakarta EE平台未来的角色。

新的名称Jakarta EE是Java EE的第二次重命名。2006年5月,“J2EE”一词被弃用,并选择了Java EE这个名称。在YouTube还只是一家独立的公司的时候,数字2就就从名字中消失了,而且当时冥王星仍然被认为是一颗行星。同样,作为Java SE 5(2004)的一部分,数字2也从J2SE中删除了,那时谷歌还没有上市。

因为不能再使用javax名称空间,Jakarta EE提供了非常明显的分界线。

  • Jakarta 9(2019及以后)使用jakarta命名空间。
  • Java EE 5(2005)到Java EE 8(2017)使用javax命名空间。
  • Java EE 4使用javax命名空间。

我们可以将项目直接打包为war包(默认),打包好之后,放入webapp文件夹,就可以直接运行我们通过Java编写的Web应用程序了,访问路径为文件的名称。

Servlet

前面我们已经完成了基本的环境搭建,那么现在我们就可以开始来了解我们的第一个重要类——Servlet。

它是Java EE的一个标准,大部分的Web服务器都支持此标准,包括Tomcat,就像之前的JDBC一样,由官方定义了一系列接口,而具体实现由我们来编写,最后交给Web服务器(如Tomcat)来运行我们编写的Servlet。

那么,它能做什么呢?我们可以通过实现Servlet来进行动态网页响应,使用Servlet,不再是直接由Tomcat服务器发送我们编写好的静态网页内容(HTML文件),而是由我们通过Java代码进行动态拼接的结果,它能够很好地实现动态网页的返回。

当然,Servlet并不是专用于HTTP协议通信,也可以用于其他的通信,但是一般都是用于HTTP。

创建Servlet

那么如何创建一个Servlet呢,非常简单,我们只需要实现Servlet类即可,并添加注解@WebServlet来进行注册。

@WebServlet("/test")
public class TestServlet implements Servlet {
		...实现接口方法
}

我们现在就可以去访问一下我们的页面:http://localhost:8080/test/test

我们发现,直接访问此页面是没有任何内容的,这是因为我们还没有为该请求方法编写实现,这里先不做讲解,后面我们会对浏览器的请求处理做详细的介绍。

除了直接编写一个类,我们也可以在web.xml中进行注册,现将类上@WebServlet的注解去掉:


    test
    com.example.webtest.TestServlet


    test
    /test

这样的方式也能注册Servlet,但是显然直接使用注解更加方便,因此之后我们一律使用注解进行开发。只有比较新的版本才支持此注解,老的版本是不支持的哦。

实际上,Tomcat服务器会为我们提供一些默认的Servlet,也就是说在服务器启动后,即使我们什么都不编写,Tomcat也自带了几个默认的Servlet,他们编写在conf目录下的web.xml中:

    
        default
        /
    

    
    
        jsp
        *.jsp
        *.jspx
    

我们发现,默认的Servlet实际上可以帮助我们去访问一些静态资源,这也是为什么我们启动Tomcat服务器之后,能够直接访问webapp目录下的静态页面。

我们可以将之前编写的页面放入到webapp目录下,来测试一下是否能直接访问。

探究Servlet的生命周期

我们已经了解了如何注册一个Servlet,那么我们接着来看看,一个Servlet是如何运行的。

首先我们需要了解,Servlet中的方法各自是在什么时候被调用的,我们先编写一个打印语句来看看:

public class TestServlet implements Servlet {

    public TestServlet(){
        System.out.println("我是构造方法!");
    }

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("我是init");
    }

    @Override
    public ServletConfig getServletConfig() {
        System.out.println("我是getServletConfig");
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("我是service");
    }

    @Override
    public String getServletInfo() {
        System.out.println("我是getServletInfo");
        return null;
    }

    @Override
    public void destroy() {
        System.out.println("我是destroy");
    }
}

我们首先启动一次服务器,然后访问我们定义的页面,然后再关闭服务器,得到如下的顺序:

我是构造方法!
我是init
我是service
我是service(出现两次是因为浏览器请求了2次,是因为有一次是请求favicon.ico,浏览器通病)

我是destroy

我们可以多次尝试去访问此页面,但是init和构造方法只会执行一次,而每次访问都会执行的是service方法,因此,一个Servlet的生命周期为:

  • 首先执行构造方法完成 Servlet 初始化
  • Servlet 初始化后调用 init () 方法。
  • Servlet 调用 service() 方法来处理客户端的请求。
  • Servlet 销毁前调用 destroy() 方法。
  • 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。

现在我们发现,实际上在Web应用程序运行时,每当浏览器向服务器发起一个请求时,都会创建一个线程执行一次service方法,来让我们处理用户的请求,并将结果响应给用户。

我们发现service方法中,还有两个参数,ServletRequest和ServletResponse,实际上,用户发起的HTTP请求,就被Tomcat服务器封装为了一个ServletRequest对象,我们得到是其实是Tomcat服务器帮助我们创建的一个实现类,HTTP请求报文中的所有内容,都可以从ServletRequest对象中获取,同理,ServletResponse就是我们需要返回给浏览器的HTTP响应报文实体类封装。

那么我们来看看ServletRequest中有哪些内容,我们可以获取请求的一些信息:

@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    //首先将其转换为HttpServletRequest(继承自ServletRequest,一般是此接口实现)
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        
        System.out.println(request.getProtocol());  //获取协议版本
        System.out.println(request.getRemoteAddr());  //获取访问者的IP地址
  		  System.out.println(request.getMethod());   //获取请求方法
        //获取头部信息
        Enumeration enumeration = request.getHeaderNames();
        while (enumeration.hasMoreElements()){
            String name = enumeration.nextElement();
            System.out.println(name + ": " + request.getHeader(name));
        }
}

我们发现,整个HTTP请求报文中的所有内容,都可以通过HttpServletRequest对象来获取,当然,它的作用肯定不仅仅是获取头部信息,我们还可以使用它来完成更多 *** 作,后面会一一讲解。

那么我们再来看看ServletResponse,这个是服务端的响应内容,我们可以在这里填写我们想要发送给浏览器显示的内容:

//转换为HttpServletResponse(同上)
HttpServletResponse response = (HttpServletResponse) servletResponse;
//设定内容类型以及编码格式(普通HTML文本使用text/html,之后会讲解文件传输)
response.setHeader("Content-type", "text/html;charset=UTF-8");
//获取Writer直接写入内容
response.getWriter().write("我是响应内容!");
//所有内容写入完成之后,再发送给浏览器

现在我们在浏览器中打开此页面,就能够收到服务器发来的响应内容了。其中,响应头部分,是由Tomcat帮助我们生成的一个默认响应头。

因此,实际上整个流程就已经很清晰明了了。

解读和使用HttpServlet

前面我们已经学习了如何创建、注册和使用Servlet,那么我们继续来深入学习Servlet接口的一些实现类。

首先Servlet有一个直接实现抽象类GenericServlet,那么我们来看看此类做了什么事情。

我们发现,这个类完善了配置文件读取和Servlet信息相关的的 *** 作,但是依然没有去实现service方法,因此此类仅仅是用于完善一个Servlet的基本 *** 作,那么我们接着来看HttpServlet,它是遵循HTTP协议的一种Servlet,继承自GenericServlet,它根据HTTP协议的规则,完善了service方法。

在阅读了HttpServlet源码之后,我们发现,其实我们只需要继承HttpServlet来编写我们的Servlet就可以了,并且它已经帮助我们提前实现了一些 *** 作,这样就会给我们省去很多的时间。

@Log
@WebServlet("/test")
public class TestServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().write("恭喜你解锁了全新玩法");
    }
}

现在,我们只需要重写对应的请求方式,就可以快速完成Servlet的编写。

@WebServlet注解详解

我们接着来看WebServlet注解,我们前面已经得知,可以直接使用此注解来快速注册一个Servlet,那么我们来想细看看此注解还有什么其他的玩法。

首先name属性就是Servlet名称,而urlPatterns和value实际上是同样功能,就是代表当前Servlet的访问路径,它不仅仅可以是一个固定值,还可以进行通配符匹配:

@WebServlet("/test/*")

上面的路径表示,所有匹配/test/随便什么的路径名称,都可以访问此Servlet,我们可以在浏览器中尝试一下。

也可以进行某个扩展名称的匹配:

@WebServlet("*.js")

这样的话,获取任何以js结尾的文件,都会由我们自己定义的Servlet处理。

那么如果我们的路径为/呢?

@WebServlet("/")

此路径和Tomcat默认为我们提供的Servlet冲突,会直接替换掉默认的,而使用我们的,此路径的意思为,如果没有找到匹配当前访问路径的Servlet,那么久会使用此Servlet进行处理。

我们还可以为一个Servlet配置多个访问路径:

@WebServlet({"/test1", "/test2"})

我们接着来看loadOnStartup属性,此属性决定了是否在Tomcat启动时就加载此Servlet,默认情况下,Servlet只有在被访问时才会加载,它的默认值为-1,表示不在启动时加载,我们可以将其修改为大于等于0的数,来开启启动时加载。并且数字的大小决定了此Servlet的启动优先级。

@Log
@WebServlet(value = "/test", loadonStartup = 1)
public class TestServlet extends HttpServlet {

    @Override
    public void init() throws ServletException {
        super.init();
        log.info("我被初始化了!");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().write("恭喜你解锁了全新玩法");
    }
}

其他内容都是Servlet的一些基本配置,这里就不详细讲解了。

使用POST请求完成登陆

我们前面已经了解了如何使用Servlet来处理HTTP请求,那么现在,我们就结合前端,来实现一下登陆 *** 作。

我们需要修改一下我们的Servlet,现在我们要让其能够接收一个POST请求:

@Log
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getParameterMap().forEach((k, v) -> {
            System.out.println(k + ": " + Arrays.toString(v));
        });
    }
}

ParameterMap存储了我们发送的POST请求所携带的表单数据,我们可以直接将其遍历查看,浏览器发送了什么数据。

现在我们再来修改一下前端:


    登录到系统
    

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5609222.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-15
下一篇 2022-12-15

发表评论

登录后才能评论

评论列表(0条)

保存