走进JavaWeb技术世界2JSP与Servlet的曾经与现在

132次阅读

共计 5790 个字符,预计需要花费 15 分钟才能阅读完成。

本系列文章将整理到我在 GitHub 上的《Java 面试指南》仓库,更多精彩内容请到我的仓库里查看

https://github.com/h2pl/Java-…

喜欢的话麻烦点下 Star 哈

文章首发于我的个人博客:

www.how2playlife.com

本文是微信公众号【Java 技术江湖】的《走进 JavaWeb 技术世界》其中一篇,本文部分内容来源于网络,为了把本文主题讲得清晰透彻,也整合了很多我认为不错的技术博客内容,引用其中了一些比较好的博客文章,如有侵权,请联系作者。

该系列博文会告诉你如何从入门到进阶,从 servlet 到框架,从 ssm 再到 SpringBoot,一步步地学习 JavaWeb 基础知识,并上手进行实战,接着了解 JavaWeb 项目中经常要使用的技术和组件,包括日志组件、Maven、Junit,等等内容,以便让你更完整地了解整个 JavaWeb 技术体系,形成自己的知识框架。为了更好地总结和检验你的学习成果,本系列文章也会提供每个知识点对应的面试题以及参考答案。

如果对本系列文章有什么建议,或者是有什么疑问的话,也可以关注公众号【Java 技术江湖】联系作者,欢迎你参与本系列博文的创作和修订。

<!– more –>

jsp 作为 Servlet 技术的扩展,经常会有人将 jsp 和 Servlet 搞混。本文,将为大家带来 servlet 和 jsp 的区别,希望对大家有所帮助。

servlet 和 jsp 的区别

1、Servlet 在 Java 代码中可以通过 HttpServletResponse 对象动态输出 HTML 内容。

2、JSP 是在静态 HTML 内容中嵌入 Java 代码,然后 Java 代码在被动态执行后生成 HTML 内容。

servlet 和 jsp 各自的特点

1、Servlet 虽然能够很好地组织业务逻辑代码,但是在 Java 源文件中,因为是通过字符串拼接的方式生成动态 HTML 内容,这样就容易导致代码维护困难、可读性差。

2、JSP 虽然规避了 Servlet 在生成 HTML 内容方面的劣势,但是在 HTML 中混入大量、复杂的业务逻辑。

通过 MVC 双剑合璧

JSP 和 Servlet 都有自身的适用环境,那么有没有什么办法能够让它们发挥各自的优势呢?答案是肯有的,MVC 模式就能够完美解决这一问题。

MVC 模式,是 Model-View-Controller 的简称,是软件工程中的一种软件架构模式,分为三个基本部分,分别是:模型(Model)、视图(View)和控制器(Controller):

Controller——负责转发请求,对请求进行处理

View——负责界面显示

Model——业务功能编写(例如算法实现)、数据库设计以及数据存取操作实现

在 JSP/Servlet 开发的软件系统中,这三个部分的描述如下所示:

1、Web 浏览器发送 HTTP 请求到服务端,然后被 Controller(Servlet)获取并进行处理(例如参数解析、请求转发)

2、Controller(Servlet)调用核心业务逻辑——Model 部分,获得结果

3、Controller(Servlet)将逻辑处理结果交给 View(JSP),动态输出 HTML 内容

4、动态生成的 HTML 内容返回到浏览器显示

MVC 模式在 Web 开发中有很大的优势,它完美规避了 JSP 与 Servlet 各自的缺点,让 Servlet 只负责业务逻辑部分,而不会生成 HTML 代码;同时 JSP 中也不会充斥着大量的业务代码,这样能大提高了代码的可读性和可维护性。

JavaWeb 基础知识

一、Servlet 是什么?

Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。

使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。

Java Servlet 通常情况下与使用 CGI(Common Gateway Interface,公共网关接口)实现的程序可以达到异曲同工的效果。但是相比于 CGI,Servlet 有以下几点优势:

  • 1、性能明显更好。
  • 2、Servlet 在 Web 服务器的地址空间内执行。这样它就没有必要再创建一个单独的进程来处理每个客户端请求。
  • 3、Servlet 是独立于平台的,因为它们是用 Java 编写的。
  • 4、服务器上的 Java 安全管理器执行了一系列限制,以保护服务器计算机上的资源。因此,Servlet 是可信的。
  • 5、Java 类库的全部功能对 Servlet 来说都是可用的。它可以通过 sockets 和 RMI 机制与 applets、数据库或其他软件进行交互。

二、Servlet 的生命周期

Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:

  • 1、Servlet 通过调用 init () 方法进行初始化。
  • 2、Servlet 调用 service() 方法来处理客户端的请求。
  • 3、Servlet 通过调用 destroy() 方法终止(结束)。
  • 4、最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。

init() 方法

init 方法被设计成只调用一次。它在第一次创建 Servlet 时被调用,在后续每次用户请求时不再调用。因此,它是用于一次性初始化,就像 Applet 的 init 方法一样。

Servlet 创建于用户第一次调用对应于该 Servlet 的 URL 时,但是您也可以指定 Servlet 在服务器第一次启动时被加载。

service() 方法

service() 方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service() 方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。

每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service() 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet、doPost、doPut,doDelete 等方法。

 destroy() 方法

destroy() 方法只会被调用一次,在 Servlet 生命周期结束时被调用。destroy() 方法可以让您的 Servlet 关闭数据库连接、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。

在调用 destroy() 方法之后,servlet 对象被标记为垃圾回收。

 示例

执行后:

以后继续请求时:

可见,就绪请求时只有 service()方法执行!

相关面试题

怎样理解 Servlet 的单实例多线程?**

不同的用户同时对同一个业务(如注册)发出请求,那这个时候容器里产生的有是几个 servlet 实例呢?

答案是:只有一个 servlet 实例。一个 servlet 是在第一次被访问时加载到内存并实例化的。同样的业务请求共享一个 servlet 实例。不同的业务请求一般对应不同的 servlet。

由于 Servlet/JSP 默认是以多线程模式执行的,所以,在编写代码时需要非常细致地考虑多线程的安全性问题。

JSP 的中存在的多线程问题:

当客户端第一次请求某一个 JSP 文件时,服务端把该 JSP 编译成一个 CLASS 文件,并创建一个该类的实例,然后创建一个线程处理 CLIENT 端的请求。如果有多个客户端同时请求该 JSP 文件,则服务端会创建多个线程。每个客户端请求对应一个线程。以多线程方式执行可大大降低对系统的资源需求, 提高系统的并发量及响应时间。

对 JSP 中可能用的的变量说明如下:

实例变量 : 实例变量是在堆中分配的, 并被属于该实例的所有线程共享,所以 不是线程安全的。

JSP 系统提供的 8 个类变量

JSP 中用到的OUT,REQUEST,RESPONSE,SESSION,CONFIG,PAGE,PAGECONXT 是线程安全的(因为每个线程对应的 request,respone 对象都是不一样的,不存在共享问题),APPLICATION 在整个系统内被使用, 所以不是线程安全的。

局部变量 : 局部变量在堆栈中分配, 因为每个线程都有它自己的堆栈空间, 所以 是线程安全的

静态类 : 静态类不用被实例化, 就可直接使用, 也 不是线程安全的

外部资源: 在程序中可能会有多个线程或进程同时操作同一个资源(如: 多个线程或进程同时对一个文件进行写操作). 此时也要注意同步问题.

Servlet 单实例多线程机制:

Servlet 采用多线程来处理多个请求同时访问。servlet 依赖于一个线程池来服务请求。线程池实际上是一系列的工作者线程集合。Servlet 使用一个调度线程来管理工作者线程。

当容器收到一个 Servlet 请求,调度线程从线程池中选出一个工作者线程, 将请求传递给该工作者线程,然后由该线程来执行 Servlet 的 service 方法。

当这个线程正在执行的时候, 容器收到另外一个请求, 调度线程同样从线程池中选出另一个工作者线程来服务新的请求, 容器并不关心这个请求是否访问的是同一个 Servlet. 当容器同时收到对同一个 Servlet 的多个请求的时候,那么这个 Servlet 的 service()方法将在多线程中并发执行。

Servlet 容器默认采用单实例多线程的方式来处理请求,这样减少产生 Servlet 实例的开销,提升了对请求的响应时间,对于 Tomcat 可以在 server.xml 中通过 <Connector> 元素设置线程池中线程的数目。

如何开发线程安全的 Servlet

1、实现 SingleThreadModel 接口

该接口指定了系统如何处理对同一个 Servlet 的调用。如果一个 Servlet 被这个接口指定, 那么在这个 Servlet 中的 service 方法将不会有两个线程被同时执行,当然也就不存在线程安全的问题。这种方法只要将前面的 Concurrent Test 类的类头定义更改为:

<pre>Public class Concurrent Test extends HttpServlet implements SingleThreadModel {
…………
} </pre>

同步对共享数据的操作

使用 synchronized 关键字能保证一次只有一个线程可以访问被保护的区段

避免使用实例变量

本实例中的线程安全问题是由实例变量造成的,只要在 Servlet 里面的任何方法里面都不使用实例变量,那么该 Servlet 就是线程安全的。

