小言_互联网的博客

Servlet 和 JSP学习

362人阅读  评论(0)

1、servlet 就是服务器用来处理请求的辅助应用

2、简单来说,在Servlet中引入Java,就是JSP

3、什么是容器,Servlet中没有main()方法,它受控于另外一个Java应用,这个控制Servlet的应用叫做容器。

4、在Web服务器接收到请求时,首先是到达Web服务器应用(如Apache),接着服务器应用将这个请求交给Web容器应用,然后要由容器向Servlet提供HTTP请求和响应,而且要由容器调用Servlet的方法,如doPost()和doGet()。

5、使用容器能够提供哪些好处:

  1. 通信支持:利用容器提供的方法,能够让Servlet与Web服务器对话,而无需建立自己的ServerSocket(详见:https://www.cnblogs.com/baxianhua/p/9287836.html)。容器知道自己与Web服务器之间的协议,所以你的Servlet不用担心Web服务器和你自己的Web代码之间的API,要考虑的只是如何在Servlet中实现业务逻辑。
  2. 生命周期管理:容器控制着Servlet的生与死。它会负责如何加载类、实例化和初始化Servlet、调用Servlet方法,并使Servlet实例能够被垃圾回收,有了容器的控制,就可以不用太多地考虑资源管理了。
  3. 多线程支持:容器会自动为它接收的每个Servlet请求创建一个新的Java线程。针对客户的请求,如果servlet已经运行完相应的HTTP服务方法,这个线程就会结束(也就是会死掉)。
  4. 生命方式实现安全:利用容器,可以使用XML部署描述文件来配置(和修改)安全性,而不必硬编码到Servlet或其他类代码中
  5. 将JSP翻译成真正的Java

6、容器如何处理请求

  1. 用户点击一个URL,这个URL指向一个Servlet而不是一个静态页面。
  2. 容器“看出来”这个请求要的是一个Servlet,所以容器会创建两个对象,即HttpServletResponse和HttpServletRequest。
  3. 容器根据请求中的URL找到正确的servlet,为这个请求创建或者或分配一个线程,并把请求和响应对象传递给这个Servlet线程
  4. 容器调用servlet的service()方法。根据请求的同类型,service()方法会调用doGet()或者是doPost()方法
  5. doGet()方法生成动态页面,并把这个页面“填入”响应对象。因为前文已经指出,容器还有响应对象的一个引用。
  6. 线程结束,容器把响应对象转换为一个HTTP响应,把它发送给客户,然后删除请求和响应对象。

7、所有用作Servlet的类都应该扩展 抽象类HttpServlet,然后,这个Servlet要覆盖其中的doGet() 或者是doPost()方法,这里以doGet()方法为例,doGet()方法的参数为HttpServletRequest和HttpServletResponse,这是两个接口,但实际使用的是具体的类,由容器生成。在Servlet从容器得到的HttpServletResponse对象中得到一个PrintWriter,使用这个PrintWriter能够将HTML文本输出到响应对象(除了输出PrintWriter外,还可以输出其他内容,比如输出图片而不是HTML文本)。

8、容器是怎么找到所需要的Servlet的,首先要理解Servlet有3种不同的名称,第一个是实际的类名(class文件的名字),第二个是部署Servlet的人给它取的一个特殊的部署名,第三个是客户可以看到的,要写到HTML中的公共URL名。因此需要在部署描述文件中将对应关系指定清楚。在xml中的<servlet>元素中将内部名映射到实际的类名,<servlet-mapping>将内部名映射到公共URL名。

完全限定名对应的xml元素为<servlet-class>,内部名对应的xml元素为<servlet-name>,公共URL名对应的xml元素为<url-pattern>。

用户通过浏览器的公共url名来请求servlet,首先映射到实际servlet的类名,然后容器根据该类名查找实际的类,然后再进行相应的处理。

9、部署描述文件(Deployment Descriptor)的作用:把URL映射到实际的servlet。还可以用部署描述文件对Web应用的其他方面进行定制,包括安全角色,错误页面,标记库,初始配置信息等,如果是一个完整的J2EE服务器,甚至可以声明你要访问特定的企业JavaBean。

利用部署描述文件,可以采用一种一种方式修改应用,而无需修改源代码。此外,还有以下优点:

9、经过Servlet处理后, 将请求转发到一个JSP,然后由JSP处理响应HTML。这样还可以将业务逻辑与表示分离。

10、MVC设计模式:(model-view-control:模型-视图-控制器)。这个设计模式就是讲业务逻辑从servlet中抽离出来,把业务逻辑放到一个“模型”中,所谓模型就是一个可重用的普通Java类,模型是业务数据和处理数据的方法的组合。

在Servlet & JSP世界中,MVC就是就是: 

模型(普通Java类):包含业务逻辑和状态,具体来说就是模型知道用什么来得到和更新状态。系统中只有这个部分与数据库通信(不过它也有可能使用另一个对象来完成具体的数据库通信)

视图(jsp):负责表示方面,它从控制器得到模型的状态(不过不是直接得到,控制器会把模型数据放在模型能找到的一个地方)。另外视图还要获得用户输入,并交给控制器。

控制器(servlet):从请求获得用户输入,并明确这些输入对模型有什么影响。告诉模型自行更新,并让视图(jsp)得到最新的模型状态。

11、J2EE中集成的一切:J2EE应用服务器包含一个Web容器和一个EJB容器。

解释一下JavaBean:JavaBean其实就是一个普通的Java类,是Java程序设计中的一种设计模式,是一种基于Java平台的软件组织思想。

JavaBean遵循着特定的写法,通常有以下规则:

  • 有无参数的构造函数
  • 成员属性私有化
  • 封装的属性如果被外界(在对象外)所操作,必须编写public类型的setter和getter方法

看起来高大上,实际上JavaBean就是按照特定写法、规则编写的JavaBean对象。

为什么要使用JavaBean,使用JavaBean的好处就是:封装,重用,可读

如果把bean类与数据库联合使用,一张表使用bean类,可以使你的代码更加简洁高效,易于理解,现在大多数框架都会使用这种机制。

其他内容详见:https://segmentfault.com/a/1190000013165165

12、给出一个啤酒应用的简单步骤:

  1. 客户端请求初始界面(这里是form.html界面)
  2. 请求到达容器,容器找到这个界面
  3. 容器把这个页面返回给浏览器,用户再在浏览器填写表单
  4. 用户点击提交按钮,浏览器将数据发送给容器
  5. 容器根据URL查找查找到正确的servlet,把请求传递给这个servlet
  6. Servlet调用模型进行处理。
  7. 模型返回一个回答,servlet将这个回答增加到请求对象
  8. 下一步就进入生成响应的阶段,servlet将请求发送给jsp
  9. Jsp从请求对象得到回答
  10. Jsp为容器生成一个页面
  11. 容器将这个页面返回给用户

13、开发web应用的4大步骤:

  1. 分析web应用的用户视图,即要构建什么样的页面
  2. 分析结构体系,即采用什么设计模式,这里我们采用MVC体系结构构建
  3. 建立创建和部署应用的开发环境和部署环境
  4. 创建应用

最后一个步骤又分为5个小步骤,

  1. 构建和测试用户最初请求的html表单
  2. 构建控制器servlet的第一个版本。并用HTMl表单测试这个控制器
  3. 为专家/模型类构建一个测试类,然后构建并测试专家/模型类本身
  4. 把servlet升级到第二版。这个版本可以调用模型类得到一个啤酒建议
  5. 构建JSP,Servlet可以将表示分派给JSP,然后测试整个应用

14、servlet的生命周期:

  1. 用户点击一个链接,链接的URL指向一个servlet
  2. 容器看出这个请求指向一个servlet,所以容器创建两个对象:HttpServletRequest和HttpServletResponse
  3. 容器根据请求中的url查找正确的servlet,为这个请求创建或分配一个线程,并调用servlet的service()方法(传递请求和响应对象作为参数)
  4. service()方法根据客户发出的HTTP方法(GET、POST等),确定要调用哪个service()方法。例如客户发出了一个HTTP GET请求,所以service()方法会调用servlet的doGet()方法,并传递请求和响应对象作为参数
  5. Servlet使用响应对象将响应写至客户。响应通过容器传回。
  6. Service()方法结束,线程要么撤销,要么返回到容器管理的一个线程池。请求和响应对象引用已经出了作用域,所以这些对象已经没有意义(可以垃圾回收)。
  7. 客户得到响应。

15、Servlet的生命周期很简单:只有一个主要的状态——初始化。一个servlet要么还不存在,要么还没有初始化,要么正在初始化(运行其构造函数或init()方法)、正在撤销(运行其destroy()方法)。

其详细的生命周期如下图所示:

Servlet的生命周期:

第一步:寻找类,容器启动时,它会寻找已经部署的Web应用,然后开始搜索servlet类文件。

第二步:加载类,这可能在容器启动时发生,也可能是在第一个客户使用时进行。容器可能允许你来完成类加载,也可能希望会在它希望的任何时刻加载类。总之,在servlet没有完全初始化之前绝不能运行servlet()的service()方法。

第3步:初始化类,即运行init()函数,记住不要在servlet的构造函数中放任何东西,任何东西都可以放在init()函数里。

第4步:调用service()方法

第5步:调用destroy()方法

HttpServlet的继承关系为:

servlet生命周期中的3个重要时刻是

16、容器运行多个线程来处理对一个servlet的多个请求,对应于每个客户请求,都会生成一对新的请求和响应对象。

如果容器使用了集群分布,把应用分布在多个JVM上,那么每个JVM都会有特定servlet的一个实例,不过对于每个servlet来说,都只有该servlet的一个实例。

17、ServletConfig对象和ServletContext对象:

ServletConfig对象:

  • 每个servlet都有一个ServletConfig对象。
  • 用于向servlet传递部署时信息(例如数据库或者企业bean的查找名),而你不想把这个信息硬编码写到servlet中(servlet初始化参数)。
  • 用于访问ServletContext。
  • 参数在部署描述文件中配置

 

ServletContext对象:

  • 每个Web应用有一个ServletContext
  • 用于访问Web应用参数(也在部署描述文件中配置)
  • 相当于一种应用公告栏,可以在这里放置消息(称为属性),应用的其他部分可以访问这些消息
  • 用于得到服务器信息,包括容器名和容器版本,以及所支持的API版本等。

18、HttpServletRequest和HttpServletResponse的类继承关系:

19、在使用HttpServletRequest和HttpServletResponse的过程中,要注意:  这两个接口的具体应用类由容器的开发商实现,在使用时,只需要记住这两个引用具有HttpServletRequest和HttpServletResponse的所有功能。

20、在实际的servlet使用中,只需要关心GET和POST,但同时也应该注意,还有其他的HTTP方法。

21、get方法和post方法的区别:get方法表单信息在请求行里,post方法的表单信息在消息体里:

22、非幂等请求:

首先给出幂等性的定义:就是对资源的操作,无论操作一次还是多次,其资源本身不发生变化。

其详细信息见:https://blog.csdn.net/shangrila_kun/article/details/89057968

23、如果在html中的form表单中没有指定所需的HTTP方法,默认会使用get方法,如果想让一个servlet同时支持post和get方法,通常会把逻辑放在doGet()中,如果有必要,再建立doPost()方法委托到doGet()。

24、表单中的每个参数对应于HttpServletRequest对象中的相应参数。注意一个属性可能对应多个值。这个时候需要使用request.getParameterValues(),并且这个方法返回的是一个数组String[],而request.getParameter()返回的是String。

25、HttpServletRequest中可能被用到的方法:

为什么需要从请求中得到一个InputStream呢?Post方法的请求体中,有些参数的值可能很大,并且有可能创建一个servlet来处理计算机驱动的请求,其中请求体包含要处理的文本和二进制内容,在这种情况下,可以使用getReader()或者是getInputStream()方法。这些流只包含HTTP请求的体而不包含首部。

getHeader()和getIntHeader()有什么区别:getHeader()返回的是一个String,但如果你已经确切地知道某个首部的值为整数,那么就可以使用getIntHeader()

getRemotePort()为得到“客户的端口”,也就是得到发送请求的客户的端口号,

getServerPort()为“请求原来打算发送到哪个端口”

getLocalPort()为“请求最后实际上发送到了哪个端口”

这两者是有区别的,因为尽管请求要发送到一个端口(服务器所监听的端口),但是服务器会为不同的线程找一个不同的本地端口,这样一来,一个应用就能够同时处理多个不同客户了。

26、最后再来考虑响应,响应是要返回给客户的东西,可以使用响应对象得到一个输出流(通常是一个Writer),并使用这个流写出HTML(或者是其他类型的内容)。返回给客户。

对于响应对象,常用的方法有:

27、应该使用JSP,而不是servlet把HTML发回到输出流。

28、如果客户的请求是下载一个JAR文件,下载JAR的Sevlet代码如下所示:

29、response.setContentType(),这个函数用于设置内容类型,这个内容类型告诉浏览器将要发回什么,当谈到内容类型时,指的一般都是MIME类型。内容类型是响应中必须要有的一个首部。

30、ServletResponse接口只提供了两个流可供选择,ServletOutputStream用于输出字节,PrintWriter用于输出字符数据。其实传输的都是字节,但是PrintWriter实际上是包装了ServletOutputStream,也就是说,PrintWriter有ServletOutputSteam的一个引用,而且会把调用委托给ServletOutputStream。返回给客户的输出流只有一个,但是PrintWriter会“装饰”这个流,为它增加更高层的“字符友好”方法(即适于处理字符的方法)。

如果将字节流写至ServletOutputStream,就要调用write()方法,如果写至PrintWriter(),就要使用println(),

31、可以设置响应首部,也可以增加响应首部,这其中涉及到的方法有:response.setHeader()、response.addHeader()、response.setIntHeader,

32、可以把请求重定向到一个完全不同的URL,或者可以把请求分派给web应用的另一个组件(通常是一个JSP)。

33、Servlet可以重定向让浏览器完成工作。

设置重定向后,用户得到的HTTP响应有一个状态码301,还有一个Location首部,这个首部值是一个URL。在sendRedirect()中使用绝对和相对URLs

不能在响应已经提交之后再调用sendRedirect(),如果“在响应已经提交”之后再调用这个方法,就会抛出一个IllegalStateException异常。

这里的“提交”是指:响应已经发出,即数据已经刷新输出到流中。

另外,setRedirect()方法取的是一个String,而不是一个URL对象

34、请求分派:请求分派是让服务器的其他组件来完成某项任务。

重定向=返回新的URL让用户的浏览器处理。

请求分派=安排服务器上的其他组件继续处理

35、可以在servlet中配置初始化参数,下面是配置的过程以及使用的方法:

注意,在servlet初始化之前不能使用servlet初始化参数,即在构造函数中不能使用getServletConfig()方法得到ServletConfig的一个引用,因为这时还没有ServletConfig。

在容器初始化一个servlet时(即运行init()函数),会为这个servlet建立一个唯一的ServletConfig。容器从部署描述文件(DD)中读取servlet初始化参数,并把这些参数交给ServletConfig,然后将ServletConfig传递给servlet的init()方法。

36、ServletConfig的主要任务是提供初始化参数,它还提供了一个ServletContext,但是我们一般以另外一种方式得到上下文。getServletName()方法很少使用。

37、如果想在servlet之外的web组件(如JSP中)使用初始化参数,那么可以在请求对象中设置属性。

38、request 中Parameter(参数) 和Attribute(属性)区别,详见:https://blog.csdn.net/qq_36557815/article/details/78826617

39、servlet初始化参数<init-param>只能用于构建ServletConfig对象,只能用作servlet的init()函数的参数。JSP不能直接访问。

还有一个上下文初始化参数,这对整个web应用可用,而不仅仅限于一个servlet,即<congtext-param>。

上下文初始参数和servlet初始化参数的对比:

40、JSP最终会变成首类servlet,所以也有自己的ServletConfig。如果应用是分布式应用,那么每个JVM有一个ServletContext

41、这两类初始化参数一经部署,就不能改变,要把初始化参数认为是部署时常量,可以在运行时得到这些初始化参数,但是不能设置。如果在配置文件web.xml中更改了初始化值,那么只有在重新部署web应用时才能看到这些更改。

42、如果没有提到是servlet初始化参数还是上下文初始化参数,那么默认是servlet初始化参数。但是最好应该指明。

43、ServletContext的一些方法:

44、注意,初始化参数的值只能是字符串,要想根据初始化参数进行一些其他的初始化操作,可以使用一个监听者(listener),能够在应用为用户提供服务之前 应用一些代码。这就是一个ServletContextListener,实现这个接口的类能够监听ServletContext一生中的两个关键事件,初始化(创建)和撤销。

实现上下文监听者很简单,继承ServletContextListener并重载contexInitialized()和contextDestroyed()方法。这两个函数的事件对象为ServletContextEvent。(可以这样想,在ServletContext完成,init()函数开始之前触发侦听器,在应用取消部署或结束之后运行侦听器。)

45、上下文监听者类的使用

46、最好保证servlet的属性是可串行化(Serializable)的:

47、注意getAttribute()方法返回的类型为Object,需要对结果进行强制类型转换。而getInitParameter返回的是String。

在部署ServletContextListener时,由于这个listener监听的是ServletContext,因此需要放在<servlet>外,<web-app>中

48、下面给出web应用处理用户请求的完整步骤:

49、listener可以不止用于上下文事件。只有是生命周期里的重要时刻,总会有一个监听者在监听。除了上下文事件以外,还可以监听与上下文属性、servlet请求与属性、以及Http会话和会话属性相关的事件。

下表列出了8个监听者:

区分其中的HttpSessionBindingListener和HttpSessionAttributeListener,前者是以作为属性的对象为主体,后者是以会话为主体

注意,HttpSessionBindingListener并不用在DD里注册,它会自动运行。

50、到底什么是属性

51、属性和参数的区别:

52、三个作用域:上下围,请求和会话。

53、属性的API

53、上下文作用域不是线程安全的。并且对doGet()方法

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


转载:https://blog.csdn.net/WFY_WFY/article/details/102558318
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场