# Java Web开发基本概念

# 一、什么是 Java Web

Java语言能长期霸占语言排行榜一个重要的原因就是强大的web开发能力,web开发是java的基石(在EJB推出的时候当时的Sun用基石来描述EJB),所以了解java web开发原理是非常重要的。

# 1.1 Web开发的历史

web开发的历史其实并不久远,要搞清楚java web开发的特点(主要是优点),首先要了解web开发的历史(简单的回归一下)。早期的web是非常简单的结构,用户发出请求(request),服务器给出回应(response),这个时期的web应用,我们称为web site(网站),特点是一些列静态内容的集合。看一个图示:

图中的服务器保持了一系列html脚本来响应用户的请求,可以说这个时期的web应用还是比较简单的,但是却确立了两个重要的对象:一个request(代表请求),另一个是response(代表回应)。

在web发展过程中,有一个小插曲,就是在web site向web application发展的过程中,出现了一个小“玩意儿”,就是applet,很多人了解java都是从使用java applet开始的,当时applet给静态页面一个动态交互的可能,着实火了一段时间。现在知道applet的程序员,你已经暴露年龄了。看一张图片吧:

过了这个插曲,真正的三层web开发来了,一个里程碑式的web处理方式CGI,看一张图:

CGI的推出,使得web开发正式进入了动态处理时代,服务器能与客户有真正意义上的交流了,有能存储数据的数据库了,虽然CGI的使用周期并不长,但是一定要纪念一下它,毕竟它是里程碑式的变革。java web技术正是踩着CGI的肩膀来到了广大程序员的面前,java web解决了CGI的性能问题。CGI是以进程为单位管理请求的,而java web则是以线程为单位,处理能力更强,占用的资源更少,这个核心的组件就是Servlet。看一组资源占用图,先看CGI的:

再看一下java web中的servlet资源图:

孰优孰劣一目了然,Servlet解决相同数量的请求,却占用较少的系统资源,这就是为什么广大程序员抛弃了CGI转向java web的原因。

另外,开发一个Servlet并不复杂,看一个Servlet编写的HelloWorld应用:

这个代码结构是不是很亲切,是不是很有Coding的感觉,其实Servlet就是个java 类而已,只不过增加了几个限制而已,所以开发一个Servlet并不复杂。然后就是把它部署到web服务器上(Tomcat这个老人家现在身体依然硬朗!),然后就等待客户的请求就可以了。这是Servlet的三层部署图:

java web开发的技术体系还包括javabean和jsp,采用MVC结构来组合这三个技术是java web开发的基础内容,先看一下MVC的功能图:

再看一下组合使用Servlet+javaBean+JSP的Model2开发结构:

这个结构是标准的Java web开发结构,现在是不是很少能看到这么“干净”的描述图了?以上就是标准的java web 开发的历史描述,当然这并不是说这些内容已经过时了,反而它一直是官方的标准解决方案。只不过web发展迎来了另一个阶段,繁荣的开源架构时代来了。。。

# 1.2 开源框架时代

这个时代的典型代表就是StrutsSpringHibernate,简称SSH

严格的说,这部分内容并不是官方解决方案,但是这些方案却得到了广大程序员的拥护,一方面原因是EJB的方案太重了,另一方面开源架构使用起来非常方便和灵活,所以从03年以后这些开源框架得到了普遍的使用。

下面我简单描述一下这三个框架:

  • Struts 基于MVC结构的解决方案,分为struts1(已经淘汰了,用过Struts1的程序员已经老了)和struts2两个版本,和Python一样,这两个版本不兼容,目前Struts2的最新版本是2.5.14.1,简单的说Struts就是构建了现成的MVC框架,程序员往这个框架里加代码就可以了,使用起来非常方便。
  • Hibernate 框架完成了面向对象与面向关系的映射,让java程序以面向对象的方式操作面向关系的数据库。整体结构基于DAO进行扩展,很多操作只需要配置一下就可以了,极其方便。
  • Spring 提供了javaBean的容器,池化了javabean,提高了性能,而且核心代码不到2M,小巧且强大。

# 二、Java Web基础知识

# 2.1 JavaEE

J2EE, JavaEE 以及 JEE 现在可以认为指的都是同一个东西,只不过由于历史原因出现了若干名称。为了大家理解方便,我们统一用拼写和理解比较方便的 JavaEE 这个名称。