1) Struts2 的 Action 是原型,非单实例的;会对每一个请求, 产生一个 Action 的实例来处理

Struts1 Action 是单实例的

mvc 的 controller 也是如此。因此开发时要求必须是线程安全的,因为仅有 Action 的一个实例来处理所有的请求。单例策略限制了 Struts1 Action 能作的事,并且要在开发时特别小心。Action 资源必须是线程安全的或同步的。

2) Struts1 的 Action,Spring 的 Ioc 容器管理的 bean 默认是单实例的.

Spring 的 Ioc 容器管理的 bean 默认是单实例的。

Struts2 Action 对象为每一个请求产生一个实例,因此没有线程安全问题。(实际上,servlet 容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃圾回收问题)。

当 Spring 管理 Struts2 的 Action 时,bean 默认是单实例的,可以通过配置参数将其设置为原型。(scope=”prototype)

五、servlet 与 jsp 的区别

1.jsp 经编译后就变成了 Servlet.(JSP 的本质就是 Servlet,JVM 只能识别 java 的类,不能识别 JSP 的代码,Web 容器将 JSP 的代码编译成 JVM 能够识别的 java 类)

2.jsp 更擅长表现于页面显示,servlet 更擅长于逻辑控制.

3.Servlet 中没有内置对象,内置对象都是必须通过 HttpServletRequest 对象,HttpServletResponse 对象以及 HttpServlet 对象得到.Jsp 是 Servlet 的一种简化,使用 Jsp 只需要完成程序员需要输出到客户端的内容,Jsp 中的 Java 脚本如何镶嵌到一个类中,由 Jsp 容器完成。而 Servlet 则是个完整的 Java 类,这个类的 Service 方法用于生成对客户端的响应。

4. 对于静态 HTML 标签,Servlet 都必须使用页面输出流逐行输出

参考文章

https://www.w3cschool.cn/serv…
https://blog.csdn.net/qq_1978…
https://blog.csdn.net/qiuhuan…
https://blog.csdn.net/zt15732…
https://blog.csdn.net/android…

微信公众号

个人公众号:黄小斜

黄小斜是跨考软件工程的 985 硕士,自学 Java 两年,拿到了 BAT 等近十家大厂 offer,从技术小白成长为阿里工程师。

作者专注于 JAVA 后端技术栈,热衷于分享程序员干货、学习经验、求职心得和程序人生,目前黄小斜的 CSDN 博客有百万 + 访问量,知乎粉丝 2W+,全网已有 10W+ 读者。

黄小斜是一个斜杠青年,坚持学习和写作,相信终身学习的力量,希望和更多的程序员交朋友,一起进步和成长!

原创电子书:
关注公众号【黄小斜】后回复【原创电子书】即可领取我原创的电子书《菜鸟程序员修炼手册:从技术小白到阿里巴巴 Java 工程师》

程序员 3T 技术学习资源: 一些程序员学习技术的资源大礼包,关注公众号后,后台回复关键字 “资料” 即可免费无套路获取。

考研复习资料:
计算机考研大礼包,都是我自己考研复习时用的一些复习资料, 包括公共课和专业的复习视频,这里也推荐给大家,关注公众号后,后台回复关键字 “考研” 即可免费获取。

技术公众号:Java 技术江湖

如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号【Java 技术江湖】一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM、SpringBoot、MySQL、分布式、中间件、集群、Linux、网络、多线程,偶尔讲点 Docker、ELK,同时也分享技术干货和学习经验,致力于 Java 全栈开发!

Java 工程师必备学习资源: 一些 Java 工程师常用学习资源,关注公众号后,后台回复关键字 “Java” 即可免费无套路获取。

正文完
 0

走进JavaWeb技术世界2JSP与Servlet的曾经与现在

132次阅读

共计 27197 个字符,预计需要花费 68 分钟才能阅读完成。

微信公众号【黄小斜】大厂程序员,互联网行业新知,终身学习践行者。关注后回复「Java」、「Python」、「C++」、「大数据」、「机器学习」、「算法」、「AI」、「Android」、「前端」、「iOS」、「考研」、「BAT」、「校招」、「笔试」、「面试」、「面经」、「计算机基础」、「LeetCode」等关键字可以获取对应的免费学习资料。

                         

    转自:微信公众号 码农翻身

    这个问题来自于 QQ 网友,一句两句说不清楚,索性写个文章。

    我刚开始做 Web 开发的时候,根本没有前端,后端之说。

    原因很简单,那个时候服务器端的代码就是一切:接受浏览器的请求,实现业务逻辑,访问数据库,用 JSP 生成 HTML,然后发送给浏览器。

    即使后来 Javascript 在浏览器中添加了一些 AJAX 的效果,那也是锦上添花,绝对不敢造次。因为页面的 HTML 主要还是用所谓“套模板”的方式生成:美工生成 HTML 模板,程序员用 JSP,Veloctiy,FreeMaker 等技术把动态的内容添加上去,仅此而已。

    那个时候最流行的图是这个样子:

    

    在最初的 J2EE 体系中,这个表示层可不仅仅是浏览器中运行的页面,还包括 Java 写的桌面端,只是 Java 在桌面端太不争气,没有发展起来。

    每个程序员都是所谓“全栈”工程师,不仅要搞定 HTML, JavaScript, CSS,还要实现业务逻辑,编写访问数据库的代码。等到部署的时候,就把所有的代码打成一个 WAR 包,往 Tomcat 指定的目录一扔,测试一下没问题,收工回家!

    不差钱的公司会把程序部署到 Weblogic,Websphere 这样的应用服务器中,还会用上高大上的 EJB。

    虽然看起来生活“简单”又“惬意”,但实际上也需要实现那些多变的、不讲逻辑的业务需求,苦逼的本质并没有改变。

    1 前后端的分离

    随着大家对浏览器页面的视觉和交互要求越来越高,“套模板”的方式渐渐无法满足要求,这个所谓的表示层慢慢地迁移到浏览器当中去了,一大批像 Angular, ReactJS 之类的框架崛起,前后端分离了!

    后端的工程师只负责提供接口和数据,专注于业务逻辑的实现,前端取到数据后在浏览器中展示,各司其职。

    像 Java 这样的语言很适合去实现复杂的业务逻辑,尤其是一些 MIS 系统,行业软件如税务、电力、烟草、金融,通信等等。所以剥离表示层,只做后端挺合适的。

    但是如果仅仅是实现业务逻辑,那后端也不会需要这么多技术了,搞定 SSH/SSM 就行了。

    2 后端技术

    互联网,尤其是移动互联网开始兴起以后,海量的用户呼啸而来,一个单机部署的小小 War 包肯定是撑不住了,必须得做分布式。

    原来的单个 Tomcat 得变成 Tomcat 的集群,前边弄个 Web 服务器做请求的负载均衡,不仅如此,还得考虑状态问题,session 的一致性。

(老刘注:参见文章《小白科普:分布式和集群》)

    业务越来越复杂,我们不得不把某些业务放到一个机器(或集群)上,把另外一部分业务放到另外一个机器(或集群)上,虽然系统的计算能力,处理能力大大增强,但是这些系统之间的通信就变成了头疼的问题,消息队列(MQ),RPC 框架(如 Dubbo)应运而生,为了提高通信效率,各种序列化的工具 (如 Protobuf) 也争先空后地问世。

    单个数据库也撑不住了,那就做数据库的读写分离,如果还不行,就做分库和分表,把原有的数据库垂直地切一切,或者水平地切一切,但不管怎么切,都会让应用程序的访问非常麻烦,因为数据要跨库做 Join/ 排序,还需要事务,为了解决这个问题,又有各种各样“数据访问中间件”的工具和产品诞生。

    为了最大程度地提高性能,缓存肯定少不了,可以在本机做缓存(如 Ehcache),也可以做分布式缓存(如 Redis),如何搞数据分片,数据迁移,失效转移,这又是一个超级大的主题了。

    互联网用户喜欢上传图片和文件,还得搞一个分布式的文件系统(如 FastDFS),要求高可用,高可靠。

    数据量大了,搜索的需求就自然而然地浮出水面,你得弄一个支持全文索引的搜索引擎 (如 Elasticsearch ,Solr) 出来。

    林子大了,什么鸟都有,必须得考虑安全,数据的加密 / 解密,签名、证书,防止 SQL 注入,XSS/CSRF 等各种攻击。

    3“大后端”

    前面提到了这么多的系统,还都是分布式的,每次上线,运维的同学说:把这么多系统协调好,把老子都累死了。

    得把持续集成做好,能自动化地部署,自动化测试(其实前端也是如此),后来出现了一个革命化的技术 docker,能够让开发、测试、生成环境保持一致,系统原来只是在环境(如 Ngnix, JVM,Tomcat,MySQL 等)上部署代码,现在把代码和环境一并打包,运维的工作一下子就简化了。

    公司自己购买服务器比较贵,维护也很麻烦,又难于弹性地增长,那就搞点虚拟的服务器吧,硬盘、内存都可以动态扩展(反正是虚拟的),访问量大的时候多用点,没啥访问量了就释放一点,按需分配,很方便,这就是云计算的一个场景。

    随着时间的推移,各个公司和系统收集的数据越来越多,都堆成一座大山了,难道就放在那里白白地浪费硬盘空间吗?

    有人就惊奇地发现,咦,我们利用这些数据搞点事情啊,比如把数据好好分析一下,预测一下这个用户的购买 / 阅读 / 浏览习惯,给他推荐一点东西嘛。

    可是这么多数据,用传统的方式计算好几天甚至好几个月才能出个结果,到时候黄花菜都凉了,所以也得利用分布式的技术,想办法把计算分到各个计算机去,然后再把计算结果收回来,时势造英雄,Hadoop 及其生态系统就应运而生了。

    之前听说过一个大前端的概念,把移动端和网页端都归结为“前端”,我这里造个词“大后端”,把那些用户直接接触不到的、发生在服务器端的都归结进来。

    4 怎么学?

    现在无论是前端还是后端,技术领域多如牛毛,都严重地细分了,所以我认为真正的全栈工程师根本不存在,因为一个人精力有限,不可能搞定这么多技术领域,太难了。

    培训机构所说的“全栈”,我认为就是前后端还在拉拉扯扯,藕断丝连,没有彻底分离的时候的“全栈”工程师。

    那么问题来了,后端这么多东西,我该怎么学?

    之前写过一篇文章叫做《上天还是入地》,说了学习的广度和深度,在这里也是相通的。

    往深度挖掘,可以成为某个技术领域的专家,如搜索方面的专家、安全方面的专家,分布式文件的专家等等,不管是哪个领域,重点都不是学会使用某个工具和框架,而是保证你可以自己的知识和技术去搞定这个领域的顶尖问题。

    往广度发展,各个技术领域都要了解,对于某种需求,能够选取合适的软件和技术架构来实现它,把需求转化成合适的技术组件,让这些组件以合适的方式连接、部署、运行,这也需要持续地学习和不断的经验积累。

    最后,以一张漫画来结束吧!

    

