简单介绍了Java Web的概念以及Web应用的发展。


文章目录

  • 1 Java Web概述
  • 2 Web请求
  • 3 Web服务器与Servlet的关系
  • 3.1 web服务器
  • 3.2 Servlet规范
  • 3.3 其他补充


1 Java Web概述

Java EE全名为Java Platform Enterprise Edition,即Java平台企业版。JavaEE以JavaSE作为语法基础,可以看作一个大的框架,提供了的一系列使用Java语言进行企业级应用开发的组件和API标准,没有提供具体的实现(也就是接口)。比如常见的Servlet、JDBC、EJB、JNDI、Servlet、JSP等等都是它制定的企业级应用程序开发的规范或者标准。

Java Web使用常常指Java开发的Web应用程序,并且实现了Java EE的一系列标准,比如我们自己的Servlet类,就是Servlet标准的实现。Java Web算作企业级开发的组成部分或者子集,Java Web和Java EE可以说不算是一个层次的东西,一个是实现,一个是规范!

Java Web应用程序开发,常常使用的规范就是Servlet、JSP、JDBC等,在更加上层的框架层出不穷的今天,我们也基本不会亲自手动实现这些标准提供的接口了,而是由框架来帮我们实现,然后我们又使用框架提供的封装好的更上层的、更友好的API进行开发。比如Spring web MVC,它帮我们实现了Servlet标准并且提供了更好使用的API,我们在使用Spring MVC开发的时候,根本感觉不到Servlet的存在!

但是如果不仅仅是会用,而是想要阅读这些框架的源码,那么,我们仍然不得不接触这些底层的API和规范。

2 Web请求

所谓Web应用程序,关键的使用前面的“web”,即“网络”的意思,也就是说,我们开发的企业级web应用层序,可以通过互联网,让远程的客户端能够访问。

通常,我们的web应用程序被称为服务端,包括了编写的程序代码、数据、各种资源,被放在服务器中,而客户操作的浏览器被称为客户端,这实际上就是BS应用架构的基础模型。最简单的服务端和客户端通信规则为:浏览器通过网络将请求发送给服务器,随后服务器根据请求地址把请求转发到对应的web应用程序中对应的处理程序,接着通过我们写的代码逻辑进行数据处理,并且将处理结果填充到前端页面中,并把Web页面数据传递给web服务器,web服务器再通过网络将web页面传递给客户的浏览器,随后展示给用户即可。

现在,我们模拟一下客户端和服务端的交互!我们使用浏览器作为客户端,因此主要就是模拟服务器的逻辑,我们采用Socket来编程 ,Socket隐藏了各种TCP/IP协议的交互,提供了针对TCP或者UDP编程的接口,可以接收请求和发送响应,实现不同计算机之间的通信。

/**
 * 模拟服务器
 *
 * @author lx
 */
public class MyServer {

    /**
     * 服务器通过线程池处理请求
     */
    static ThreadPoolExecutor serverExecutors = new ThreadPoolExecutor(10, 20, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()
            , Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());

    /**
     * 启动服务器
     */
    public static void main(String[] args) throws IOException {
        //1.创建服务器socket,并绑定端口号,可以自己指定。
        ServerSocket ss = new ServerSocket(8888);
        //循环监听客户端连接
        while (true) {
            //监听客户端连接,返回对应的socket对象.此方法在成功返回之前一直阻塞!
            Socket socket = ss.accept();
            //启动一个线程处理该连接
            serverExecutors.submit(new myCall(socket));
        }
    }

    static class myCall implements Callable<Object> {

        Socket socket;

        public myCall(Socket socket) {
            this.socket = socket;
        }

        @Override
        public Object call() throws Exception {
            //处理请求
            processData();
            //返回响应
            responseData();
            return null;
        }

        private void processData() throws IOException {
            //创建输入流,使用read()读取数据,如果未读取到数据,此方法将一直阻塞!
            InputStream is = socket.getInputStream();
            byte[] b = new byte[1024];
            int read = is.read(b);
            //获取请求的数据
            System.out.println(new String(b, 0, read));
        }

