JavaRush /Java 博客 /Random-ZH /Java 中的 Web 应用程序
Viacheslav
第 3 级

Java 中的 Web 应用程序

已在 Random-ZH 群组中发布
Java 中的 Web 应用程序 - 1

介绍

曾几何时,Java 由于选择 Web 应用程序作为优先事项而巩固了自己的地位。从早期开始,Java 就一直在努力寻找自己的出路。首先,我建议使用小程序。这为开发人员在静态 HTML 页面上创建动态内容提供了许多机会。然而,由于安全、开销等多种原因,小程序并没有达到预期。于是Java语言的开发者提出了一种替代方案——Servlet API。事实证明这是一个正确的决定。 Servlet API是构建任何 Java Web 应用程序的规范,无论是基于 Web 的应用程序还是根据请求返回信息的 Web 服务。因此,了解 Java Web 应用程序如何工作的途径始于了解 Servlet API。
Java 中的 Web 应用程序 - 2

服务程序接口

因此,Servlet API是语言开发人员提供给 Java 开发人员的。Servlet API 是一个应该回答我们主要问题的规范。您可以在这里找到它:“ JSR-000340 JavaTM Servlet 3.1 Final Release for Assessment ”。“ 1.1 什么是Servlet? ”一章说Servlet是基于Java技术的创建动态内容(即内容)的Web组件。“基于Java”意味着servlet 是编译成字节码的Java 类。Servlet 由 servlet 容器(有时称为 Servlet 引擎)管理。servlet 容器是提供 servlet 功能的Web 服务器扩展。反过来,servlet 在请求/响应范例中提供与客户端的交互,这是由 servlet 容器实现的。在“ 1.2什么是Servlet容器? ”一章中,提到Servlet容器是Web服务器或应用程序服务器的一部分,它提供网络服务,通过它发送请求和响应,生成和处理基于MIME的请求和响应。此外,servlet 容器管理 servlet 的生命周期(即决定何时创建它们、删除它们等)。所有servlet容器都必须支持HTTP协议来接收请求和发送响应。在这里我想补充一点,MIME是一个标准,一个规范,它告诉信息应该如何编码和消息格式化,以便它们可以通过互联网发送。
Java 中的 Web 应用程序 - 3

网络服务器

Web 服务器是接受来自客户端的 HTTP 请求并向其提供 HTTP 响应(通常连同 HTML 页面、图像、文件或其他数据)的服务器。请求的资源由 URL 标识。Apache Tomcat是最流行的支持 Servlet API 的 Web 服务器之一。大多数网络服务器都是由各种组件组成的复杂机器,每个组件执行特定的功能。例如:
Java 中的 Web 应用程序 - 4

连接器

— 在输入处,我们有连接器(即连接器),用于接受来自客户端的传入请求。Tomcat 中的 HTTP 连接器是使用“Coyote”组件实现的。连接器从客户端接收数据并将其传递到 Tomcat 引擎。 Servlet 容器- Tomcat 引擎反过来使用“Catalina”组件(一个 Servlet 容器)处理从客户端接收到的请求。更多详细信息请参阅 Tomcat 文档:“架构概述”。还有其他支持 Servlet API 规范的 Web 服务器。例如,“ Jetty ”或“ Undertow ”。它们的架构相似,因此了解使用一个 servlet 容器的原理后,您可以切换到使用另一个容器。
Java 中的 Web 应用程序 - 5

Web应用程序