(完)

    我为什么对后端编程情有独钟?

    转自:老刘 码农翻身 2017-05-10

    这几年前端很热闹,发展也很快,Angular, React, Vue … 等各种各样的新技术层出不穷,并且不断地抢后端的饭碗。比如说著名的 Model – View -Controller , 原来前端只负责 View 层,展示数据,现在前后端分离,前端把控制层 Controller 也给抢走了,可怜的后端程序猿只剩下 RESTful 服务提供的数据了,再加上 Node.js 趁火打劫,入侵服务器端,有木有危机感?

    但我还是对后端编程情有独钟,原因很简单,相比 GUI 编程,我更喜欢 API 编程,有点费解?听我慢慢道来。

    1  单机时代的 GUI

    最早的时候我其实也是挺喜欢 GUI 编程的,谁愿意只在命令行下折腾呢?

    作为“初恋”的 C 语言,虽然功能强大、效率高,但是只能在命令行窗口写点小程序,很无趣。

    后来遇到 VB, 不由的眼前一亮:哇塞,还能这么写程序!创建一个窗体,把各种各样的控件拖动上去,摆好位置,设好属性,然后双击,在 onClick 方法中写下代码就 ok 了。

    号称 VB 杀手的 Delphi 也类似,都是所谓的 RAD(Rapid Application Development)。

    当时的控件多如牛毛,上了年纪的程序员应该还记得 CSDN 出的《程序员大本营》,里边有张光盘,全是程序猿们开发的 VB 控件,你想发送邮件,拖动一个不可见的 SMTP 控件过来,设定邮箱服务器和端口,直接就可以发信,非常简单。你想用一个定时器功能,没问题,那里有个定时器控件,可以直接使用,这才是真正的组件化开发。

    由于开发出来的应用只能在 PC 上,在 Windows 上运行,不会出现如今手机端各种各样的适配问题,垄断在某些时候也是好处啊。

    虽然这些桌面应用程序不像 Web 页面那样美轮美奂,但对于我这个缺乏艺术细胞的人来说,这是 GUI 编程的黄金时代。

    2 Web GUI

    好景不长,技术变迁很快,Web 时代来临了。

    于是开始学 HTML, CSS, Javascript,忙着折腾个人主页,做了没多久就意识到,用 HTML 产生页面布局怎么这么麻烦,当时 CSS 还不普及 , 页面布局全靠一个表格套一个表格来做,如果没有 Dreamweaver, Frontpage 这样的软件帮忙,这些 <tr><td> 标签绝对会把人淹死。

    光有布局还不行,还得弄图片,调颜色,我在大学还学了 photoshop,想着自己设计。后来看到一本书上的例子,在 photoshop 中不使用任何外界图片,从零开始做出一个可口可乐易拉罐出来,那光影效果当时就把我震撼了,立刻意识到自己不是搞艺术的这块料,还是老老实实的回去写程序去吧。

    个人主页怎么办?我就 Copy 了一个别人的网站,改了改,变成了这个样子(图片没有显示):

    

    忘了 Copy 谁的网站了,向原作者表示歉意,当时是学生,知识产权意识不够,搁现在肯定得掏钱去买。

    现在一般的开发团队都配有美工,可以设计界面,设计完还能“切图”,形成漂亮的 html+css 的静态页面,接下来的问题就是怎么把静态的网页变成动态的网页,这就八仙过海,各显神通了。

    传统的方式就是在服务器端完成,利用各种各样的模板技术,在静态页面中加上动态内容,直接生成 HTML UI 元素。

    最近流行的就是让浏览器来负责,通过 js 调用后端 API,把数据变成 HTML UI 元素后展示出来。

    不管是那种方式,CSS 都是不可或缺的。因为它控制了页面的布局结构,又是布局,快逃!

    3 Java GUI

    上了 Java 的贼船以后,也做了一些 GUI 的工作,相比于 VB/Delphi 拖放控件的便捷,用 Java 写界面简直就是地狱!

    虽然也有图形化的界面编辑器,也可以拖放控件,但是自动生成的代码那叫一个惨不忍睹。更悲催的是,稍微手工改动一下,那个界面编辑器就可能不认了。绝大多数情况下还是直接写代码来生成界面。(再次大声疾呼:不要再 Swing 和 AWT 上浪费精力,用的极少。)

    这里必须说一下 Java 和 VB 在界面编程的区别,Java 的界面全是用代码来生成的,就是说你需要写代码创建一个按钮,写代码把这个按钮放到什么地方去,所以即使是 GUI 程序,最终的表现形式也只是 Java 文件而已。

    VB 则不同,它专门有个.frm 文件,里边存储所有界面控件和布局的信息,最终也需要把.frm 打包发布。所以在编辑界面这一点,VB 和 Dephi 是非常方便的。

    程序员们这么痛苦,那些大牛肯定会来解救我们的,比方说能不能用 XML 来描述界面啊,在 XML 中把各个控件及其布局关系都给描述好,由系统读取,创建界面,就像原来的.frm 文件一样。Android 不就是这么干的吗?

    但是 XML 文件读起来也够痛苦的,为了灵活性,这个 XML 文件还不能隐藏起来,有时候还要手工去改,改完还不容易看到真正的效果,唉,想想就头大。

    更不用说 Android 的适配问题了,不同屏幕尺寸,不同的分辨率,不同的像素密度给程序员带来了极大的工作量。

    