        private void responseData() throws IOException {
            //给客户端响应,获得输出流,发送数据
            OutputStream os = socket.getOutputStream();
            String str = "HTTP/1.1 200 OK    " +
                    "Server: Apache-Coyote/1.1   " +
                    "Accept-Ranges: bytes\n" +
                    "ETag: W/\"480-1543570084000\"\n" +
                    "Last-Modified: Fri, 03 Dec 2020 17:47:04 GMT \n" +
                    "Content-Type: text/html       \n" +
                    "Content-Length: 480           \n" +
                    "Date: Fri, 30 Nov 2018 16:39:05 GMT    \n" +
                    "\n" +
                    "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" +
                    "<html>\n" +
                    "  <head>\n" +
                    "<title>demo.html</title>\n" +
                    "\n" +
                    "    <meta http-equiv=\"keywords\" content=\"keyword1,keyword2,keyword3\">\n" +
                    "    <meta http-equiv=\"description\" content=\"this is my page\">\n" +
                    "    <meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n" +
                    "    <!--<link rel=\"stylesheet\" type=\"text/css\" href=\"./styles.css\">-->\n" +
                    "  </head>\n" +
                    "  <body>" +
                    "<font color=red > 响应的html数据,This is my HTML page.  </font>" +
                    "   </body>\n" +
                    "</html>\n";
            os.write(str.getBytes());
            os.flush();
        }
    }
}

这里我们仅提供局域网的交互!在自己的电脑中浏览器中访问:http://localhost:8888/,随后运行main函数,即“启动服务器”,即可在浏览器中获取响应,完成一次请求和响应交互。也可以在局域网中将localhost换成自己的ip即可实现远程计算机访问!

下面是客户端浏览器显示的服务器发送的响应数据!

java服务巡检 javaee规范的服务器_java服务巡检

3 Web服务器与Servlet的关系

3.1 web服务器

上文中,我们已经模拟了一个简单的HTTP服务器,可以接收请求,发送响应,似乎也没那么难,然而,实际上我们这个服务器仅仅提供了非常简单的获取请求和响应数据的功能,其他的比如http请求中各种参数的解析,响应数据的处理,以及更底层的TCP连接复用,多线程IO,异常处理等功能则没有编写,这些功能需要编写大量的代码,并且进行多次的测试、优化,对于大部分菜鸡程序员来说(比如本人),是不具备这些底层功能的编写以及优化能力的!

在一个web中项目中,大多数程序员最关心的应该是业务层面的功能,而非网络传输、请求解析、发送响应等底层功能,实际上在我们开发的web项目中,确实也没有写这些网络通信的底层代码,仅仅是写的“业务代码”而已,为什么可以这样呢?这就是web服务器的功劳!我们只需要将编写的web应用放到web服务器指定的位置,随后启动web服务器,即可根据请求实现我们的业务功能。

web服务器也是一个应用程序,专门用于处理TCP连接、接受HTTP请求、响应数据等底层的功能,简单的说就是处理网络请求与响应。这样,有了web服务器,对于大多数程序员来说,就能一心一意的实现业务功能,而不必关心网络通信这一底层功能!典型的web服务器就是tomcat。

到此,我们可以知道编写的“Web应用”是不具备处理网络请求的接收与响应的功能的,它仅仅是一个在中间工作程序,用于处理已经被Web服务器解析、封装好的网络请求。web服务器将接受到的请求转发给对应的web应用的处理程序,处理完毕之后再将结果响应给客户端。

3.2 Servlet规范

那么web服务器如何将封装好的请求传递给web应用,以及web应用如何将响应结果数据传递给web服务器,以及二者交互的数据的格式是怎样的,对于这种通用的问题,制定一个统一的标准最好了,所有的web项目都可以按照这个标准来开发,web服务器也按照这个标准来实现。这对于Java来说很简单,那就是抽象成为一个接口(interface)即可,这就是Servlet,Servlet没有标准中文翻译,直译过来就是“小型应用程序”,Servlet就是一个Java中的接口而已,接口的作用是什么?对,就是标准、规范,Servlet就定义了通过Java开发web应用处理网络请求以及响应数据的接口规范。换一个说法, Servlet就是Java方便进行动态web应用开发的一个统一的标准(模版)。