因此,为了让我们运行一个 Web 应用程序,我们需要一个支持 Servlet API 的 Web 服务器(即具有为 Web 服务器实现 Servlet API 支持的扩展组件)。美好的。到底什么是网络应用程序? 根据Servlet API 规范的“ 10 Web 应用程序”一章, Web 应用程序是 servlet、HTML 页面、类和其他资源的集合,它们构成 Web 服务器上的最终应用程序。根据“ 10.6 Web应用程序存档文件”一章,可以将Web应用程序打包在Web ARchive(带有WAR扩展名的存档)中。如“ Glossary-219 ”页面所述:
Java 中的 Web 应用程序 - 6
也就是说,制作WAR而不是JAR来表明这是一个Web应用程序。下一个重要事实:我们的 WAR 存档中必须有一定的目录结构。在Servlet API规范中的“ 10.5目录结构”一章中。本章说有一个特殊的目录叫“WEB-INF”。该目录的特殊之处在于它对客户端不可见,也不会直接显示给客户端,但 servlet 代码可以访问它。它还说明了 WEB-INF 目录可以包含哪些内容:
Java 中的 Web 应用程序 - 7
从整个列表中,我们现在不知道也不理解有关某些称为部署描述符的web.xml文件的项目。它是什么?“ 14.部署描述符”一章专门介绍部署描述符。简而言之,部署描述符是一个 xml 文件,描述如何在 Web 服务器上部署(即运行)我们的 Web 应用程序。例如,部署描述符指示应使用哪些 URL 来访问我们的应用程序、指示与我们的应用程序相关的安全设置等。“ 14.2 处理部署的规则”一章说,在配置和启动我们的应用程序之前,web.xml 将进行模式验证(即,将根据模式检查 web.xml 的内容是否正确写入) 。在“ 14.3 部署描述符”一章中指出该图在这里: 如果我们查看文件的内容,我们可以看到: http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd
Java 中的 Web 应用程序 - 8
XML 文件使用什么架构?模式指示如何正确填写 XML 文档:可以使用哪些元素、可以在元素中指定什么类型的数据、元素应按什么顺序排列、需要哪些元素等。您可以将 XML 文档的模式与 Java 中的接口进行比较,因为 Java 中的模式还指定应如何编写满足给定接口(即实现给定接口)的类。因此,我们已经掌握了秘密知识,并准备好创建我们的第一个 Web 应用程序!
Java 中的 Web 应用程序 - 9

创建 Web 应用程序

很难想象在不使用自动项目构建系统的情况下使用现代 Java 应用程序。一些最流行的系统是Maven 和 Gradle。我们将使用 Gradle 进行本次审查。Gradle的安装在官方网站上有描述。要创建一个新应用程序,我们需要一个内置于 Gradle 中的插件:“ Build Init Plugin ”。要创建 Java 应用程序,您需要运行以下命令: gradle init --type java-application
Java 中的 Web 应用程序 - 10
创建项目后,我们需要编辑build.gradle文件。这就是所谓的Build Script(更多详细信息,请参见Gradle文档:“编写构建脚本”)。该文件描述了如何组装项目以及使用 Java 项目的其他方面。插件块描述了当前 Gradle 项目应该使用哪些“ Gradle 插件”。插件扩展了我们项目的功能。例如,默认插件是“ java ”。如果我们需要 Java 支持,总是会使用这个插件。但我们不需要“应用程序”插件,因为...... 它的描述表明它用于创建“可执行的 JVM 应用程序”,即 运行 JVM 应用程序。我们需要以 WAR 存档的形式创建一个 Web 应用程序。如果我们在 Gradle 文档中查找 WAR 一词,我们会找到“ War Plugin ”。因此,我们将指定以下插件:
plugins {
    id 'java'
    id 'war'
}
另外,在“ War Plugin Default Settings ”中,表示包含Web应用程序所有内容的目录应该是“src/main/webapp”,应该有与web.xml相同的WEB-INF目录位于。让我们创建一个这样的文件。我们稍后会填写它,因为...... 我们对此还没有足够的信息。在“依赖项”块中,我们指示项目的依赖项,即那些我们的应用程序无法运行的库/框架。在本例中,我们正在编写一个 Web 应用程序,这意味着没有 Servlet API 就无法工作:
dependencies {
    providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
    testCompile 'junit:junit:4.12'
}
providedCompile 意味着依赖项不需要包含在我们的 Web 应用程序的 WAR 存档中:仅在编译时需要它。而执行时,这个依赖将由其他人(即Web服务器)提供。好吧,我们在构建脚本中留下有关我们要使用哪个依赖项存储库的信息 - 所有指定的依赖项都将从它下载:
repositories {
    jcenter()
}
我们从构建脚本文件中删除其他所有内容。现在让我们编辑类 src\main\java\App.java。让我们用它来创建一个 servlet。“第 2 章 Servlet 接口”一章中的 Servlet API 规范指出,Servlet 接口有一个HttpServlet 的基本实现,这在大多数情况下应该足够了,开发人员只需要继承它即可。在“ 2.1.1 HTTP 特定请求处理方法”一章中指出了处理传入请求的主要方法。因此,让我们重写 App.java 类:
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.IOException;