(每个矩形代表一种设备)

    4  后端编程

    啰嗦了这么多,其实就想表达一个中心思想:我是有点害怕 GUI 编程。而 Web 前端和 App 前端都是在和 GUI 打交道。

    我甚至想,这可能和内向的性格有关系,擅长和机器打交道,不擅长和人打交道。前端需要琢磨用户的心理、使用习惯、用户体验,这不是我的优势。

    在软件编程领域,与其费力不讨好的补上短板,不如把自己的长处发挥到极致。

    既然如此,那就呆在后端编程吧,这里没有 GUI, 只有 API。悄悄地躲在电脑的背后,给 Web 前端和 App 前端提供服务,让他们调用。

    有人会说:前端把 Controller 和 View 都拿走了,后端就是个 API 的提供者,能折腾啥啊。

    别小看后端编程,后端是非常有技术含量的,像什么高并发、缓存、负载均衡、分布式、消息队列、安全、搜索、数据复制 …. 每个方向都值得静下心来去深挖。

    不管前端技术怎么变化,作为提供服务的后端总是要存在的,这是一大优势。

    后端编程还有一个优势就是相对稳定,比起大爆炸的前端,后端技术的变化要缓慢一些,心思不会那么浮躁,有很多知识可以慢慢的沉淀。

    对于那些不喜欢做 GUI 的同学,不妨考虑下后端编程。

    J2EE 到底是何方神圣

    一、J2EE 历史

    前言:

    昨天下午有同学问我 Java EE 是干什么用的,能开发什么系统,我在 QQ 中敲了很多字,掰扯了半天,终于给他整明白了。

    我突然意识在其实很多初学者对 Java EE 的来龙去脉并去清楚,大家并不知道为什么会出现这个技术,要解决什么问题。所以就写了这篇文章介绍下 Java EE 的历史。

    ———————————————————————–

    先把时间扯的远一点,94 年我上高中的时候,见过亲戚家有过电脑,很好奇,虽然上面都是一些单机桌面程序,根本上不了网,但是一个小小的扫雷程序就足以吸引很多眼球了。

    后来上了大学,接触电脑越来越多,局域网已经普及,互联网开始抬头,这时候 C /S (Client-Server)结构的程序开始出现了,例如 QQ, 游戏,还有著名的 PowerBuilder 开发的 MIS(管理信息系统),都是典型的客户端 - 服务器结构, 程序运行在个人的电脑上,和服务器通信。

    C/S 软件界面可以整的很漂亮,用户体验很好,但是缺点也很明显,就是用户必须要安装客户端,并且需要升级客户端来用新功能,这样就带来两个问题

    (1) 服务器端发生变化,一定要能兼容很多客户端版本,要不然有的客户端软件就运行不了了,因为客户不一定升级。

    (2) 可能会出现 DLL 地狱问题 — 自己百度下啥是 DLL 地狱。

    再后来 Web 速度快了,带宽也够了,大家发现,Web 页面也能做的很漂亮了,把程序放到服务器端,用浏览器访问多好,用户也不用安装,所有功能都是即时更新。

    但是 html 只是静态的,怎么才能变成动态的,可交互的呢?

    CGI 技术出现了,CGI 允许 web 服务器调用外部的程序,并把结果输出到 Web 浏览器,这样就能根据用户的操作生产不同的动态页面了。

    在我读大学的年代,CGI 最常见的就是实现一个计数器,看着自己的主页访问量慢慢的增长,那种感觉是很爽的。

    当然,使用 CGI 最苦逼的就是需要用程序输出 html , 那可是整个网页的 html , 不是其中的一个片段!

    换句话说,程序员只能在代码里写 html, 而不能在 html 里写代码。你可以想象一下用 C 语言或者 Perl 写 CGI 脚本的程序员那种咬牙切齿的感觉。

    举个例子,通过浏览器提交了用户名 / 密码, cgi 会这么做:

    if (name==”liuxin” and password == “xxxx”) {

    println (“<html>”);

    println (“<head>”);

    println (“<title> 欢迎 </title>”);

    println (“</title>”);

    println(“<body>”)

    println(“<table>”)

    println(“<tr><td>”) ….. 我实在是写不下去了,太崩溃了,这是伪码,不要和我较劲 ……………

    println(“</td></tr>”)

    println(“</table>”)

    println(“</body>”)

    print(“</html>”)

    }

    这个时候我们赖以糊口的 Java 在干嘛?

    Java 是 Sun 发明的,当然 Sun 现在已经被 Oracle 收购了。

    Java 其实是依靠 Applet , 依靠互联网才起家发达的,大家在简陋的页面上突然看到当时极为炫目的 Applet 小动画,立刻就震惊了。

    但悲剧的是大家发现 Applet 除了用来演示以外,似乎没有找到真正的用武之地。

    浏览器还必须得装个 java 插件,后来微软为了阻止 Java 还在自己浏览器 IE 中使坏 :-),这个插件运行一直不太稳定。

    Java 看到 CGI 技术发展的不错,自己也搞一个类似的吧,这就是 Servlet , 由于提供了一个 request, response, session 等支持, 用起来比 CGI 方便多了。

    但是输出 html 这一块没有任何改进,还得程序员一句一句的输出 html.

    06 年我进 IBM 的时候发现有个项目是在用 servlet 输出页面 html , 完全没用 jsp,我也就见怪不怪了,这肯定是个 90 年代的遗留应用。

    最后大家都受不了这种极为痛苦的输出方式,于是 Sun 就弄了个 JSP , 微软也不甘示弱,出了个 ASP。

    这下子 Web 开发出现了跃进,因为不管是 JSP 还是 ASP, 都是所谓的 Server Page , 也就是说程序员终于可以把逻辑代码和 html 混在一起了!在也不用一行一行的输出 html 了。

    当然现在老师教导你,jsp 中不要有逻辑代码,但在当时,这可是了不起的突破。

    我们可以先让美工把页面设计好,然后把程序嵌入进去就行了。

    再后来出现了 Struts, 使用 MVC 解决了职责划分问题,Web 应用迈向了新的台阶,开始飞速发展,对于这种应用,我们称为 B/S 结构, 即 Browser(浏览器)-Server (服务器)。

    C/ S 结构的程序当然不会消亡,因为像聊天,视频,游戏等对性能,界面,用户体验要求很高,天然适合桌面程序实现。

    为了支持更大,更复杂的应用开发,微软为 ASP 添加了实现业务逻辑的 COM, COM+,访问数据库的 ADO 等技术。

    而 Sun 和 Java 社区有更大的野心,他们提出了一套更大的,不同于传统应用开发的架构,专门用于满足企业级开发的需求。

    这些需求包括数据库,邮件,消息,事务处理,Java 对这些通用的需求做了抽象,形成了一些规范和标准,除了 Servelt 和 JSP , 还有 EJB, JMS , JDBC 等等。

    这些东西,Sun 把他们称为 J2EE。

    为啥不是 Java EE ? 那个 2 是怎么回事?

    这完全是一种市场策略,原来 Java 的版本是 1.1,从 1.2 开始,Sun 称之为为 Java 2 平台,以便告诉大家,这是一个突破性的技术平台。实际上也确实有所突破,Java 被分成了 J2SE (标准版),J2EE(企业版) 和 J2ME(移动版),当然移动版在手机上一直没有发展起来,直到 Android 的出现 – 这是后话了。

    到了 2005 年,Sun 又取消了那个“2”,于是就变成了 Java SE, Java EE, Java ME 了。

    J2EE 需要运行在一个叫应用服务器的东西里,这就是 Weblogic, websphere , jboss, 也称为应用中间件。

    J2EE 发展的非常迅猛,迅速统治了 Web 应用开发市场,微软的 ASP 只能偏居一隅了,后来推出.NET 才算扳回一城。

    我们走了漫长的路,终于来到你的面前,现在你知道 Java EE 是干啥的了吧 :-)

    在后来的故事估计很多人都听过了,Java EE 远远没有宣传的那么美好,EJB,尤其是 Entity Bean 极为难用,对业务代码侵入性极强,在大家想抛弃而又没有替代品的时候,有一位大牛 Rod Johnson 如约而至,他说我们不要这种臃肿,低效,脱离现实的 J2EE, 我们要灵活,轻便,轻量级的框架,这就是 Spring,我们就有了依赖注入,AOP….

    有位叫 Gavin King 的澳大利亚小伙子也是在忍受不了 EJB 的 O /R Mapping , 自己整了一个 Hibernate。

    再加上更早出现的 Struts, 我们 Java 程序员终于过起了幸福的 SSH 生活。

    二、J2EE 入门指南

    前言

    这是写给零基础小白的一系列文章。

    为啥叫生存指南呢,因为 Java 发展了 20 年,现在已经不仅仅是一个单纯的语言了,而是一套完整的生态系统,其中的术语像 HTML, XML, CSS, Javascript , AJAX, JQuery,Prototype, HTTP, Tomcat, JBoss, Nginx , Memecached , Redis, MVC ,Servlet, JSP, Struts, Hibernate, myBatis , Spring, JFinal, MySql, JDBC, EJB, JMS, Swing , AWT, Reflection, OSGi… 铺面而来,搞的你头晕脑胀,无所适从,很容易就 Lost 了。

    所以写这个文章的目的就是帮助小白能在 Java 的汪洋大海里生存,时不时的能冒出水面喘口气,看看空中的生存指南,把握自己的方向继续向前。

    ————————————————————-

    先回答一个问题?为什么要学习 Java ?

    我想原因无非有这么几点

    1. 我周围的人都在学,所以我也学

    2. Java 好找工作啊,确实是,在中国,软件行业还处于模仿、学习美国鬼子的阶段,做系统级编程的不是没有,像 BAT 就会用到。不过绝大部分公司还都是搞应用级程序的开发,所以 Java, 尤其是 Java EE 工作机会是非常多的。

    3. Java 看起来挺简单的。

    Java 语言本身看起来确实挺简单的,不像 C 语言,一个指针就把你搞迷糊了;

    也不像 C ++,语法复杂而诡异,new 了一个对象以后还得记住 释放内存,确实是挺悲催的;

    Java 即使加上了面向对象(封装,继承,多态),也是简单的令人发指,不用上大学,高中生,甚至初中生都能看明白。

    可是你会发现学了基本的 Java 以后,除了能写个水仙花数,九九乘法表,还是啥也干不了,更别说月薪过万了。

    人家公司至少要求精通 SSH,AJAX,JQuery,CSS,mysql , 这条路走起来就不那么容易了。

    再看第二个问题:Java 到底能干什么?

    一句话,Java 最擅长的就是 Web 应用开发(通俗的讲就是网站开发),不善长桌面应用开发。

    你想想你开发一个 Java 桌面应用,还得让用户下载一个 Java 虚拟机,甚至要设置环境变量,一般人是搞不定的。此外 Java 内置的 Swing ,AWT 确实不怎么样,开发出来的界面距离操作系统原生界面差了很远,所以除了特殊情况,奉劝那些还在孜孜不倦的研究 Java 界面编程 (Swing, AWT) 的同学还是不要浪费精力了,不要硬逼着 Java 干他不擅长也不不愿意做的事情。

    所以咱们就聊聊 Java Web 开发中用到的那些技术和术语。

    先来说说 HTML, 咱们想象一个场景,互联网还没有出现,你是个球迷+程序员,电脑里有很多的记录足球的文件例如 足球.txt, 巴塞罗那.txt,曼联.txt …..

    其中足球.txt 中有一个词 ” 巴萨罗那 ”,为了方便,你想点这 4 个字就打开另外一个文件“巴赛罗那.txt”立刻就能看看球队的介绍,这该怎么办?

    你冥思苦想,终于顿悟了,可以这么干:定义一个协议 巴塞罗那,然后开发一个软件, 把所有的文本都管理起来,遇到像这样的东西,软件就认为这是一个链接,点击后就打开另外一个文件!

    这的确是个好主意,其实在 1989 年,万维网的发明人蒂姆·伯纳斯·李也是这么干的,你看你要是早出生 20 年,估计也是 WWW 的发明人了。

    加了链接以后,文本就不是不同的文本了,而升级为超文本 (Hypertext)了!

    但是如果你的“足球.txt”还有“广州恒大”几个字,但是广州恒大的介绍是在你哥们的电脑上,而他也想把他的文本能链接到你的电脑文件上,这怎么办?

    一个单机运行的软件就不行的,必须得有网络,有网络还不够,你得解决通信的问题。

    你就想:既然上面的文本是超文本,并且需要把你好哥们的文本传输到你的电脑上才能看,那通信方法就叫做超文本传输协议吧 HyperText Transfer Protocol,简称 Http。

    于是你又扩展上一个程序,不但把你的文本文件都管理起来,还允许通过网络访问,别人要想通过网络看你的文件,得发个这样的命令给你的软件:

    http://192.168.0.10/football/…。你的程序就会找到 football 目录下的 巴萨罗那.txt , 读出内容,发给对方, 并且给他说:200 成功

    如果找不到,软件就告诉他:404 对不起,找不到。

    如果你的软件在处理过程中出了错,软件就说:500  唉,服务器出错了。

    这个软件功能强大,专门在网络上别人服务,就叫网络服务器吧,可以起个名字叫 Apache。

    可是只看文字太不爽了,你还想看表格,列表,图片,甚至视频。干脆自己定义一个描述界面的语言吧,像这样:

    <table> — 表示表格

    <li> — 表示列表

    <image> — 表示图片。

    这些都称为标记(markup) , 所以现在你的超文本就更加丰富了,这个语言就叫做 Hyper Text Markup Language,简称为 HTML。

    原来的软件只能显示文本和处理链接,现在还需要能处理这些标签,遇到不同的标签,就显示相应的内容。

    现在你把你的文本全部用 html 改写了一遍,放到了 Apache 服务器中,你的哥们也把他的 html 放到了他的 Apache 服务器上,当然你们的 html 之间还保持着链接。然后你们就可以用新的软件对这些 html 进行浏览了,对了,可以把这个软件称为浏览器。

    由于方便,快捷,能发布图文并茂的信息,更关键的是可以把散布在全世界各个角落中的计算机连接在一起,HTML,HTTP, 网络服务器,浏览器 迅速普及,人们上网在也不用使用那些难用的 telnet , ftp 了。网络就变成了这样:

    

    下面的文字来源于百度百科:

    因为在互联网技术上的杰出贡献,伯纳斯·李被业界公认为“互联网之父”。他的发明改变了全球信息化的传统模式,带来了一个信息交流的全新时代。然而比他的发明更伟大的是,伯纳斯·李并没有像其他人那样为“WWW”申请专利或限制它的使用,而是无偿的向全世界开放。伯纳斯·李本来可以在金钱上与盖茨一比高低,但他的这一举措却为互联网的全球化普及翻开了里程碑式的篇章,让所有人都有机会接触到互联网,也圆了那些。com 公司创建者们的富翁梦。即便如此,伯纳斯·李仍然十分谦虚,总是以一种平静的口气回应:“我想,我没有发明互联网,我只是找到了一种更好的方法。”

    

    互联网之父 伯纳斯·李

    接上篇《给小白的 Java EE 生存指南》,你可以通过公共号的消息历史中查看。今天继续聊 Web 的事情。

    上次说到你发明了 html , http, 浏览器,web 服务器,这下子把整个世界的信息都链接成了一个了一个大网:world wide web

    可是你注意到一个问题没有,这些 html 都是静态的,也就是说你除了浏览你的网站和别人的网站之外,什么都做不成。用你发明的 HTTP 术语来讲,就是现在的互联网,只支持“GET”

    比如说,你看了哥们的网站,知道广州恒大夺得了 2015 亚冠冠军,想给他留个言表示一下兴奋之情,这就做不到了。

    这是不能令你满意的,对互联网做扩展吧

    先扩展 HTML,加上一堆新标签 像 <form> 了,了 什么 type=text, radio, textarea,checkbox。。。这样你就可以在 html 中支持输入各式各样的信息了。

    你还得扩展 HTTP 协议,引入一个“POST”这样可以把数据发到 Web 服务器。

    Web 服务器当然也得扩展,接收到 POST 过来的留言数据以后,肯定得想办法处理啊,怎么处理?

    无非就是新生成一个 html , 除了把原有的数据保留以外,还要把新的留言相关的数据“动态”的加上去,这就是所谓的动态页面。

    必须得有程序来办这件事情,你马上就面临两个问题:

    (1) 用什么语言来写,毕竟有那么多语言像 C, Perl , Python, 甚至 Java 都在虎视眈眈呢

    (2) 这个程序怎么和 Web 服务器交互

    解决方法也很简单,你弄了了一个叫做 Common Gateway Interface (简称 CGI) 的东西,定义了标准的输入 (STDIN) 和输出(STDOUT),所有的程序都可以从 STDIN 读入数据,处理后形成 html, 然后向 STDOUT 输出。

    这下子任何语言都可以写 cgi 程序了,网页也变成了“可交互式”的,整个互联网又向前推进了一大步,各种各样的商业网站如雨后春笋一般发展起来。

    (ps : 现在 CGI 技术已经过时了,ASP, JSP, PHP 等技术是主流。

    在 Java 的世界里,把 Apache ,Ngnix 这样的服务器称为静态内容服务器,主要处理像图片 / 文件件这样不变的,只读的静态资源,性能非常好;把 Tomcat, JBoss,Websphere, Weblogic 等称为动态内容服务器,主要产生动态内容。

    一般的设计会把 Apache/Ngnix 放的前边,接收所有的 Http 请求,如果是静态资源,就直接处理掉,如果是动态资源,就转发到 Tomcat/Jboss 去处理。)

    等等,还有个小问题,我的留言我能看到,别人的留言我也想看到改怎么办?很明显,每个人通过浏览器提交的留言都需要保存起来,在生成页面的时候动态的读取他们,形成 html。

    可以把所有的用户留言都存放到一个文件当中,读取文件形成 html 没有任何压力,但是你要非常小心处理同步的问题:你提交留言的时候,别人也在提交,可不能相互覆盖啊!

    这也是为什么 Web 程序都有一个数据库的原因,数据库帮我们解决了这些乱七八糟的同步问题,我们只需要向数据库发出 Select, Insert, Upate ,Delete 就好了。数据库的历史要比 WWW 久远的多,早在大机时代就出现了,现在已经发展的非常成熟,直接拿过来用就可以了。

    解决了用户留言的问题,你接下来要写一个网上售票的应用,让大家在网上买球票,买票的时候需要一个购物车,大家可以把票暂时放到购物车里。

    开发购物车的时候发现了你设计的 HTTP 一个非常严重的缺陷:没有状态,因为用户往购物车里加了一个球票,一刷新页面购物车就空了,里边的所有东西都丢失了。

    假设用户 A 在操作,用户 B 也在操作,你的 Apache 服务器实际上根本区分不出来谁是用户 A, 谁是用户 B, 只是看到了一个个毫无关联的 GET 和 POST。根本记录不下来同一个用户在过去一段时间内做了什么事情。

    你想改一下 HTTP 协议,可是悲催的是数以亿计的网站已经在用了,你想改都改不了了。

    于是你想了个办法,HTTP 协议不是有个 header 吗,在里边做做手脚:

    浏览器 A 第一次给服务器发送请求时,服务器通过 header 告诉它一个编号 number_a,浏览器 A 需要记住这个编号,然后下次请求的时候(以及后续所有请求的时候)也通过 header 把 number_a 发给服务器。这样服务器一看,奥,原来你是浏览器 A 啊,就可以把浏览器 A 相关的购物车数据从内存中取出来,形成 html 发回浏览器 A 了。

    浏览器 A 和服务器的这个通过这个编号来交互,这个过程就称为:session

    用了这一个小伎俩,这样服务器就把各个浏览器(各个用户)给区分开了。

    到目前为止,构建一个 Web 系统最基础的工作可以说已经完成了,我想起来高中时物理老师说的一句话:牛顿三定律出来以后,经典物理学的大厦已经建立起来了,后人的工作无非是刷刷墙,装饰装饰而已。

    对于互联网也是:HTTP + HTML + Web 服务器 + 数据库 就是 WWW 的基石,后续的工作都是为了提高生产率做的包装和装饰。

    最后简单做一个总结:其实发明创造并没有那么难,马云说过,哪里有抱怨,哪里就有机会,所以找一找现在你感觉最不爽的地方,也许能发明下一代互联网呢。

    前两篇文章《给小白的 Java EE 生存指南(1)》和《给小白的 Java EE 生存指南(2)》(注:回复关键字“小白”即可查看)基本上把 Web 编程所依赖的基础技术(HTTP,HTML, WEB 服务器,浏览器)的来龙去脉介绍完了,从这篇开始,正式开始进入应用程序的开发领域。

    其实 Web 应用程序开发也有个极为常见的技术:XML . 很多小白在问,为什么有 XML, 要 XML 干嘛?不是有 HTML 了吗?晕倒

    对一项技术我的风格是喜欢刨根问底,不但要知道 how, 还要知道 why , 了解了一个技术的成因,才能真正掌握。

    假设你用 Java 写了一个很棒的 Web 应用,这个程序可以在线购书,在互联网刚起步的时代这个应用火的一塌糊涂,后来有个出版社看到了机遇,就想和你搞合作:我这儿也有个 Web 应用,可以给你提供很多书籍的资源,你能不能开发个程序把这些书的信息读出来,放到你的网站上?

    这没啥难的,首先得定义一下你的应用和出版社的应用中间怎么进行数据交换,你要定义一个格式,像这样:

    [isbn| 书名 | 作者 | 简介 | 价格]

    例如:[978-7-229-03093-3| 三体 | 刘慈欣 | 中国最牛的科幻书 |38.00]

    数据虽然紧凑,但是每个字段是什么含义,不好理解,你想起了 HTML 的标签好像不错,不如学习 HTML 改进一下:

    <book>

    <isbn>978-7-229-03093-3</isbn>

    <name> 三体 </name>

    作者

    <introduction> 中国最牛的科幻书 </introduction>

    <price>38.00</price>

    </book>

    由于 HTML 的标签 <head>,<title>,<tr><td>…… 是有限的,而你的标签确实可以随意扩展的,想写什么写什么 所以你就把它称为 Extensible Markup Language, 简称 XML

    现在每个字段的含义很明确,人读起来也很爽了,但是毕竟是程序在处理出版社发过来的数据,万一他们的数据里少了一些重要字段该怎么办,能不能自动的检测出来?

    所以你需要设计一套校验规则,能让程序自动校验一段 xml 文本是不是你期望的,这个规则可以像这样:

    <!ELEMENT book (isbn, name, author, introduction, price)>

    <!ELEMENT price (#PCDATA)>

    <!ELEMENT name (#PCDATA)>

    <!ELEMENT isbn (#PCDATA)>

    <!ELEMENT introduction (#PCDATA)>

    <!ELEMENT author (#PCDATA)>

    其中第一行的意思是 xml 需要有个 book 标签(元素),它包含了几个子标签,并且这几个标签必须都得有,并且按次序出现。

    其他行表示每个标签都是文本就可以了。

    你把这个东西起名为文档类型定义 Document Type Definition, 简称 DTD。

    这样就不怕出版社使坏了,对他们发过来的数据, 在真正的处理之前,你写了个程序,调用用 DTD 一验证就知道是不是合法的,少了个字段什么的一下子就能查出来,巨爽。

    后来又有人发明了 DTD 的改进版 XML Schema,那就是后话了。

    慢慢的你就发现,XML 极为灵活,描述一个东西非常方便,除了应用之间交互数据之外,用来描述你的系统的配置信息也大有永无之地。

    原来你为了让代码有可移植性(说白了就是在别人的机器上安装时不用费那么大劲),把数据库的 ip , 用户名,密码 都写在了一个文本文件中,这样就可以只改配置而不用改动代码了

    ip=192.168.0.1

    name=test

    user=liuxin

    password=liuxin

    但是碰到复杂的,尤其是层次化的配置用文本文件就捉襟见肘了,例如:

    form1.name=login

    form1.class=com.test.login

    form1.property1.name=ok

    form1.property1.type=java.lang.String

    form1.property2.name=failure

    form1.property2.type=java.lang.String

    form2.name=logout

    form2.class=com.test.logout

    form2.property1.name=ok

    form2.property1.type=java.lang.String

    form2.property2.name=failure

    form2.property2.type=java.lang.String

    是不是看的头大?

    改成 xml 描述看看,是不是就容易理解多了:

    <form name=”login” >

    <property name=”ok” type=”java.lang.String” />

    <property name=”failure” type=”java.lang.String” />

    </form>

    <form name=”logout” >

    <property name=”ok” type=”java.lang.String” />

    <property name=”failure” type=”java.lang.String” />

    </form>

    其实不光是你,现在绝大多数 Java 应用程序的配置文件都是 xml , 已经成为事实的标准了。

    总结:XML 主要用于程序之间的数据交换,以及描述程序的配置信息。

    历史知识:

    早在 1969 年,IBM 公司就开发了一种文档描述语言 GML 用来解决不同系统中文档格式不同的问题, 这个语言在 1986 年演变成一个国际标准(ISO8879),并被称为 SGML,SGML 是很多大型组织,比如飞机、汽车公司和军队的文档标准,它是语言无关的、结构化的、可扩展的语言,这些特点使它在很多公司受到欢迎,被用来创建、处理和发布大量的文本信息。

    在 1989 年,在 CERN 欧洲粒子物理研究中心的研究人员开发了基于 SGML 的超文本版本,被称为 HTML。HTML 继承了 SGML 的许多重要的特点,比如结构化、实现独立和可描述性,但是同时它也存在很多缺陷:比如它只能使用固定的有限的标记,而且它只侧重于对内容的显示。

    同时随着 Web 上数据的增多,这些 HTML 存在的缺点就变的不可被忽略。W3C 提供了 HTML 的几个扩展用来解决这些问题,最后,它决定开发一个新的 SGML 的子集,称为 XML。

    本文是给小白的 Java EE 生存指南的第 4 篇,讲一下几乎 100%Java 开发人员都要用的 Tomcat。

    为什么有 Tomcat?其实需要从 Servlet 说起。

    记得《给小白的 Java EE 生存指南(2)》(回复“小白”查看) 提到的动态网页吗?常见的实现动态网页的技术就是 CGI。

    但是作为 Java 的发明人,Sun 肯定要搞一个超越 CGI 的技术出来,之前 Sun 通过 Applet 出了一个超级大风头,让整个世界一下子认识了 Java,不过很快发现悲催的 Applet 其实用途不大,眼看着互联网开始起势,一定要搭上千载难逢的快车啊。

    于是 Servlet 就应运而生了,Servlet 其实就是 Sun 为了让 Java 能实现动态的可交互的网页,从而进入 Web 编程的领域而定义的一套标准。

    这套标准说了:

    你想用 Java 开发动态网页,可以定义一个自己的 ”Servlet”(名字很怪,不知道怎么翻译), 但一定要是实现我的 HttpServlet 接口,然后重载 doGet(), doPost()等方法。

    用户从浏览器 GET 的时候,调用 doGet()方法,从浏览器向服务器发送表单数据的时候,调用 doPost()方法。

(参见《给小白的 Java EE 生存指南(1)》,回复“小白”查看)。

    如果你想访问用户从浏览器传递过来的参数,没问题,用 HttpServletRequest 对象就好了,里边有 getParameter(),getQueryString()方法。

    如果你处理完了,想向浏览器返回数据,用 HttpServletResponse 调用 getPrintWriter() 就可以输出数据了。

    如果你想实现一个购物车,需要 session, 很简单,从 HttpServletRequest 调用 getSession() 就好了。

    你写了一个 ”Servlet”,接下来要运行,你就发现没法通过 java 直接运行了,你需要一个能够运行 Servlet 的容器,这个容器 Sun 最早实现了一个,叫 Java Web Server, 1999 年捐给了 Apache Software foundation , 就改名叫 Tomcat。

    所以 Tomcat 就是一个 Servlet 容器,能接收用户从浏览器发来的请求,然后转发给 Servlet 处理,把处理完的响应数据发回浏览器。

    但是 Servlet 输出 html , 还是采用了老的 CGI 方式,是一句一句输出,所以,编写和修改 HTML 非常不方便。

    于是 Java Server Pages(JSP) 就来救急了,JSP 并没有增加任何本质上不能用 Servlet 实现的功能。

    实际上 JSP 在运行之前,需要先编译成 servlet , 然后才执行的。

    但是,在 JSP 中编写静态 HTML 更加方便,不必再用 println 语 句来输出每一行 HTML 代码。更重要的是,借助内容和外观的分离,页面制作中不同性质的任务可以方便地分开:比如,由页面设计者进行 HTML 设计,同时留出供 Java 程序员插入动态内容的空间。

    Tomcat 能运行 Servlet, 当然运行 JSP 肯定也是易如反掌。

    既然是 Web 服务器,Tomcat 除了能运行 Servlet 和 JSP 之外,也能像 Apache/nginx 那样,支持静态 html, 图片,文档的访问,只是性能要差一些,在实际的应用中,一般是这么使用他们的:

    

    Nginx 作为负载均衡服务器 和静态资源服务器放在最前端,后面是 tomcat 组成的集群。

    如果用户请求的是静态资源,Nginx 直接搞定,不用麻烦后面的 tomcat 了。

    如果是动态资源(如 xxx.jsp), Nginix 就会按照一定的算法转发到某个 Tomcat 上,达到负载均衡的目的。

    本文是给小白的 Java EE 生存指南的第 5 篇,讲一下前端工程师必备的 AJAX 的来龙去脉。

    回到 2001 年,当时的老刘还是小刘,在计算所跟着老板和四川的一个公司合作,做一个类似于 OA(办公自动化)的项目。

    当时小刘刚毕业,写过一些 asp 的程序,在小刘的意识当中,还是觉得只要你通过浏览器向服务器发出请求,服务器处理以后,需要刷新整个网页才能看到服务器处理的结果。

    但是有一天我突然看到项目中大牛写的一个页面,这个页面上面是菜单,中间是一个树形结构,代表了一个公司的各个部门。

    点击了菜单以后,整个页面没有刷新,神奇的是那个部门的树形机构竟然发生了变化!也就是说整个页面没有刷新,只是页面的局部发生了刷新。

    太不可思议了!我赶紧打开那个普通的 asp 程序,看看到底是什么情况。

    原来点了菜单以后,执行了一段 javascript,其中创建了一个叫 XMLHttpRequest 的东西;

    var xhr;

    if (window.XMLHttpRequest){

    xhr=new XMLHttpRequest(); // 非 IE 浏览器

    }else{

    xhr=new ActiveXObject(“Microsoft.XMLHTTP”); //IE 浏览器

    }

    // 放置一个回调函数:state_change,当 http 的状态发生变化时会调用

    xhr.onreadystatechange=state_change

    xhr.open(“GET”,”http://xxxxxx.xxx/xxx.asp”,true); // true 表示异步调用

    xhr.send(); // 这是一个耗时的操作

    // 具体的回调函数定义

    function state_change()

    {

    if (xmlhttp.readyState==4 && xmlhttp.status==200){

    // 获取到服务器返回的 xml

    xmlRes = xhr.responseXML;

    // 对 xml 进行处理,更新部门的树形结构,代码略

    document.getElementById(‘deptTree’).innerHTML = xxxxxxx

    }

    }

    // 其他代码,略

    你可以想象我第一次看到这样的处理时那种震惊的表情。原来页面可以这么写,javascript 可以这么用!

    其实这里体现的思想有两点:

    1. 异步调用

    异步的意思是说,调用一个耗时的函数(上例中的 xhr.send())以后,不等到它返回,就直接执行后续的代码了。

    当然在调用它之前会放置一个回调的函数 callback(上例中的 state_change),等到这个耗时的函数完成以后,再来调用 callback。

    为什么要这么做呢?主要是网络操作太耗时了,你在浏览器中的一个点击可能访问是地球那一边的服务器,如果是同步操作,即等待网络操作完成以后再进行下一步,就可能阻塞当前线程,甚至会导致浏览器卡死的情况。

    异步调用在编程中是个非常常用的手段,后来服务器端的 javascript Node.js 几乎全是基于事件的异步调用。

    2. 用 XML 做浏览器端和服务器端的数据交换

    这点毋庸解释,参见《给小白的 Java EE 指南(3): XML》,看看 xml 的作用。

    3. 局部刷新

    Javascript 取到从服务器端返回的 XML 以后,解析该 XML, 然后通过 DOM 对象只更新整个页面 html 的一部分,例如更新一个 table, 一个 div ….

    document.getElementById(‘deptTree’).innerHTML = xxxxxxx

    异步的 JavaScript 和 XML(Asynchronous Javascript And XML)就简称 AJAX, 你看这些缩写其实没什么神秘的。

    AJAX 这个词 2005 才出现,之前已经出现了大量的“AJAX”Web 应用,我认为其中最著名的就是 Google Maps 它使用 XMLHttpRequest 异步调用服务器端来获取数据,并将数据应用在客户端,实现了无刷新的效果,极好的用户体验让 Google Maps 获取了巨大的成功。

【XML VS JSON】

    但是在 javascript 中使用 XML 有两个问题:

    1. XML 要求有开始标签和结束标签,如 <name>liuxin</name> ,name 出现了两次,这在网络传输中其实是一种冗余浪费。

    2. javascript 需要解析 xml , 然后展示到浏览器中。

    第二点尤其不爽,所以就有人发展了一个叫 JSON(JavaScript Object Notation) 的一个轻量级的数据格式。JSON 其实就是 javascript 语法的子集,就是 javascript 中的对象和数组。

    对象在 js 中表示为“{}”括起来的内容,数据结构为 {key:value,key:value,…}的键值对的结构。

    数组在 js 中是中括号“[]”括起来的内容,数据结构为 [“java”,”javascript”,”vb”,…]。

    这两种结构虽然很简单,但是递归组合起来能表达任意的数据结构,这就是简单的力量,下面就是一个例子:

    {

    “programmers”:

    [{

    “firstName”: “Brett”,

    “lastName”: “McLaughlin”,

    “email”: “aaaa”

    }, {

    “firstName”: “Jason”,

    “lastName”: “Hunter”,

    “email”: “bbbb”

    }],

    “authors”:

    [{

    “firstName”: “Isaac”,

    “lastName”: “Asimov”,

    “genre”: “sciencefiction”

    }, {

    “firstName”: “Tad”,

    “lastName”: “Williams”,

    “genre”: “fantasy”

    }],

    “musicians”:

    [{

    “firstName”: “Eric”,

    “lastName”: “Clapton”,

    “instrument”: “guitar”

    }, {

    “firstName”: “Sergei”,

    “lastName”: “Rachmaninoff”,

    “instrument”: “piano”

    }]

    }

    由于 JSON 本身就是 Javascript 语法的一部分,javascipt 代码可以直接把他们当成对象来处理,根本不用解析 XML 了。

    再加上 JSON 结构很紧凑,很快就流行开来了,现在 AJAX 基本上都是在用 JSON 来传递数据了。

    题外话:第一次看到 AJAX 这个词的时候,作为球迷的我脑海里第一反应是荷兰的阿贾克斯足球俱乐部,在 90 年代,阿贾克斯足球号称青年近卫军,一帮小孩在欧冠决赛中把如日中天的 AC 米兰都搞定了,后来由于《博斯曼法案》的实施,球员可以自由转会,阿贾克斯就没落了。

    本文是给小白的 Java EE 生存指南的第 6 篇,讲点稍微有深度的:反射。

    这里不定义什么叫反射,先来看个例子,假设我给你一个 Java 类:

    package com.example;

    public class HelloWorld {

        public HelloWorld(){

        }

        public void sayHello(){

            System.out.println(“hello world!”);

        }

    }

    现在要求: 

    (1) 你不能使用 HelloWorld hw = new HelloWorld(),但是要构建一个 HelloWorld 的实例来. 

    (2) 调用 sayHello() 方法,但是不能直接用 HelloWorld 实例的 hw.sayHello()方法,说起来怪拗口的 :-)

    用 Java 的反射功能,可以很轻松的完成上面的要求:

    // 第一步,先把 HelloWorld 的类装载进来

    Class cls = Class.forName(“com.example.HelloWorld”);

    // 第二步,创建一个 HelloWorld 的实例,注意,这里并没有用强制转型把 obj 转成 HelloWorld

    Object obj = cls.newInstance();

    // 第三步,得到这个类的方法,注意,一个类的方法也是对象啊

    Method m = cls.getDeclaredMethod(“sayHello”); 

    // 第四部,方法调用,输出 ”hello world”

    m.invoke(obj);    

    可能有人要问了,为什么不直接 new 出来呢?通过反射来创建对象,调用方法多费劲啊?

    这是个好问题,关键点就是:很多时候我们并不能事先知道要 new 什么对象,相反,我们可能只知道一个类的名称和方法名,很多时候这些名称都是写在 XML 配置当中的。

    为了更好的说明问题,来看看几个 SSH 的例子:

【Struts 的例子】

    1. 在 XML 配置文件中定义 Action

            

            <result>/hello.jsp</result>  

     

    2. 定义 Java 类

    public class HelloWorld extends ExampleSupport {

        public String execute() throws Exception {    

            ……

            return SUCCESS;                           

        }  

        …….

    }

    Struts 框架的作者事先肯定不知道你会配置一个 HelloWorld 的 Action。

    不过他可以这么做,Struts 在启动以后,解析你配置 XML 配置文件,发现名称为 HelloWorld 的 Action, 找到相对于的类名 example.HelloWorld, 然后就可以通过反射去实例化这个类。等到有人调用这个 action 的时候,可以通过反射来调用 HelloWorld 的 execute() 方法。

【Hibernate 的例子】

    1.  定义 Java 类和表之间映射,类名叫 Event, 对应的表名是 EVENTS。

    <hibernate-mapping package=”org.hibernate.tutorial.hbm”>

        <class name=”Event” table=”EVENTS”>

            <id name=”id” column=”EVENT_ID”>

                <generator />

            </id>

            <property name=”date” type=”timestamp” column=”EVENT_DATE”/>

            <property name=”title”/>

        </class>

    </hibernate-mapping>

    2. 定义 Event 类,如下所示:

    public class Event {

        private Long id;

        private String title;

        private Date date;

        …… 为了节省篇幅,每个属性的 getter /setter 方法略 …

    }

    3. 查询,你可以用 Hibernate 这么查询表中的数据了:

    List result = session.createQuery(“from Event”).list();

            for (Event event : (List<Event>) result ) {

                System.out.println(“Event (” + event.getDate() + “) : ” + event.getTitle());

    }

    Struts 的作者事先也不知道你会配置一个叫 Event 的类。

    不过他会这么处理:类名(Event)-> 数据库表名(EVENTS) -> 发出 SELECT 查询表数据 -> 通过反射创建 Event 的实例 -> 通过反射调用实例的 setter 方法把数据库的值设置进去

【Spring 的例子】

    1. 配置一个 Bean

    <beanid=”helloWorld”>

        <propertyname=”message”value=”Hello World!”/>

    </bean>

    2. 写一个 Java 文件

    public  class   HelloWorld

    {

        private String message;

        public void setMessage(String message){

            this.message  = message;

        }

        public void getMessage(){

            System.out.println(“My Message : “+ message);

        }

    }

    3. 调用

    ApplicationContext context =newClassPathXmlApplicationContext(“Beans.xml”);

    HelloWorld hw=(HelloWorld) context.getBean(“helloWorld”);

    hw.getMessage();

    我都懒得解释了,无非是根据类的名称通过反射创建一个类 HelloWorld 的实例,然后再通过反射调用 setMessage 方法,这样当你 getMessage 就有值了。

    所以反射是很重要的,在 Java EE 世界里,反射最大的用途就是支持以声明式的方法(在 XML 中)来描述应用的行为,是 Struts, Hibernate , Spring 的最核心的技术之一。

    简单的来讲,反射能让你在运行时而不是编程时做下面的事情:

    (1) 获取一个类的内部结构信息(或者成为元数据),包括包名,类名,类所有的方法,

    (2) 运行时对一个 Java 对象进行操作,包括创建这个类的实例,设置一个属性的值,调用这个类的方法等等。

    这篇文章只是介绍了反射的一点皮毛和用途,具体的细节还是等待你自己去发掘吧。

    三、J2EE 要死了?

    最近有人问我说:欣哥,我们现在都用 Spring,Hibernate, SpringMVC 了,这 Java EE 是不是已经死掉了?

    这个问题让我哭笑不得,因为他似乎还没有搞清楚 Java EE 到底是怎么回事,就给 Java EE 判了死刑。

    Java EE 是什么呢?

    简单来讲 Java EE 就是一个技术规范的集合,一些核心的技术规范包括:JDBC, Servlet, JSP, JNDI, EJB, RMI, XML , JMS , JTA, JPA,JavaMail 等等。这些规范的目标很美好,就是帮助程序员开发大规模的、分布式的、高可用的“企业级”应用,只是实际的效果可能就没那么美好了。

    我们先来看一看这些核心的技术规范都是干嘛的,然后再来评判 Java EE 是不是快要死掉了。

    JDBC:Java 程序访问数据库的核心技术,不管是使用 Hibernate , MyBatis, EJB, 还是自己写类库,只要你访问数据库,JDBC 是绕不过去的,我还没见过在 Java 中用其他技术读取数据库的案例。

(参见文章:《JDBC 的诞生》)

    Servlet :   简单地说,Servlet 就是 Web 应用让外界访问的入口。当然现在直接写 Servlet 的越来越少,因为在框架的包装下,应用程序只需要直接写 Action 或者 Controller 就可以了,但这并不能否定 Servlet 的核心和基石地位。

    JSP & JSTL:由于前后端的分离和其他更优秀的替代技术,现在用 JSP 当做视图层也是凤毛麟角了,更多的是在遗留系统中在使用,JSP 确实在走向没落。

    JSTL 是 JSP Standard Tag Library, 一套标准的标签库,JSP 不受人待见,JSTL 就更不行了。

(扩展阅读:《JSP: 一个装配工的没落》)

    EJB:曾经最为热门的技术,寄托了大家最美好的期望,但事实证明 EJB 1.x 2.x 简直就是灾难,EJB3.x 虽然吸收了 Hibernate 的很多优秀特性,奈何大家使用轻量级类库和框架的习惯已经养成,无法翻身了。

    更要命的是使用 EJB 需要昂贵、复杂、笨重的应用服务器(如 Weblogic, Websphere 等),这也是使用它的巨大障碍,再加上 Spring 这个轻量级框架的崛起,EJB 被彻底打入冷宫。

    a.EJB 实现原理:就是把原来放到客户端实现的代码放到服务器端,并依靠 RMI 进行通信。

    b.RMI 实现原理:就是通过 Java 对象可序列化机制实现分布计算。

    RMI:远程方法调用,让 Java 程序可以像访问本地对象一样来访问远程对象的方法,早期和 EJB 搭配用的最多,因为 EJB 会被部署在分布式的应用服务器中,一个机器上的程序需要访问远程的 EJB,RMI 是个基础的技术。随着 EJB 的失宠,RMI 似乎也用的很少了。

    RMI 应该是 RPC 的一种实现,只能在 Java 世界中使用,相对于被广泛使用的基于 Web、基于 HTTP 的 RPC 调用,RMI 更不受待见。

(参见文章《我是一个函数》)

    JNDI:这是一个基础性的技术,可以把名称和对象进行绑定,最常见的就是通过一个名称来访问数据源,而不用关心这个数据源到底在什么地方。依然在广泛使用。

    JAXP:XML 在 Java 中无处不在,Java EE 通过 Java API for XML Processing(JAXP)来读取 / 解析 XML 文件,只是这个接口实在是不太好用,学院派气息过浓,所以现在大家都用民间开源的 JDOM,DOM4J 了。

    等到 JSON 开始流行,“悲剧”又重演了,大家都在用民间的 fastjson, jackson,似乎选择性的忘记了 官方的 Java API for JSON Processing。

    官方的不如民间的,也是 Java 世界的一大特色。

    JMS:Java 世界访问消息队列的标准方式,各大消息队列产品都支持,没有理由不用它。

(扩展阅读《Java 帝国之 JMS 的诞生》)

    JavaMail:在 Java 中发送邮件,又一个基础性的技术。

    JTA:Java Transaction API,主要是为了在多个数据源之间实现分布式事务,但是 JTA 在在高并发和高性能的场景下表现不佳,很多时候并没有用 JTA,而是用异步的方式来解决问题。

(扩展阅读:《Java 帝国之宫廷内斗(上)》《Java 帝国之宫廷内斗(下)》)

    除了上面列举的,Java EE 还有很多规范

    Java API for WebSocket

    Java Persistence

    Concurrency Utilities for Java EE

    Java EE Connector Architecture

    Java API for RESTful Web Services (JAX-RS)

    Java API for XML-Based RPC (JAX-RPC)

    Java Management Extensions (JMX)

    JavaBeans Activation Framework (JAF)

    ……

    这些规范在日常的 Web 开发中可能用的更少,SSH/SSM 中也可以找到对应物,给大家的感觉就是这 Java EE 的规范没用了,要死掉了。

    我倒是觉得应该是 Websphere , Weblogic 这些应用服务器不行了,从上面的描述中可以看到,活得最滋润的几个是 JDBC, Servlet, JMS,  最不如意的就是 EJB 了,而 EJB 恰恰是昂贵的商业应用服务器 Weblogic , Websphere 的一个核心功能。

    在云计算的环境下,在容器技术、微服务当道的情况下,那种集中式的、繁琐的、笨重的部署注定要走向消失。现在很多后端开发,用 Tomcat 这种主要支持 Servlet/JSP 的 Web 服务器,再配上轻量级的 SSH, 或者 SSM 就足够了。

    说了这么多,可能有人想到这个问题:这些规范是怎么制定的的呢?

    答案是 JCP(Java Community Process),在这个组织中,一些特定的成员(Full Memeber)如果想把某个技术如 JDBC 变成 Java 规范,就需要发起一个叫做 Java Specification Request (JSR) 东西,公开让大家进行评判,最后由执行委员会投票,决定是否可以发布成为正式规范。

    理论上每个人都能够成为 JCP 的成员,但是有发言权的,能上桌玩牌的还是一些巨头,如 Oracle, Google, IBM, SAP 等等,所以有很多人认为 JCP 制定出来的 Java EE 规范只是考虑大厂商的利益,不顾普罗大众的死活,这种脱离人民群众的规范是没人用的,是要死掉的。

    这么说确实有一定的道理,我个人觉得 Java EE 规范中有一些精华会保留下来,被一直使用,直到 Java 退出历史舞台。至于其他的 Java EE 规范,就让他们自生自灭去吧。

(完)

正文完
 0