JavaEE 全称 Java Platform, Enterprise Edition,它是对 JavaSE(Java Platform, Standard Edition) 的扩展,加入了面向企业开发(实际上就是网络和 Web 有关开发)的支持,包括 Servlet,WebSocket,EL,EJB 等。简单理解,JavaEE 就是 JavaSE + 更多的 jar 包,这些 jar 包命名以 javax 开头,例如 javax.servlet, javax.websocket 等。

注意 JavaEE 平台提供了 API 标准,但是不一定提供了实现。因此单纯靠 JavaEE 平台本身,是无法进行完整的 Web 开发的,下面会具体讲到。

# 2.2 Servlet 和 Servlet Container

在 JavaEE 的诸多组件中,做 Web 开发一定躲不开的是 Servlet。Servlet 是一套用于处理 HTTP 请求的 API 标准。我们可以基于 Servlet 实现 HTTP 请求的处理。但是 JavaEE 当中只提供了 Servlet 的标准,要真正运行 Servlet,需要使用 Servlet Container。

如果 Servlet 是电器,Servlet Container 就是电源插座。如果你之前接触过 Python 的 WSGI 或者其它语言的 Web 框架,这样的设计就很容易理解。这层抽象让 Servlet 可以跑在任何一个 Container 当中,隔绝了对 Runtime 环境的依赖。

JavaEE 本身没有提供 Servlet Container,比较常用支持 Servlet Container 的 Server 软件有 Apache Tomcat,Glassfish,JBoss,Jetty 等等。

Servlet 是 Java Web 开发的事实标准,不过也不代表所有 Java Web 框架都一定要使用或者兼容 Servlet。不使用 Servlet 也可以进行 Java Web 开发,例如 Play Framework,就是完全自立门户的一个框架。

# 2.3 EJB 和 EJB Container

EJB 全称 Enterprise JavaBean,和 Servlet 一样,也是 JavaEE 当中的一个组件,面向更加复杂的企业业务开发。对于 Web 开发来说,EJB 不是必须的。本教程不打算涉及有关 EJB 开发的内容,读者如果感兴趣可以查阅有关资料。

有一个概念需要明确,和 Servlet 类似,运行 EJB 也需要专门的 EJB Container。并不是所有的 Server 软件都支持 EJB。例如,Apache Tomcat 不支持 EJB,而 JBoss 提供了对 EJB 的支持。

# 2.4 Spring

Spring 是一个非常庞大的框架,其中包括 SpringMVC,SpringBoot 以及 SpringCloud 等用于 Web 开发的工具。

Spring 某种程度上可以认为是 EJB 的替代品。Spring 不需要完整的 JavaEE 内容,仅仅依赖了最基础的 Servlet,也不需要 EJB Container,只用普通的 Servlet Container 就可以运行。在 Servlet 之上 Spring 提供了诸多方便好用的工具,极大地降低了 Java Web 开发入门门槛。

注意一些中文资料把 JavaEE 以及 JavaWeb 和 Spring 混淆在一起,是十分不妥的。经过前面的介绍可以看到,Spring 和 JavaEE 不是一个层面上的东西。Spring 仅依赖了 JavaEE 的 API 标准,最新版的 Spring 甚至进一步隔绝了 JavaEE 的 API。开发者可以完全不关心 Servlet 或者 JavaEE 等概念,也可以进行 Java Web 开发。同时 Spring 也不是唯一的 Java Web 框架,其竞争对手有 Structs,Spark 等。

# 2.5 Annotation

在前面的代码中,我们使用了诸如 @Controller 等 Annotation。要理解前面的代码,首先要对 Java 的 Annotation 语法有一定了解。考虑到并不是所有读者对于 Annotation 语法都很熟悉,这里简单做一下介绍。

Annotation,可以翻译成注解,可以看成对源代码的一种标注。注解本身不会对源代码产生任何影响,不过我们可以通过在编译器或者运行期检查代码中的注解,通过注解给代码引入更多的功能。

Java 语言本身就自带了一些注解,例如最常见的 @Override,用来标记方法是一个重载方法。以及 @Deprecated 用于标记代码为废弃。

为了编写自定义注解,需要使用 Java 提供的若干用于生成注解的注解,即所谓的元注解(Meta Annotations)。这里可能有一点绕,简单理解就是,这些注解存在的作用,就是为了让我们可以编写自己的注解。用一个例子来说明:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
}