Servlet的简单规范如下:我们想要开发某个项目,只需要开发对应的Servlet实现,并将其对应到一个请求url地址,随后在方法里面编写业务逻辑即可,而请求和响应,都已被同样实现了Servlet标准的web服务器(比如tomcat)帮我们封装为ServletRequest和ServletResponse对象,当一个请求到来时,tomcat会将封装好的参数对象传递到Servlet的方法参数中,我们可以直接从ServletRequest参数对象中获取请求的参数,也可以通过ServletResponse参数对象发出响应。这样,就解决了web服务器和web应用数据交互的问题,所有的Java Web应用都可以按照这个模式/标准来开发。

因此,大部分人所说的web项目开发,主要就是Servlet开发。虽然现在都是基于框架开发,比如Spring MVC,但是Spring MVC只是对Servlet原生API进行封装,它的Controller方法就是Servlet方法的入口,内部的Service层和DAO层不过是为了方便开发而拆分出来的,它们的方法都直接或者间接的被Controller方法调用。

总结一下:Servlet就是一个sun制订的使用Java语言开发Web项目的一个规范,我们使用Servlet规范开发的Web项目,就能够运行在同样实现了Servlet规范的Web服务器中,这样的好处是,解析客户端网络请求、线程复用、响应数据给客户端等一系列底层的网络编程工作都由Web服务器帮我们完成了,而我们的“Web项目”仅仅需要编写Servlet的实现,在实现中仅仅需要编写业务逻辑就行了,随后我们还需要将指定的Servlet实现与指定的请求路径绑定。

3.3 其他补充

Web服务器以多线程的形式处理请求,当一个请求到来的时候,Web服务器会使用一条线程帮我们将请求参数和响应数据都封装成为一个HttpServletRequest和HttpServletResponse对象,并将HttpServletRequest和HttpServletResponse传递给对应请求路径的Servlet实例,在Servlet的方法中的业务逻辑处理完之后,Web服务器又自动将我们的return返回的结果封装成对应的响应数据返回给客户端,总体而言就这么简单。

Servlet,甚至于我们编写的整个web应用程序,在一次请求与响应过程中,都是非常被动的一个角色,我们不需要手动创建Servlet对象,应用中也没有main方法,我们只需要将应用中某个Servlet和某个URL请求对应上,然后将web应用交给tomcat,随后启动tomcat,之后tomcat接受到请求之后就会自动映射到对应的Servlet,创建Servlet对象并调用Servlet的方法,这个方法中就是我们自己开发的处理请求的逻辑,从而完成一次请求和响应,因此tomcat由于支持Servlet和JSP的运行,因此也被称为Servlet/jSP容器!注意,一个Servlet只会实例化一次,但是一个请求对应着一个不同的线程,因此它们可能共用一个Servlet实例,所以我们开发的Servlet需要注意线程安全问题。

对于大部分程序员而言,他们不具备或者说不精通网络编程的能力,因此,使用Servlet规范开发Web项目对于他们而言是再好不过了(对我来说也是一样的)!
那么,不使用Servlet规范开发Web项目行不行呢?那肯定也是可以的呀,完全没问题,只不过这需要我们自己来解析请求、返回响应,比如说在“Web请求”部分的案例中,我们就只使用了一个类就给客户端浏览器响应了数据。当然,一个大型项目业务众多,各种请求,甚至包括文件上传、下载之类的,网络编程的实现远比这个案例复杂,那就相当于你自己开发一个项目的同时还顺便开发一个Web服务器了。当然,这就比较夸张了,实际上我们基于Netty就能比较轻松实现一个Web服务器,自己解析请求、响应,就能构建一个不遵循Servlet规范的Web项目。

另外,在“Web应用”层面,目前我们也很少亲自实现Servlet了,因为有了更好的Web框架帮助我们对Servlet进行了封装,比如Spring MVC,它就是基于Servlet API封装的一种框架,但是在使用Spring MVC的时候,我们完全感受不到Servlet的存在,这就是框架的强大之处。而Spring 5.0又推出了底层基于Netty的异步非阻塞的响应式Web框架Spring WebFlux,Spring WebFlux开发的Web项目就可以不运行在Servlet容器中,比如它可以直接运行在Netty服务器中。

参考:

《图解HTTP》

如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!