public class App extends HttpServlet {
    public String getGreeting() {
        return "Hello world.";
    }

    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
 		// https://www.oracle.com/technetwork/java/servlet-142430.html
 		PrintWriter out = resp.getWriter();
 		out.println(this.getGreeting());
 		out.close();
 	}
}
所以,我们似乎已经万事俱备了。剩下的就是正确编写部署描述符。从图中,将以下文本复制到 web.xml 中:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="..."
      version="3.1">
      ...
</web-app>
还有其中指示的架构路径: http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd 现在让我们看一个示例,说明 web.xml 在 Servlet API 规范中应是什么样子。该示例在“ 14.5.1 基本示例”一章中给出。让我们将图中所示的内容与规范中所示的示例结合起来。我们得到以下信息:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
      version="3.1">
      <display-name>A Simple Web Application</display-name>
      <servlet>
		<servlet-name>app</servlet-name>
		<servlet-class>App</servlet-class>
	  </servlet>
	  <servlet-mapping>
		<servlet-name>app</servlet-name>
		<url-pattern>/app</url-pattern>
	  </servlet-mapping>
</web-app>
如您所见,我们使用了之前指定的 schema 和 schemaLocation。元素本身的描述取自第 14.5.1 章的示例。如果一切正确,我们将正确执行 gradle war 任务:
Java 中的 Web 应用程序 - 11
Java 中的 Web 应用程序 - 12

启动网络应用程序

Web 应用程序如何启动?让我们首先处理更复杂的选项。我们之前说过有一个支持 Servlet API 的 Apache Tomcat Web 服务器。这意味着我们可以在该服务器上部署我们收集的战争档案(他们也说“部署”)。在“下载 Tomcat ”页面上,从“二进制发行版”部分下载 zip 格式的“核心”交付类型。并将下载的存档解压到某个目录,例如 C:\apache-tomcat-9.0.14。在启动服务器之前,让我们打开文件进行编辑conf\tomcat-users.xml并向其中添加以下行: <user username="tomcat" password="tomcat" roles="tomcat,manager-gui,admin-gui"/> 现在,在命令行上,转到 bin 目录并执行catalina.bat start. 默认情况下,服务器控制台将在 处可用http://localhost:8080/manager。登录名和密码与我们在 tomcat-users.xml 中指定的相同。Tomcat 有一个“webapps”目录,其中包含Web 应用程序。如果我们想部署我们自己的,我们必须将我们的战争档案复制到那里。当我们之前运行 gradle war 命令时,\build\libs\会在目录中创建一个 war 存档。这就是我们需要复制的。复制完成后刷新页面http://localhost:8080/manager即可看到:
Java 中的 Web 应用程序 - 13
完成后http://localhost:8080/javaweb/app,我们将转向我们的 servlet,因为 我们之前将 /app 请求“映射”(即映射)到 App servlet。有一种更快的方法来检查应用程序的工作情况。装配系统再次帮助我们解决了这个问题。在 Gradle 项目的构建脚本中,我们可以将一个新插件“ Gretty ”添加到插件部分: id "org.gretty" version "2.3.1" 现在我们可以执行 gradle 任务来运行我们的应用程序:gradle appRun
Java 中的 Web 应用程序 - 14
详细信息 请参阅“添加 gretty 插件并运行应用程序”。
Java 中的 Web 应用程序 - 15

Spring 和 Servlet API

Servlet 是一切的基础。即使是现在流行的 Spring 框架也只不过是 Servlet API 的一个附加组件。首先,Spring 框架是我们项目的新依赖项。因此,让我们将其添加到依赖项块中的构建脚本中: compile 'org.springframework:spring-webmvc:5.1.3.RELEASE' 在 Spring 框架文档中有一章“ 1.1.DispatcherServlet ”。它说 Spring 框架是建立在“前端控制器”模式之上的 - 这是当有一个名为“ DispatcherServlet ”的中央 servlet 时。所有请求都到达此 servlet,并将调用委托给必要的组件。您看,即使这里也有 servlet。您需要将侦听器添加到部署描述符:
<listener>
	&ltlistener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