在上面这个例子中,我们使用了若干个注解,来编写我们自己的 @MyAnnotation 注解,下面分别介绍一下它们的作用:

  1. @Target:指明这个注解可以用在哪些语言元素上,例如方法,类型,函数参数等。
  2. @Retention:指明注解的存储方式,分别是 SOURCE(只存在于代码中,编译完成后被丢弃),CLASS(存储在生成的 class 文件中,但是会被 VM 在运行期丢弃),RUNTIME(存储在 class 文件中,并且 VM 在运行期间也会保留,因此可以使用反射获得),默认的存储方式是 CLASS。
  3. @Documented:指明使用了这个注解的代码,其生成的 Javadoc 中会把此注解展示出来
  4. @Inherited:这里没有使用,它是用于标记 Target 是 Class 的 Annotation 的,在其他类型的 Annotation 上使用不起作用。它标记对于父类的注解,会被子类做继承,默认行为是不继承。

# 2.6 POJO

Java 语言中引入了很多乍一看奇奇怪怪的概念,POJO 应该算是其中的一个。POJO 全称是 Plain Old Java Object,即普通的 Java 对象。单看全称的话,POJO 实在是没什么意义,我们随便写一个 Java 对象都可以被称为 POJO,而事实也正是如此,大部分 Java 对象都可以被称为 POJO。那么 POJO 这个概念实际上存在有什么意义呢?

想了解这个,首先要知道 POJO 想要对比的是什么。我们之前提到的 EJB 以及类似的 Java 框架当中,开发者为了使用框架的功能,需要继承特定的类,实现特定的接口,这样产生的依赖相对重的类,就不能被称为 POJO。例如在使用 JMS 框架时,为了实现一个 MessageListener,需要实现特定的接口:

public class ExampleListener implements MessageListener {
    public void onMessage(Message message) {
        // ...
    }
}

再例如,在使用 RMI 时,我们自己的业务接口,也需要继承自特定的接口:

public interface IHello extends Remote {
    public String helloWorld() throws RemoteException;
}

这样的设计实际上把业务和框架本身紧紧的耦合在了一起,POJO 的提出就是反对这种设计,鼓励大家减少这种很重的框架依赖。

SpringMVC 框架是鼓励使用 POJO 的,例如我们前面编写的 Controller 类,如果把所有的 Annotation 都去掉,就成了这个样子:

public class MyFirstSpringController {
    public String Hello() {
        return "Hello, SpringMVC.";
    }
}

可以看到这是一个再简单不过的 Java 类了,没有对任何东西产生依赖。而我们前面提到,Annotation 实际上对代码本身没有任何影响,因此哪怕加入了诸多 Annotation,这个类本身零依赖的这种轻量性依然不会发生变化。而依赖越轻量的代码,越容易解耦,在架构上也越清晰,越灵活。这也是 POJO 这个概念想要表达的。

# 2.7 JavaBean

又是一个听起来很可爱,但是好像又什么都没说的 Java 概念。JavaBean 实际上是对于用 Java 编写数据 Model 时的一种编程惯例,具体的要求是:

  1. 有无参的构造器
  2. 所有的成员都是 private,对外暴露 getter 和 setter
  3. 实现 Serializable

后面 JavaBean 这个概念也逐渐的泛化,不再局限于 Model 类,出现了所谓的业务 Bean 等,对于 Serializable 的要求也显得可有可无。还是拿我们的 Controller 类举例子,它不仅仅是一个 POJO,也是一个 JavaBean。

后面我们会看到,Spring 把很多东西包括自己的一些组件也都称为 Bean,这个称呼可以看成是对 JavaBean 的进一步泛化。

# 2.8 SpringMVC Magic

首先 SpringMVC 需要知道我们的 Controller 是什么。前面我们在配置文件里标明了我们的 Controller 类是什么。

在 Controller 类中,对于请求的分发处理,@RequestMapping 的作用很明显。在初始化 Controller 的时候,SpringMVC 把 @RequestMapping 注解当中的信息进行处理,之后就可以根据这些信息,把请求分发到对应的 Controller 当中的对应方法里。

@ResponseBody 表明函数的返回值应该被用作 HTTP 返回的 body 处理。SpringMVC 用我们返回的字符串生成 HTTP 响应,返回给了客户端。

这就是 SpringMVC 大体的工作流程,可以看到 SpringMVC 通过 Annotation 以一种低侵入性的方式,提供了一套简洁好用的 Web 开发的 API。

更新时间: 4/27/2020, 6:06:56 PM