这是一个 servlet 上下文事件监听器。也就是说,当Servlet Context启动时,Spring上下文(WebApplicationContext)也启动。什么是 Servlet 上下文?它在 Servle API 规范的“第 4 章 Servlet 上下文”一章中进行了描述。Servlet 上下文是 Servlet 运行的 Web 应用程序的“视图”。每个 Web 应用程序都有自己的 Servlet 上下文。接下来,要启用 Spring 框架,您需要指定 context-param - servlet 上下文的初始化参数。
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>/WEB-INF/app-context.xml</param-value>
</context-param>
DispatcherServlet定义完成配置:
<servlet>
	<servlet-name>app</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
 		<param-value></param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
	<servlet-name>app</servlet-name>
	<url-pattern>/</url-pattern>
</servlet-mapping>
现在我们只需要填写 contextConfigLocation 中指定的文件即可。Spring框架文档的“1.3.1.声明”一章描述了如何做到这一点:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="ru.javarush.javaweb"/>
    <mvc:annotation-driven/>
</beans>
这里重要的是不仅要指示要扫描哪个包,而且我们要注解驱动,即控制 Spring 将如何工作的注解。剩下的就是创建 ru.javarush.javaweb 包并将 Spring 控制器类放入其中:
package ru.javarush.javaweb;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class SpringController {

    @GetMapping("/app")
    @ResponseBody
    public String getGreeting() {
        return "Hello world.";
    }
}
现在运行 gradle appRun 并转到该地址,http://127.0.0.1:8080/javaweb/app我们将得到相同的 Hello World。正如您所看到的,Spring 框架与 Servlet API 紧密相连,并使用它在其之上工作。
Java 中的 Web 应用程序 - 16

注释

正如我们所见,注释很方便。我们并不是唯一这么认为的人。因此,在Servlet API规范中,从3.0版本开始,出现了“ CHAPTER 8 Annotations and Pluggability ”一章,规定Servlet容器必须支持通过注解指定之前在Deployment Descriptor中指定的内容的能力。这样,web.xml就可以从项目中完全删除,并且在servlet类之上您可以指定@WebServlet注释并指示将servlet映射到哪个路径。这里一切似乎都很清楚。但是如果我们把Spring连接到项目中,需要更复杂的设置呢?这里一切都有点复杂。首先,Spring 文档说要在没有 web.xml 的情况下配置 Spring,您需要使用自己的类来实现 WebApplicationInitializer。更多详细信息,请参见“ 1.1.DispatcherServlet ”章节。原来这是一个Spring类。那么这里的Servlet API是如何使用的呢?事实上,ServletContainerInitializer已添加到 Servlet API 3.0 中。使用 Java 中的特殊机制(称为SPI),Spring 指定了名为 的 servlet 容器初始值设定项SpringServletContainerInitializer。反过来,它已经查找 WebApplicationInitializer 的实现并调用必要的方法并执行必要的设置。有关更多详细信息,请参阅“ servlet 容器如何查找 WebApplicationInitializer 实现”。上述设置可以这样完成:
package ru.javarush.javaweb.config;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

public class AppInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        // регистрируем конфигурацию созданую высше
        ctx.register(AppConfig.class);
        // добавляем в контекст слушателя с нашей конфигурацией
        servletContext.addListener(new ContextLoaderListener(ctx));

        ctx.setServletContext(servletContext);

        // настраиваем маппинг Dispatcher Servlet-а
        ServletRegistration.Dynamic servlet =
                servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
        servlet.addMapping("/");
        servlet.setLoadOnStartup(1);
    }
}
现在,使用“基于 Java 的配置”,我们将指示要扫描哪个包+启用注释:
package ru.javarush.javaweb.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "ru.javarush.javaweb.controllers")
public class AppConfig {
}
并且SpringController本身被移到了ru.javarush.javaweb.controllers,这样在扫描的时候,配置就不会找到它自己,而只会寻找控制器。
Java 中的 Web 应用程序 - 17

总结

我希望这篇概述能让您对 Web 应用程序如何在 Java 中工作有所了解。这只是冰山一角,但如果不了解基础知识,就很难理解基于此基础的技术是如何工作的。Servlet API 是任何 Java Web 应用程序的核心部分,我们已经研究了其他框架如何融入其中。要继续,您可以查看以下材料: #维亚切斯拉夫
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION