Image 2 Image 3 Image 3 Image 3

我把面试问烂了的 Java Web 面试题总结了一下带答案,建议收藏

频道:行业资讯 日期: 浏览:1248

1、阐述Servlet和CGI的区别?

答:概括来讲,Servlet可以完成和CGI相同的功能。

CGI应用开发比较困难,因为它要求程序员有处理参数传递的知识,这不是一种通用的技能。CGI不可移植,为某一特定平台编写的CGI应用只能运行于这一环境中。每一个CGI应用存在于一个由客户端请求激活的进程中,并且在请求被服务后被卸载。这种模式将引起很高的内存、CPU开销,而且在同一进程中不能服务多个客户。

Servlet提供了Java应用程序的所有优势:可移植、稳健、易开发。使用Servlet Tag技术,Servlet能够生成嵌于静态HTML页面中的动态内容。

Servlet对CGI的最主要优势在于一个Servlet被客户端发送的第一个请求激活,然后它将继续运行于后台,等待以后的请求。每个请求将生成一个新的线程,而不是一个完整的进程。多个客户能够在同一个进程中同时得到服务。一般来说,Servlet进程只是在Web Server卸载时被卸载。

Java Servlet与CGI (Common Gateway Interface 公共网关接口)的比较:

与传统的CGI和许多其他类似CGI的技术相比,Java Servlet具有更高的效率,更容易使用,功能更强大,具有更好的可移植性,更节省投资。在未来的技术发展过程中,Servlet有可能彻底取代CGI。

在传统的CGI中,每个请求都要启动一个新的进程,如果CGI程序本身的执行时间较短,启动进程所需要的开销很可能反而超过实际执行时间。而在Servlet中,每个请求由一个轻量级的Java线程处理(而不是重量级的操作系统进程)。

在传统CGI中,如果有N个并发的对同一CGI程序的请求,则该CGI程序的代码在内存中重复装载了N次;而对于Servlet,处理请求的是N个线程,只需要一份Servlet类代码。在性能优化方面,Servlet也比CGI有着更多的选择。

* 方便

Servlet提供了大量的实用工具例程,例如自动地解析和解码HTML表单数据、读取和设置HTTP头、处理Cookie、跟踪会话状态等。

* 功能强大

在Servlet中,许多使用传统CGI程序很难完成的任务都可以轻松地完成。例如,Servlet能够直接和Web服务器交互,而普通的CGI程序不能。Servlet还能够在各个程序之间共享数据,使得数据库连接池之类的功能很容易实现。

* 可移植性好

Servlet用Java编写,Servlet API具有完善的标准。因此,为IPlanet Enterprise Server写的Servlet无需任何实质上的改动即可移植到Apache、Microsoft IIS或者WebStar。几乎所有的主流服务器都直接或通过插件支持Servlet。

2、Servlet接口中有哪些方法?

(1)init方法:

init(ServletConfig config)

在 Servlet 的生命期中,仅执行一次 init() 方法,它是在服务器装入 Servlet 时执行的。

可以配置服务器,以在启动服务器或客户机首次访问 Servlet 时装入 Servlet。 无论有多少客户机访问 Servlet,都不会重复执行 init() 。

缺省的 init() 方法通常是符合要求的,但也可以用定制 init() 方法来覆盖它,典型的是管理服务器端资源。 例如,可能编写一个定制 init() 来只用于一次装入 GIF 图像,改进 Servlet 返回 GIF 图像和含有多个客户机请求的性能。另一个示例是初始化数据库连接。缺省的 init() 方法设置了 Servlet 的初始化参数,并用它的 ServletConfig 对象参数来启动配置, 因此所有覆盖 init() 方法的 Servlet 应调用 super.init() 以确保仍然执行这些任务。在调用 service() 方法之前,应确保已完成了 init() 方法。

(2)service方法:

service(ServletRequest req, ServletResponse res)

service() 方法是 Servlet 的核心。每当一个客户请求一个HttpServlet 对象,该对象的service() 方法就要被调用,而且传递给这个方法一个"请求"(ServletRequest)对象和一个"响应"(ServletResponse)对象作为参数。 在 HttpServlet 中已存在 service() 方法。缺省的服务功能是调用与 HTTP 请求的方法相应的 do 功能。例如, 如果 HTTP 请求方法为 GET,则缺省情况下就调用 doGet() 。

Servlet 应该为 Servlet 支持的 HTTP 方法覆盖 do 功能。因为 HttpServlet.service() 方法会检查请求方法是否调用了适当的处理方法,不必要覆盖 service() 方法,只需覆盖相应的 do 方法就可以了。

post方法与get方法:

当一个客户通过HTML 表单发出一个HTTP POST请求时,doPost()方法被调用。与POST请求相关的参数作为一个单独的HTTP 请求从浏览器发送到服务器。当需要修改服务器端的数据时,应该使用doPost()方法。

当一个客户通过HTML 表单发出一个HTTP GET请求或直接请求一个URL时,doGet()方法被调用。与GET请求相关的参数添加到URL的后面,并与这个请求一起发送。当不会修改服务器端的数据时,应该使用doGet()方法。

一般开发采用post方法,大小一般限制为64KB。

(3)destroy方法:

destroy()

与init方法相类似,destroy() 方法仅执行一次,即在服务器停止且卸装Servlet 时执行该方法。典型的,将 Servlet 作为服务器进程的一部分来关闭。缺省的 destroy() 方法通常是符合要求的,但也可以覆盖它,典型的是管理服务器端资源。例如,如果 Servlet 在运行时会累计统计数据,则可以编写一个 destroy() 方法,该方法用于在未装入 Servlet 时将统计数字保存在文件中。另一个示例是关闭数据库连接。

当服务器卸装 Servlet 时,将在所有 service() 方法调用完成后,或在指定的时间间隔过后调用 destroy() 方法。一个Servlet 在运行service() 方法时可能会产生其它的线程,因此请确认在调用 destroy() 方法时,这些线程已终止或完成。

(4)getServletInfo方法:

getServletInfo()

GetServletInfo()方法是一个可选的方法,它提供有关servlet 的信息,如作者、版本、版权。

当服务器调用sevlet 的Service()、doGet()和doPost()这三个方法时,均需要 "请求"和"响应"对象作为参数。"请求"对象提供有关请求的信息,而"响应"对象提供了一个将响应信息返回给浏览器的一个通信途径。

(5)getServletConfig方法:

getServletConfig()

getServletConfig() 方法返回一个 ServletConfig 对象,该对象用来返回初始化参数和 ServletContext。ServletContext 接口提供有关 servlet 的环境信息。

3、转发(forward)和重定向(redirect)的区别?

页面跳转的两种实现方式:请求转发和重定向;

请求转发:

客户首先发送一个请求到服务器端,服务器端发现匹配的servlet,并指定它去执行,当这个servlet执行完之后,它要调用getRequestDispacther()方法,把请求转发给指定的student_list.jsp,整个流程都是在服务器端完成的,而且是在同一个请求里面完成的,因此servlet和jsp共享的是同一个request,在servlet里面放的所有东西,在student_list中都能取出来,因此,student_list能把结果getAttribute()出来,getAttribute()出来后执行完把结果返回给客户端。整个过程是一个请求,一个响应。

重定向:

客户发送一个请求到服务器,服务器匹配servlet,servlet处理完之后调用了sendRedirect()方法,立即向客户端返回这个响应,响应行告诉客户端你必须要再发送一个请求,去访问student_list.jsp,紧接着客户端收到这个请求后,立刻发出一个新的请求,去请求student_list.jsp,这里两个请求互不干扰,相互独立,在前面request里面setAttribute()的任何东西,在后面的request里面都获得不了。可见,在sendRedirect()里面是两个请求,两个响应。(服务器向浏览器发送一个302状态码以及一个location消息头,浏览器收到请求后会向再次根据重定向地址发出请求)

请求转发:

request.getRequestDispatcher("/test.jsp").forword(request,response);

重定向:response.sendRedirect("/test.jsp");

区别:

请求次数:重定向是浏览器向服务器发送一个请求并收到响应后再次向一个新地址发出请求,转发是服务器收到请求后为了完成响应跳转到一个新的地址;重定向至少请求两次,转发请求一次;

地址栏不同:重定向地址栏会发生变化,转发地址栏不会发生变化;

是否共享数据:重定向两次请求不共享数据,转发一次请求共享数据(在request级别使用信息共享,使用重定向必然出错);

跳转限制:重定向可以跳转到任意URL,转发只能跳转本站点资源;

发生行为不同:重定向是客户端行为,转发是服务器端行为;

使用:

可以利用request的域对象的特点,由源组件向其中存放写数据;

可以让用户访问到存放在WEB-INF目录中的目标资源;

重定向的速度比转发慢,因为浏览器还得发出一个新的请求,所以如果在使用转发和重定向都无所谓的时候建议使用转发;

因为转发只能访问当前WEB的应用程序,所以不同WEB应用程序之间的访问,特别是要访问到另外一个WEB站点上的资源的情况,这个时候就只能使用重定向了。

4、JSP有哪些内置对象?作用分别是什么?

答:JSP有9个内置对象:

request:用户端请求,此请求会包含来自来自GET/POST请求的参数

response:网页传回用户端的回应

out:用来传送回应的输出

page:JSP网页本身

config:servlet的架构部件

session:与请求有关的

application:servlet正在执行的内容

pageContext:网页的属性是在这里管理的

exception:针对错误网页,未捕捉的例外

request表示HttpServletRequest对象。它包含了有关浏览器请求的信息,并且提供了几个用于获取cookie, header,和session数据的有用的方法。

response表示HttpServletResponse对象,并提供了几个用于设置送回浏览器的响应的方法(如cookies,头信息等)

out对象是javax.jsp.JspWriter的一个实例,并提供了几个方法使你能用于向浏览器回送输出结果。

pageContext表示一个

javax.servlet.jsp.PageContext对象。它是用于方便存取各种范围的名字空间、servlet相关的对象的API,并且包装了通用的

servlet相关功能的方法。

session表示一个请求的

javax.servlet.http.HttpSession对象。Session可以存贮用户的状态信息

applicaton 表示一个

javax.servle.ServletContext对象。这有助于查找有关servlet引擎和servlet环境的信息

config表示一个

javax.servlet.ServletConfig对象。该对象用于存取servlet实例的初始化参数。

page表示从该页面产生的一个servlet实例

JSP共有以下6种基本动作:

jsp:include:在页面被请求的时候引入一个文件。

jsp:useBean:寻找或者实例化一个JavaBean。

jsp:setProperty:设置JavaBean的属性。

jsp:getProperty:输出某个JavaBean的属性。

jsp:forward:把请求转到一个新的页面。

jsp:plugin:根据浏览器类型为Java插件生成OBJECT或EMBED标记

5、get和post请求的区别?

总结说明

HTTP报文层面:GET将请求信息放在URL,POST放在报文体中。

数据库层面:GET符合幂等性和安全性,POST不符合。

其它层面:GET可以被缓存、被存储,POST不行

分别说明:

get用来获取数据,post用来提交数据

get参数有长度限制(受限于url长度,具体的数值取决于浏览器和服务器的限制,最长2048字节),而post无限制。

get请求的数据会附加在url之 ,以 " ? "分割url和传输数据,多个参数用 "&"连接,而post请求会把请求的数据放在http请求体中。

get是明文传输,post是放在请求体中,但是开发者可以通过抓包工具看到,也相当于是明文的。

get请求会保存在浏览器历史记录中,还可能保存在web服务器的日志中

幂等性

幂等通俗的来讲就是指同一个请求执行多次和仅执行一次的效果完全相等。这里来扯出幂等主要是为了处理同一个请求重复发送的情况,假如在请求响应之前失去连接,如果这个请求时幂等的,那么就可以放心的重发一次请求。所以可以得出get请求时幂等的,可以重复发送请求,post请求时不幂等的,重复请求可能会发生无法预知的后果。

6、常用的Web服务器有哪些?

答:Unix和Linux平台下使用最广泛的免费HTTP服务器是Apache服务器,而Windows平台的服务器通常使用IIS作为Web服务器。选择Web服务器应考虑的因素有:性能、安全性、日志和统计、虚拟主机、代理服务器、缓冲服务和集成应用程序等。下面是对常见服务器的简介:

IIS:Microsoft的Web服务器产品,全称是Internet Information Services。IIS是允许在公共Intranet或Internet上发布信息的Web服务器。IIS是目前最流行的Web服务器产品之一,很多著名的网站都是建立在IIS的平台上。IIS提供了一个图形界面的管理工具,称为Internet服务管理器,可用于监视配置和控制Internet服务。IIS是一种Web服务组件,其中包括Web服务器、FTP服务器、NNTP服务器和SMTP服务器,分别用于网页浏览、文件传输、新闻服务和邮件发送等方面,它使得在网络(包括互联网和局域网)上发布信息成了一件很容易的事。它提供ISAPI(Intranet Server API)作为扩展Web服务器功能的编程接口;同时,它还提供一个Internet数据库连接器,可以实现对数据库的查询和更新。

Kangle:Kangle Web服务器是一款跨平台、功能强大、安全稳定、易操作的高性能Web服务器和反向代理服务器软件。此外,Kangle也是一款专为做虚拟主机研发的Web服务器。实现虚拟主机独立进程、独立身份运行。用户之间安全隔离,一个用户出问题不影响其他用户。支持PHP、ASP、ASP.NET、Java、Ruby等多种动态开发语言。

WebSphere:WebSphere Application Server是功能完善、开放的Web应用程序服务器,是IBM电子商务计划的核心部分,它是基于Java的应用环境,用于建立、部署和管理Internet和Intranet Web应用程序,适应各种Web应用程序服务器的需要。

WebLogic:WebLogic Server是一款多功能、基于标准的Web应用服务器,为企业构建企业应用提供了坚实的基础。针对各种应用开发、关键性任务的部署,各种系统和数据库的集成、跨Internet协作等Weblogic都提供了相应的支持。由于它具有全面的功能、对开放标准的遵从性、多层架构、支持基于组件的开发等优势,很多公司的企业级应用都选择它来作为开发和部署的环境。WebLogic Server在使应用服务器成为企业应用架构的基础方面一直处于领先地位,为构建集成化的企业级应用提供了稳固的基础。

Apache:目前Apache仍然是世界上用得最多的Web服务器,其市场占有率很长时间都保持在60%以上(目前的市场份额约40%左右)。世界上很多著名的网站都是Apache的产物,它的成功之处主要在于它的源代码开放、有一支强大的开发团队、支持跨平台的应用(可以运行在几乎所有的Unix、Windows、Linux系统平台上)以及它的可移植性等方面。

Tomcat:Tomcat是一个开放源代码、运行Servlet和JSP的容器。Tomcat实现了Servlet和JSP规范。此外,Tomcat还实现了Apache-Jakarta规范而且比绝大多数商业应用软件服务器要好,因此目前也有不少的Web服务器都选择了Tomcat。

Nginx:读作"engine x",是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP代理服务器。 Nginx是由Igor Sysoev为俄罗斯访问量第二的Rambler站点开发的,第一个公开版本0.1.0发布于2004年10月4日。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。在2014年下半年,Nginx的市场份额达到了14%。

7、JSP和Servlet是什么关系?

答:其实这个问题在上面已经阐述过了,Servlet是一个特殊的Java程序,它运行于服务器的JVM中,能够依靠服务器的支持向浏览器提供显示内容。JSP本质上是Servlet的一种简易形式,JSP会被服务器处理成一个类似于Servlet的Java程序,可以简化页面内容的生成。Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。有人说,Servlet就是在Java中写HTML,而JSP就是在HTML中写Java代码,当然这个说法是很片面且不够准确的。JSP侧重于视图,Servlet更侧重于控制逻辑,在MVC架构模式中,JSP适合充当视图(view)而Servlet适合充当控制器(controller)。

8、讲解JSP中的四种作用域

答:首先要声明一点,所谓“作用域”就是“信息共享的范围”,也就是说一个信息能够在多大的范围内有效。4个JSP内置对象的作用域分别为:application、session、request、page 。JSP内置对象作用域表如下:

我把面试问烂了的 Java Web 面试题总结了一下带答案,建议收藏

Web交互的最基本单位为HTTP请求。每个用户从进入网站到离开网站这段过程称为一个HTTP会话,一个服务器的运行过程中会有多个用户访问,就是多个HTTP会话。作用域解释如下。

(1)application 作用域

如果把变量放到application里,就说明它的作用域是application,它的有效范围是整个应用。 整个应用是指从应用启动,到应用结束。我们没有说“从服务器启动,到服务器关闭”,是因为一个服务器可能部署多个应用,当然你关闭了服务器,就会把上面所有的应用都关闭了。 application作用域里的变量,它们的存活时间是最长的,如果不进行手工删除,它们就一直可以使用。

application作用域上的信息传递是通过ServletContext实现的,它提供的主要方法如下所示:

Object getAttribute(String name) //从application中获取信息; void setAttribute(String name, Object value) //向application作用域中设置信息。

(2)session作用域

session作用域比较容易理解,同一浏览器对服务器进行多次访问,在这多次访问之间传递信息,就是session作用域的体现。如果把变量放到session里,就说明它的作用域是session,它的有效范围是当前会话。所谓当前会话,就是指从用户打开浏览器开始,到用户关闭浏览器这中间的过程。这个过程可能包含多个请求响应。也就是说,只要用户不关浏览器,服务器就有办法知道这些请求是一个人发起的,整个过程被称为一个会话(session),而放到会话中的变量,就可以在当前会话的所有请求里使用。

session是通过HttpSession接口实现的,它提供的主要方法如下所示:

Object HttpSession.getAttribute(String name) //从session中获取信息。 void HttpSession.setAttribute(String name, Object value)//向session中保存信息。 HttpSession HttpServletRequest.getSessio() //获取当前请求所在的session的对象。

session的开始时刻比较容易判断,它从浏览器发出第一个HTTP请求即可认为会话开始。但结束时刻就不好判断了,因为浏览器关闭时并不会通知服务器,所以只能通过如下这种方法判断:如果一定的时间内客户端没有反应,则认为会话结束。Tomcat的默认值为120分钟,但这个值也可以通过HttpSession的setMaxInactiveInterval()方法来设置:

void setMaxInactiveInterval(int interval)

如果想主动让会话结束,例如用户单击“注销”按钮的时候,可以使用 HttpSession 的 invalidate()方法,用于强制结束当前session:void invalidate()

(3)request作用域

一个HTTP请求的处理可能需要多个Servlet合作,而这几个Servlet之间可以通过某种方式传递信息,但这个信息在请求结束后就无效了。request里的变量可以跨越forward前后的两页。但是只要刷新页面,它们就重新计算了。如果把变量放到request里,就说明它的作用域是request,它的有效范围是当前请求周期。 所谓请求周期,就是指从http请求发起,到服务器处理结束,返回响应的整个过程。在这个过程中可能使用forward的方式跳转了多个jsp页面,在这些页面里你都可以使用这个变量。

Servlet之间的信息共享是通过HttpServletRequest接口的两个方法来实现的:

void setAttribute(String name, Object value) //将对象value以name为名称保存到request作用域中。 Object getAttribute(String name) //从request作用域中取得指定名字的信息。

JSP中的doGet()、doPost()方法的第一个参数就是HttpServletRequest对象,使用这个对象的 setAttribute()方法即可传递信息。那么在设置好信息之后,要通过何种方式将信息传给其他的Servlet呢?这就要用到RequestDispatcher接口的forward()方法,通过它将请求转发给其他Servlet。

RequestDispatcher

ServletContext.getRequestDispatcher(String path) //取得Dispatcher以便转发,path为转发的目的Servlet。

void RequestDispatcher.forward(ServletRequest request, ServletResponse response)//将request和response转发

因此,只需要在当前Servlet中先通过setAttribute()方法设置相应的属性,然后使用forward()方法进行跳转,最后在跳转到的Servlet中通过使用getAttribute()方法即可实现信息传递。

需要注意两点:

转发不是重定向,转发是在Web应用内部进行的。

转发对浏览器是透明的,也就是说,无论在服务器上如何转发,浏览器地址栏中显示的仍然是最初那个Servlet的地址。

(4)page作用域

page对象的作用范围仅限于用户请求的当前页面,对于page对象的引用将在响应返回给客户端之后被释放,或者在请求被转发到其他地方后被释放。page里的变量只要页面跳转了,它们就不见了。如果把变量放到pageContext里,就说明它的作用域是page,它的有效范围只在当前jsp页面里。从把变量放到pageContext开始,到jsp页面结束,你都可以使用这个变量。

以上介绍的作用范围越来越小,request和page的生命周期都是短暂的,它们之间的区别:一个request可以包含多个page页(include,forward及filter)。

为了让大家更容易理解application、session、request、page 4个对象的作用范围,我们给出两个程序来进行详细说明。

【程序1】page01.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <jsp:useBean id="pagevar" scope="page" class="java.lang.StringBuffer"/> <jsp:useBean id="requestvar" scope="request" class="java.lang.StringBuffer"/> <jsp:useBean id="sessionvar" scope="session" class="java.lang.StringBuffer"/> <jsp:useBean id="applicationvar" scope="application" class="java.lang.StringBuffer"/> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP内置对象作用域</title> </head> <body> <% pagevar.append("page01"); requestvar.append("page01"); sessionvar.append("page01"); applicationvar.append("page01"); %> <jsp:forward page="page02.jsp"/> </body> </html>

【程序2】page02.jsp

<%@ page language="java" import="java.util.*" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <jsp:useBean id="pagevar" scope="page" class="java.lang.StringBuffer"/> <jsp:useBean id="requestvar" scope="request" class="java.lang.StringBuffer"/> <jsp:useBean id="sessionvar" scope="session" class="java.lang.StringBuffer"/> <jsp:useBean id="applicationvar" scope="application" class="java.lang.StringBuffer"/> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JSP内置对象作用域</title> </head> <body> <% pagevar.append("page02"); requestvar.append("page02"); sessionvar.append("page02"); applicationvar.append("page02"); %> page = <%=pagevar.toString()%><br/> request = <%=requestvar.toString()%><br/> session = <%=sessionvar.toString()%><br/> application = <%=applicationvar.toString()%><br/> </body> </html>

测试步骤以及结果分析:

直接运行程序1的结果为:(图1)

我把面试问烂了的 Java Web 面试题总结了一下带答案,建议收藏

我们看到,page的作用域的值为page02,说明确实只在当前的页面起作用,即跳转到的page2页面;request的作用域在当前请求中有效,所以其值为程序1和跳转到程序2之和;session的作用域为当前会话,所以其值也是程序1和跳转到程序2之和;而application对所有应用有效,也就是只要在应用,都要叠加,即程序1中的值与程序2中的值的叠加;

不要关闭程序1运行的浏览器,直接运行程序2,其结果为:(图2)

我把面试问烂了的 Java Web 面试题总结了一下带答案,建议收藏

对比图1的结果,我们发现page作用域没有变化,它的值只是程序2里的值;request作用域仅在当前请求作用,故也以程序2的值为准,变成page02;session的作用域为当前会话,因为运行程序1的浏览器保持着,说明还处于同一会话中,所以要在之前的基础上叠加上一个page02;而application对所有应用有效,也就是只要在应用,都要叠加,即在之前的基础上叠加上一个程序2的page02;

将上两步运行程序1和程序2的浏览器关闭,但不关闭服务器,重新运行程序2,其结果如下:

我把面试问烂了的 Java Web 面试题总结了一下带答案,建议收藏

对比之前的结果,我们发现page作用域依旧没有变化,它的值只是程序2即所在页面里的值;request作用域仅在当前请求作用,故也以程序2的值为准,变成page02;session的作用域为当前会话,因为前两步运行程序的浏览器关闭了,说明之前的会话都结束了,所以其值恢复成当前的程序2里的值page02;而application对所有应用有效,也就是只要在应用即服务器还没重启清空,都要叠加,即在之前的基础上再叠加上一个程序2的page02;

9、如何实现JSP或Servlet的单线程模式?

答:

对于JSP页面,可以通过page指令进行设置。

<%@page isThreadSafe=”false”%>

默认为true,是多线程模式

对于servlet,自定义servlet实现SingleThreadModel接口即可

**说明:**如果将JSP或Servlet设置成单线程工作模式,会导致每个请求创建一个Servlet实例,这种实践将导致严重的性能问题(服务器的内存压力很大,还会导致频繁的垃圾回收),所以通常情况下并不会这么做。

10、实现会话跟踪的技术有哪些?

会话跟踪是一种灵活、轻便的机制,它使Web上的状态编程变为可能。

HTTP是一种无状态协议,每当用户发出请求时,服务器就会做出响应,客户端与服务器之间的联系是离散的、非连续的。

当用户在同一网站的多个页面之间转换时,根本无法确定是否是同一个客户,会话跟踪技术就可以解决这个问题。

当一个客户在多个页面间切换时,服务器会保存该用户的信息。

有四种方法可以实现会话跟踪技术:URL重写、隐藏表单域、Cookie、Session。

1)隐藏表单域:,非常适合步需要大量数据存储的会话应用。

2)URL 重写:URL 可以在后面附加参数,和服务器的请求一起发送,这些参数为名字/值对。

3)Cookie:一个 Cookie 是一个小的,已命名数据元素。服务器使用 SET-Cookie 头标将它作为 HTTP响应的一部分传送到客户端,客户端被请求保存 Cookie 值,在对同一服务器的后续请求使用一个Cookie 头标将之返回到服务器。与其它技术比较,Cookie 的一个优点是在浏览器会话结束后,甚至在客户端计算机重启后它仍可以保留其值。

4)Session:使用 setAttribute(String str,Object obj)方法将对象捆绑到一个会话

实现URL重写

URL重写在客户端浏览器不支持Cookie(下章介绍)的情况下使用的,它是客户端浏览器请求服务器时,URL地址后面加上类似于 “SESSIONID=***”形式的参数,服务器端通过获取SESSIONID关键字来获取会话值。

我把面试问烂了的 Java Web 面试题总结了一下带答案,建议收藏

在程序第一次访问服务器端时,服务端并不能确认客户端浏览器是否支持Cookie。因此,当服务器第一次发出请求时,服务端会默认采用URL重写,也就是将SESSIONID写到URL地址中传递。

核心代码:String SessionId=

request.getRequestedSessionId();

当客户端发送请求后,服务器会根据提交给客户端浏览器的信息自动检查客户端是否启用了Cookie,如果启用,将不再进行URL重写。如果没有,则继续使用URL重写。

通过response对象的encodeURL(String url)方法可以进行URL重写。

public String encodeURL(String url);

对包含SessionID的URL进行编码。如果不需要编码,就直接返回这个URL。

Servlet引擎必须提供URL编码方法,因为在有些情况下,我们将不得不重写URL。

例如,在响应对应的请求中包含一个有效的Session,但是这个Session不能被非URL的(例如Cookie)的手段来维持。

所以所有提供给Servlet的URL都应通过这个方法运行,这样才能确保会话跟踪能够在所有浏览器中正常运行。

隐藏域和URL重写有着共同的优点:它们在Cookie被禁用或者根本不支持的情况下依旧能够工作。

缺点:所有页面必须是表单提交之后的结果,还有涉及许多冗长的处理工作。

11、过滤器有哪些作用和用法?

答:对于web应用来说,过滤器是一个驻留在服务器端的Web组件,它可以截取客户端和服务器之间的请求与响应信息,并对这些信息进行。当Web容器接收到一个对资源的请求时,它将判断是否有过滤器与这个资源关联,如果有,那么容器将这个请求交给过滤器处理。在过滤器中,你可以改变请求的内容,或者重新设置请求的报头信息,然后再将请求发送给目标资源。当目标资源对请求作出响应的时候,容器同样会将响应先转发给过滤器,在过滤器中你可以对响应的内容进行转换,然后再将响应发送给客户端。

常见的过滤器用途有:对用户请求进行统一的认证、对用户的访问请求进行审核和记录、对用户发送的数据进行过滤或替换、转换图像格式、对响应内容进行压缩以减少传输量、对请求或响应进行加密处理、触发资源访问事件、对XML输出应用XSLT等。

过滤器和拦截器的区别:

拦截器基于Java的反射机制,而过滤器基于函数回调;

拦截器不依赖于servlet容器,而过滤器依赖于servlet容器;

拦截器只能对action请求起作用,过滤器可以对几乎所有的请求起作用;

拦截器可以访问action上下文,值栈里的对象,而过滤器不能访问;

在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化的时候被调用一次;

拦截器可以获取IOC容器中的各个bean,而过滤器不行,在拦截器里注入一个service,可以调用业务逻辑;

拦截器需要在Spring配置文件中配置,过滤器只需要在web.xml中配置。

12、监听器有哪些作用和用法?

监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行。

监听原理

存在事件源

提供监听器

为事件源注册监听器

操作事件源,产生事件对象,将事件对象传递给监听器,并且执行监听器相应监听方法

监听器典型案例:监听window窗口的事件监听器

例如:swing开发首先制造Frame**窗体**,窗体本身也是一个显示空间,对窗体提供监听器,监听窗体方法调用或者属性改变:

* 关闭窗体时,调用windowListener 的windowclosing() , 传递windowEvent参数,表示窗体关闭事件对象

* 事件对象操作事件源,获得事件源状态

自定义监听器

以上内容可以用下图解释:

我把面试问烂了的 Java Web 面试题总结了一下带答案,建议收藏

通过person.addPersonListener(new PersonListener(){})使事件源与监听器间产生联系。

事件源(在事件源方法中创建事件对象):

/** * 事件源对象,被监听 * */ public class Person{ private String name; private int weight; private PersonListener listener; //注册监听器 public void addPersonListener(Personlistener listener){ this.listener = listener; } //吃法 public void eat(){ //体重增加 weight+=5; //在方法中调用监听器方法 if(listener != null){ //监听器存在 //创建事件对象-----通过事件对象可以获得事件源 PersonEvent event = new PersonEvent(this); listener.personeating(event); } } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } }

监听器(参数是事件对象)

/** * 监听器接口,提供对监听相应方法 * */ public interface PersonListener{ public void personeating(PersonEvent euent);//监听事件 }

事件对象(通过事件对象可以获得事件源)

/** * 事件对象,当事件源发生动作改变,产生相应事件对象,该对象被传递给监听器指定方法 * */ public class PersonEvent{ public Object source;//事件源 public Object getSource(){ return source; } public void setSource(Object source){ this.source = source; } //构造事件对象时,接收事件源 public PersonEvebt(Person person){ this.source = person; } }

测试方法

public class PersonTest(){ public static void main(){ //步骤一:创建事件源 Person person = new Person(); person.setName("Megustas"); person.setWeight(100); //步骤二 创建监听器,注册写到一起 person.addPersonListener(new PersonListener(){ @Override public void personeating(PersonEvent event) { System.out.println("监听到了,人正在吃饭!"); // 在监听方法中可以用来获得 事件源 状态 Person person = (Person) event.getSource(); System.out.println(person.getName()); System.out.println(person.getWeight()); } }); // 步骤三 操作事件源 person.eat();// 结果监听方法被调用 } }

对上的匿名类补充:匿名内部类的作用是创建一个实现接口的匿名类对象,含义是创建一个继承自PersonListener的匿名类的对象),通过new表达式返回的引用被自动向上转型为对PersonListener的引用

Servlet监听器

(不需要配置,但是监听器仍需要进行注册)

在Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别为 ServletContext, HttpSession 和 ServletRequest 这三个域对象。

Servlet监听器分为三大类

数据域对象创建和销毁监听器

数据域对象和属性变更监听器

绑定到 HttpSession 域中的某个对象的状态的事件监听器

(一)数据域对象创建销毁监听器 — 监听三个与对象 (三个监听器)

(1)ServletContextListener : 用来监听ServletContext对象的创建和销毁

contextInitialized(ServletContextEvent sce) 监听创建

contextDestroyed(ServletContextEvent sce) 监听销毁

* ServletContext对象代表全局唯一对象,每个web工程会产生一个ServletContext,服务器启动创建,服务器关闭销毁

编写监听器

步骤一:编写类实现特定监听器接口

步骤二:注册监听器,不是通过事件源,而是在web.xml 进行配置

(监听器和Servlet、Filter不同,不需要url配置,监听器执行不是由用户访问的,监听器 是由事件源自动调用的)

<listener> <listener-class> cn.megustas.listener.MyServletContextListener </listener-class> </listener>

servletContext域对象何时创建和销毁:

创建:服务器启动针对每一个web应用创建servletcontext

销毁:服务器关闭前先关闭代表每一个web应用的servletContext

ServletContextListener主流应用:

第一个:在服务器启动时,对一些对象进行初始化,并且将对象保存ServletContext数据范围内(因为在监听器内可以获得事件源对象) — 全局数据

例如:创建数据库连接池

第二个:对框架进行初始化 例如:Spring框架初始化通过ServletContextListener (因为监听器代码在服务器启动时执行)

Spring框架(配置文件随服务器启动加载)

org.springframework.web.context.ContextLoaderListener

第三个:实现任务调度,启动定时程序 (Timer、TimerTask) 使一个程序,定时执行

import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class MyServletContextListener implements ServletContextListener { @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("监听ServletContext对象销毁了..."); } @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("监听ServletContext对象创建了..."); // 获得事件源(本质也是getSource()的调用) ServletContext servletContext = sce.getServletContext();//servletContext是全局对象 // 向ServletContext 中保存数据 // 启动定时器 final Timer timer = new Timer(); // 启动定时任务 timer.schedule(new TimerTask() { @Override // 这就是一个线程 public void run() { System.out.println("定时器执行了..."); } }, 0, 3000); // 马上启动 每隔3秒重复执行 // 指定时间启动定时器 DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); try { Date first = dateFormat.parse("2016-12-28 10:42:00"); timer.schedule(new TimerTask() { int i; @Override public void run() { i++; System.out.println("从10点40分开始启动程序,每隔3秒重复执行"); //执行10次 if (i == 10) { timer.cancel();// 取消定时器任务 } } }, first, 3000); } catch (ParseException e) { e.printStackTrace(); } } }

比如说每天晚上十二点给过生日的人进行生日祝福,中国移动对账户进行同步,会在服务器使用较少的时间,例如凌晨之类,启动一段程序,进行同步

java.util.Timer 一种线程设施,用于安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。

Timer提供了启动定时任务方法 schedule

* schedule(TimerTask task, Date firstTime, long period) 用来在指定一个时间启动定时器,定期循环执行

* schedule(TimerTask task, long delay, long period) 用来在当前时间delay多少毫秒后启动定时器

停止定时器,timer.cancel取消任务

(2)HttpSession 数据对象创建和销毁监听器 —– HttpSessionListener

sessionCreated(HttpSessionEvent se) 监听Session对象创建

sessionDestroyed(HttpSessionEvent se) 监听Session对象销毁

Session何时创建:request.getSession()

Session何时销毁:关闭服务器,Session过期,session.invalidate

*Session过期时间通过web.xml配置(tomcat配置文件中),默认时间30分钟

配置:

<listener> <listener-class> cn.megustas.listener.MyHttpSessionListener </listener-class> </listener>

HttpSession监听器

public class MyHttpSessionListener implements HttpSessionListener{ @Override public void sessionCreated(HttpSessionEvent se){ //获得session中的id,通过事件对象 System.out.println("session对象被创建了"); HttpSession session = request.getSession(); System.out.println("id" + session.getId()); } }

现有如下JSP页面:

1.jsp

<body> <h1>这是一个JSP文件</h1> <a href="/megustas/demo1/2.jsp">销毁Session</a> </body>

2.jsp

<% session.invalidate(); %>

**访问1.jsp时会执行监听器原因:**因为如果观察jsp的源码,jsp会被预处理成.java代码(在tomcat中work文件夹下),我们打开这个.java代码的源码:

try ( response.setContentType("text/html; charset=UTF-8") ; pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true) ; _jspx_page_context = pageContext; application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSessirn(); out = pageContext.getOut() ; _jspx_out = out;

其中的getSession的实现实际就是request.getSession()

(3)HttpServletRequest对象的创建和销毁监听器 —- ServletRequestListener

requestInitialized(ServletRequestEvent sre)—-监听request对象创建

requestDestroyed(ServletRequestEvent sre) 监听request对象销毁

Request何时创建:请求发起时创建

Request何时销毁:响应结束时销毁

例如:每次刷新界面都会创建销毁一次

注意(创建销毁次数由请求次数决定):

使用forward —- request创建销毁几次 —– 一次

使用sendRedirect —- request创建销毁两次 (两次请求)

(二)

ServletContext/HttpSession/ServletRequest中保存数据 创建、修改、移除监听器

ServletContextAttributeListener 监听ServletContext中属性变化

HttpSessionAttributeListener 监听HttpSession中属性变化

ServletRequestAttributeListener 监听ServletRequest中属性变化

attributeAdded 监听属性添加 —- 当数据范围对象没有该属性,第一次添加时setAttribute(name,value);调用执行

attributeRemoved 监听属性移除 —- 从一个数据范围对象删除一个已经存在属性removeAttribute(name);执行

attributeReplaced 监听属性替换 —– 当一个数据范围已经存在一个属性,向数据范围添加相同名称属性setAttribute(name,value);触发替换方法

例如,此处我们用

HttpSessionAttributeListener举例(ServletContextListener与ServletRequestListener同理):

JSP页面

<% //属性添加,向session数据范围保存名称为那么,值为张三的属性 //注意此时是name属性的创建,触发attributeAdded session.setAttribute("name","张三"); //将session中的name属性值替换为李四 session.setAttribute("name",李四);//触发attributeReplaced //移除name属性 session.removeAttribute("name");//触发attributeReplaced %>

监听器

public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener { @Override public void attributeAdded(HttpSessionBindingEvent se) { // 属性添加 System.out.println("向session添加了一个属性..."); // 知道添加属性名称和值 HttpSession session = se.getSession(); // se.getValue 但是这个方法不会返回当前值 System.out.println("属性名称:" + se.getName()); System.out.println("属性值:" + session.getAttribute(se.getName())); } @Override public void attributeRemoved(HttpSessionBindingEvent se) { // 属性移除 System.out.println("从session移除了一个属性...."); System.out.println("属性名称:" + se.getName()); } @Override public void attributeReplaced(HttpSessionBindingEvent se) { // 属性替换 System.out.println("将session中一个属性值替换为其他值..."); HttpSession session = se.getSession(); System.out.println("属性名称:" + se.getName()); System.out.println("属性值:" + session.getAttribute(se.getName())); } }

注册

<listener> <listener-class> cn.megustas.servlet.listener.MyHttpSessionAttributeListener <listener-class> </listener>

注意:获得返回值通过session.getAttribute(se.getName())

(三)被绑定Session对象,自我状态感知监听器

保存在 Session 域中的对象可以有多种状态:绑定到 Session 中;从 Session 域中解除绑定;随 Session 对象持久化到一个存储设备中(钝化);随 Session 对象从一个存储设备中恢复(活化)

被存放Session的Java对象,感知自我四种状态变化

被绑定

被解除绑定

被钝化 —– 数据从内存序列化硬盘

被活化 —- 数据从硬盘重新加载回内存

HttpSessionBindingListener实现接口的java对象,感知自己被绑定到Session或者从Session中解除绑定

HttpSessionActivationListener实现接口的java对象,感知从内存被钝化硬盘上,从硬盘活化到内存中

实现这两个接口的类不需要 web.xml 文件中进行注册,都是由Session自主完成的,例如在存储对象的时候会自动调用绑定

HttpSessionBindingListener

* valueBound(HttpSessionBindingEvent event) 绑定对象方法 —- session.setAttribute(name,Object);

* valueUnbound(HttpSessionBindingEvent event) 解除绑定方法 —–session.removeAttribute()、当Session对象销毁时,当中所有绑定对象解除绑定

JSP页面:

<body> <% Bean1 bean1 = new Bean1(); bean1.setId(100); bean1.setName("Megustas"); // 将bean1对象绑定Session中,Bean1类中的valueBound方法被调用 session.setAttribute("bean1",bean1); Bean1 bean2 = new Bean1(); bean2.setId(200); bean2.setName("玛丽"); //这个代码会执行两次绑定和一次解除绑定 session.setAttribute("bean1",bean2); // 解除绑定 session.removeAttribute("bean1"); %> ${bean1.name } </body> /** * 使Bean1对象感知 自我被绑定Session中,感知自我被Session解除绑定 * */ public class Bean1 implements HttpSessionBindingListener { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public void valueBound(HttpSessionBindingEvent event) { System.out.println("Bean1对象被绑定了..."); // 当前对象,操作对象 System.out.println("绑定对象name:" + this.name); } @Override public void valueUnbound(HttpSessionBindingEvent event) { System.out.println("Bean1对象被解除绑定了..."); System.out.println("解除绑定对象name:" + this.name); } }* 使Bean1对象感知 自我被绑定Session中,感知自我被Session解除绑定

HttpSessionActivationListener

* sessionDidActivate(HttpSessionEvent se)感知对象被活化

* sessionWillPassivate(HttpSessionEvent se) 感知对象被钝化

使用场景:Session保存数据,很长一段时间没用,但是不能销毁Session对象,不想占用服务器内存资源 —– 钝化(将服务器内存中数据序列化硬盘上)

/** * 感知钝化和活化 * */ public class Bean2 implements HttpSessionActivationListener, Serializable { private String name; private double price; public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } @Override public void sessionDidActivate(HttpSessionEvent se) { System.out.println("bean2对象被活化..."); } @Override public void sessionWillPassivate(HttpSessionEvent se) { System.out.println("bean2对象被钝化..."); } }

JSP界面

<body> <!-- 将javabean 对象保存Session中 --> <% Bean2 bean2 = new Bean2(); bean2.setName("联想笔记本"); bean2.setPrice(5000); session.setAttribute("bean2",bean2); %> </body>

读取数据

<body> <!-- 读取javabean对象的数据 --> 读取bean2的数据: ${bean2.name } , ${bean2.price } </body>

注意

钝化和活化应该由tomcat服务器 自动进行 —- 配置tomcat

<Context> //文件在1分钟之后执行钝化 <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1"> //文件钝化到it315文件夹中 <Store className="org.apache.catalina.session.FileStore" directory="it315"/> </Manager> </Context>

配置context有几个位置?

1、tomcat/conf/context.xml 对所有虚拟主机 所有web工程生效

2、

tomcat/conf/Catalina/localhost/context.xml 对当前虚拟主机所有web工程生效

3、当前工程/META-INF/context.xml 对当前工程有效

钝化后 it315目录在哪里?在“

tomcat/work/Catalina/localhost/项目名”目录中

java对象如果想实现序列化,需要实现Serializable接口(因此上述Bean2实现Serializable接口,才可以被钝化,并之后进行活化并读取)

13、web.xml文件中可以配置哪些内容?

答:web.xml用于配置Web应用的相关信息,如:监听器(listener)、过滤器(filter)、 Servlet、相关参数、会话超时时间、安全验证方式、错误页面等,下面是一些开发中常见的配置:

①配置Spring上下文加载监听器加载Spring配置文件并创建IoC容器:

<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>

②配置Spring的OpenSessionInView过滤器来解决延迟加载和Hibernate会话关闭的矛盾:

<filter> <filter-name>openSessionInView</filter-name> <filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter </filter-class> </filter> <filter-mapping> <filter-name>openSessionInView</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

③配置会话超时时间为10分钟:

<session-config> <session-timeout>10</session-timeout> </session-config>

④配置404和Exception的错误页面:

<error-page> <error-code>404</error-code> <location>/error.jsp</location> </error-page> <error-page> <exception-type>java.lang.Exception</exception-type> <location>/error.jsp</location> </error-page>

⑤配置安全认证方式:

<security-constraint> <web-resource-collection> <web-resource-name>ProtectedArea</web-resource-name> <url-pattern>/admin/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> </login-config> <security-role> <role-name>admin</role-name> </security-role>

**说明:**对Servlet(小服务)、Listener(监听器)和Filter(过滤器)等Web组件的配置,Servlet 3规范提供了基于注解的配置方式,可以分别使用@WebServlet、@WebListener、@WebFilter注解进行配置。

**补充:**如果Web提供了有价值的商业信息或者是敏感数据,那么站点的安全性就是必须考虑的问题。安全认证是实现安全性的重要手段,认证就是要解决“Are you who you say you are?”的问题。认证的方式非常多,简单说来可以分为三类:

A. What you know? — 口令

B. What you have? — 数字证书(U盾、密保卡)

C. Who you are? — 指纹识别、虹膜识别

在Tomcat中可以通过建立安全套接字层(Secure Socket Layer, SSL)以及通过基本验证或表单验证来实现对安全性的支持。

14、你的项目中使用过哪些JSTL标签?

答:项目中主要使用了JSTL的核心标签库,包括<c:if>、<c:choose>、<c: when>、<c: otherwise>、<c:forEach>等,主要用于构造循环和分支结构以控制显示逻辑。

**说明:**虽然JSTL标签库提供了core、sql、fmt、xml等标签库,但是实际开发中建议只使用核心标签库(core),而且最好只使用分支和循环标签并辅以表达式语言(EL),这样才能真正做到数据显示和业务逻辑的分离,这才是最佳实践。

15、使用标签库有什么好处?如何自定义JSP标签?

答:使用标签库的好处包括以下几个方面:

分离JSP页面的内容和逻辑,简化了Web开发;

开发者可以创建自定义标签来封装业务逻辑和显示逻辑;

标签具有很好的可移植性、可维护性和可重用性;

避免了对Scriptlet(小脚本)的使用(很多公司的项目开发都不允许在JSP中书写小脚本)

自定义JSP标签包括以下几个步骤:

编写一个Java类实现实现Tag/BodyTag/IterationTag接口(开发中通常不直接实现这些接口而是继承

TagSupport/BodyTagSupport/SimpleTagSupport类,这是对缺省适配模式的应用),重写doStartTag()、doEndTag()等方法,定义标签要完成的功能

编写扩展名为tld的标签描述文件对自定义标签进行部署,tld文件通常放在WEB-INF文件夹下或其子目录中

在JSP页面中使用taglib指令引用该标签库

下面是一个自定义标签库的例子。

步骤1 - 标签类源代码TimeTag.java:

package com.hzy.tags; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.TagSupport; public class TimeTag extends TagSupport { private static final long serialVersionUID = 1L; private String format = "yyyy-MM-dd hh:mm:ss"; private String foreColor = "black"; private String backColor = "white"; public int doStartTag() throws JspException { SimpleDateFormat sdf = new SimpleDateFormat(format); JspWriter writer = pageContext.getOut(); StringBuilder sb = new StringBuilder(); sb.append(String.format("<span style=color:%s;background-color:%s>%s</span>", foreColor, backColor, sdf.format(new Date()))); try { writer.print(sb.toString()); } catch(IOException e) { e.printStackTrace(); } return SKIP_BODY; } public void setFormat(String format) { this.format = format; } public void setForeColor(String foreColor) { this.foreColor = foreColor; } public void setBackColor(String backColor) { this.backColor = backColor; } }

步骤2 - 编写标签库描述文件my.tld:

<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0"> <description>定义标签库</description> <tlib-version>1.0</tlib-version> <short-name>MyTag</short-name> <tag> <name>time</name> <tag-class>com.jackfrued.tags.TimeTag</tag-class> <body-content>empty</body-content> <attribute> <name>format</name> <required>false</required> </attribute> <attribute> <name>foreColor</name> </attribute> <attribute> <name>backColor</name> </attribute> </tag> </taglib>

步骤3 - 在JSP页面中使用自定义标签:

<%@ page pageEncoding="UTF-8"%> <%@ taglib prefix="my" uri="/WEB-INF/tld/my.tld" %> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE html> <html> <head> <base href="<%=basePath%>"> <title>首页</title> <style type="text/css"> * { font-family: "Arial"; font-size:72px; } </style> </head> <body> <my:time format="yyyy-MM-dd" backColor="blue" foreColor="yellow"/> </body> </html>

**提示:**如果要将自定义的标签库发布成JAR文件,需要将标签库描述文件(tld文件)放在JAR文件的META-INF目录下,可以JDK中的jar工具完成JAR文件的生成,如果不清楚如何操作,可以请教谷老师和百老师。

16、说一下表达式语言(EL)的隐式对象及其作用。

答:EL的隐式对象包括:pageContext、initParam(访问上下文参数)、param(访问请求参数)、paramValues、header(访问请求头)、headerValues、cookie(访问cookie)、applicationScope(访问application作用域)、sessionScope(访问session作用域)、requestScope(访问request作用域)、pageScope(访问page作用域)。

用法如下所示:

${pageContext.request.method} ${pageContext["request"]["method"]} ${pageContext.request["method"]} ${pageContext["request"].method} ${initParam.defaultEncoding} ${header["accept-language"]} ${headerValues["accept-language"][0]} ${cookie.jsessionid.value} ${sessionScope.loginUser.username}

**补充:**表达式语言的.和[]运算作用是一致的,唯一的差别在于如果访问的属性名不符合Java标识符命名规则,例如上面的accept-language就不是一个有效的Java标识符,那么这时候就只能用[]运算符而不能使用.运算符获取它的值

17、表达式语言(EL)支持哪些运算符?

答:除了.和[]运算符,EL还提供了:

算术运算符:+、-、*、/或div、%或mod

关系运算符:==或eq、!=或ne、>或gt、>=或ge、<或lt、<=或le

逻辑运算符:&&或and、||或or、!或not

条件运算符:${statement? A : B}(跟Java的条件运算符类似)

empty运算符:检查一个值是否为null或者空(数组长度为0或集合中没有元素也返回true)

18、Java Web开发的Model 1和Model 2分别指的是什么?

答:Model 1是以页面为中心的Java Web开发,使用JSP+JavaBean技术将页面显示逻辑和业务逻辑处理分开,JSP实现页面显示,JavaBean对象用来保存数据和实现业务逻辑。Model 2是基于MVC(模型-视图-控制器,Model-View-Controller)架构模式的开发模型,实现了模型和视图的彻底分离,利于团队开发和代码复用,如下图所示。

我把面试问烂了的 Java Web 面试题总结了一下带答案,建议收藏

19、Servlet 3中的异步处理指的是什么?**

答:在Servlet 3中引入了一项新的技术可以让Servlet异步处理请求。有人可能会质疑,既然都有多线程了,还需要异步处理请求吗?答案是肯定的,因为如果一个任务处理时间相当长,那么Servlet或Filter会一直占用着请求处理线程直到任务结束,随着并发用户的增加,容器将会遭遇线程超出的风险,这这种情况下很多的请求将会被堆积起来而后续的请求可能会遭遇拒绝服务,直到有资源可以处理请求为止。异步特性可以帮助应用节省容器中的线程,特别适合执行时间长而且用户需要得到结果的任务,如果用户不需要得到结果则直接将一个Runnable对象交给Executor并立即返回即可。

**补充:**多线程在Java诞生初期无疑是一个亮点,而Servlet单实例多线程的工作方式也曾为其赢得美名,然而技术的发展往往会颠覆我们很多的认知,就如同当年爱因斯坦的相对论颠覆了牛顿的经典力学一般。事实上,异步处理绝不是Serlvet 3首创,如果你了解Node.js的话,对Servlet 3的这个重要改进就不以为奇了。

下面是一个支持异步处理请求的Servlet的例子。

import java.io.IOException; import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(urlPatterns = {"/async"}, asyncSupported = true) public class AsyncServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 开启Tomcat异步Servlet支持 req.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true); final AsyncContext ctx = req.startAsync(); // 启动异步处理的上下文 // ctx.setTimeout(30000); ctx.start(new Runnable() { @Override public void run() { // 在此处添加异步处理的代码 ctx.complete(); } }); } }

20、如何在基于Java的Web项目中实现文件上传和下载?

答:在Sevlet 3 以前,Servlet API中没有支持上传功能的API,因此要实现上传功能需要引入第三方工具从POST请求中获得上传的附件或者通过自行处理输入流来获得上传的文件,我们推荐使用Apache的commons-fileupload。

从Servlet 3开始,文件上传变得无比简单,相信看看下面的例子一切都清楚了。

上传页面index.jsp:

<%@ page pageEncoding="utf-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Photo Upload</title> </head> <body> <h1>Select your photo and upload</h1> <hr/> <div style="color:red;font-size:14px;">${hint}</div> <form action="UploadServlet" method="post" enctype="multipart/form-data"> Photo file: <input type="file" name="photo" /> <input type="submit" value="Upload" /> </form> </body> </html>

支持上传的Servlet:

package com.hzy.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; @WebServlet("/UploadServlet") @MultipartConfig public class UploadServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 可以用request.getPart()方法获得名为photo的上传附件 // 也可以用request.getParts()获得所有上传附件(多文件上传) // 然后通过循环分别处理每一个上传的文件 Part part = request.getPart("photo"); if (part != null && part.getSubmittedFileName().length() > 0) { // 用ServletContext对象的getRealPath()方法获得上传文件夹的绝对路径 String savePath = request.getServletContext().getRealPath("/upload"); // Servlet 3.1规范中可以用Part对象的getSubmittedFileName()方法获得上传的文件名 // 更好的做法是为上传的文件进行重命名(避免同名文件的相互覆盖) part.write(savePath + "/" + part.getSubmittedFileName()); request.setAttribute("hint", "Upload Successfully!"); } else { request.setAttribute("hint", "Upload failed!"); } // 跳转回到上传页面 request.getRequestDispatcher("index.jsp").forward(request, response); } }

21、服务器收到用户提交的表单数据,到底是调用Servlet的doGet()还是doPost()方法?

答:HTML的元素有一个method属性,用来指定提交表单的方式,其值可以是get或post。我们自定义的Servlet一般情况下会重写doGet()或doPost()两个方法之一或全部,如果是GET请求就调用doGet()方法,如果是POST请求就调用doPost()方法,那为什么为什么这样呢?我们自定义的Servlet通常继承自HttpServlet,HttpServlet继承自GenericServlet并重写了其中的service()方法,这个方法是Servlet接口中定义的。HttpServlet重写的service()方法会先获取用户请求的方法,然后根据请求方法调用doGet()、doPost()、doPut()、doDelete()等方法,如果在自定义Servlet中重写了这些方法,那么显然会调用重写过的(自定义的)方法,这显然是对模板方法模式的应用(如果不理解,请参考阎宏博士的《Java与模式》一书的第37章)。当然,自定义Servlet中也可以直接重写service()方法,那么不管是哪种方式的请求,都可以通过自己的代码进行处理,这对于不区分请求方法的场景比较合适。

22、JSP中的静态包含和动态包含有什么区别?

答:静态包含是通过JSP的include指令包含页面,动态包含是通过JSP标准动作jsp:forward包含页面。静态包含是编译时包含,如果包含的页面不存在则会产生编译错误,而且两个页面的"contentType"属性应保持一致,因为两个页面会合二为一,只产生一个class文件,因此被包含页面发生的变动再包含它的页面更新前不会得到更新。动态包含是运行时包含,可以向被包含的页面传递参数,包含页面和被包含页面是独立的,会编译出两个class文件,如果被包含的页面不存在,不会产生编译错误,也不影响页面其他部分的执行。代码如下所示:

<%-- 静态包含 --%> <%@ include file="..." %> <%-- 动态包含 --%> <jsp:include page="..."> <jsp:param name="..." value="..." /> </jsp:include>

23、Servlet中如何获取用户提交的查询参数或表单数据?

答:可以通过请求对象(HttpServletRequest)的getParameter()方法通过参数名获得参数值。如果有包含多个值的参数(例如复选框),可以通过请求对象的getParameterValues()方法获得。当然也可以通过请求对象的getParameterMap()获得一个参数名和参数值的映射(Map)。

24、Servlet中如何获取用户配置的初始化参数以及服务器上下文参数?

答:可以通过重写Servlet接口的init(ServletConfig)方法并通过ServletConfig对象的getInitParameter()方法来获取Servlet的初始化参数。可以通过ServletConfig对象的getServletContext()方法获取ServletContext对象,并通过该对象的getInitParameter()方法来获取服务器上下文参数。当然,ServletContext对象也在处理用户请求的方法(如doGet()方法)中通过请求对象的getServletContext()方法来获得。

25、如何设置请求的编码以及响应内容的类型?

答:通过请求对象(ServletRequest)的setCharacterEncoding(String)方法可以设置请求的编码,其实要彻底解决乱码问题就应该让页面、服务器、请求和响应、Java程序都使用统一的编码,最好的选择当然是UTF-8;通过响应对象(ServletResponse)的setContentType(String)方法可以设置响应内容的类型,当然也可以通过HttpServletResponsed对象的setHeader(String, String)方法来设置。

26、解释一下网络应用的模式及其特点

答:典型的网络应用模式大致有三类:B/S、C/S、P2P。其中B代表浏览器(Browser)、C代表客户端(Client)、S代表服务器(Server),P2P是对等模式,不区分客户端和服务器。B/S应用模式中可以视为特殊的C/S应用模式,只是将C/S应用模式中的特殊的客户端换成了浏览器,因为几乎所有的系统上都有浏览器,那么只要打开浏览器就可以使用应用,没有安装、配置、升级客户端所带来的各种开销。P2P应用模式中,成千上万台彼此连接的计算机都处于对等的地位,整个网络一般来说不依赖专用的集中服务器。网络中的每一台计算机既能充当网络服务的请求者,又对其它计算机的请求作出响应,提供资源和服务。通常这些资源和服务包括:信息的共享和交换、计算资源(如CPU的共享)、存储共享(如缓存和磁盘空间的使用)等,这种应用模式最大的阻力安全性、版本等问题,目前有很多应用都混合使用了多种应用模型,最常见的网络视频应用,它几乎把三种模式都用上了。

27、什么是Web Service(Web服务)?

答:从表面上看,Web Service就是一个应用程序,它向外界暴露出一个能够通过Web进行调用的API。这就是说,你能够用编程的方法透明的调用这个应用程序,不需要了解它的任何细节,跟你使用的编程语言也没有关系。例如可以创建一个提供天气预报的Web Service,那么无论你用哪种编程语言开发的应用都可以通过调用它的API并传入城市信息来获得该城市的天气预报。之所以称之为Web Service,是因为它基于HTTP协议传输数据,这使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件,就可相互交换数据或集成。

**补充:**这里必须要提及的一个概念是SOA(Service-Oriented Architecture,面向服务的架构),SOA是一种思想,它将应用程序的不同功能单元通过中立的契约联系起来,独立于硬件平台、操作系统和编程语言,使得各种形式的功能单元能够更好的集成。显然,Web Service是SOA的一种较好的解决方案,它更多的是一种标准,而不是一种具体的技术。

28、概念解释:SOAP、WSDL、UDDI

答:

SOAP:简单对象访问协议(Simple Object Access Protocol),是Web Service中交换数据的一种协议规范。

WSDL:Web服务描述语言(Web Service Description Language),它描述了Web服务的公共接口。这是一个基于XML的关于如何与Web服务通讯和使用的服务描述;也就是描述与目录中列出的Web服务进行交互时需要绑定的协议和信息格式。通常采用抽象语言描述该服务支持的操作和信息,使用的时候再将实际的网络协议和信息格式绑定给该服务。

UDDI:统一描述、发现和集成(Universal Description, Discovery and Integration),它是一个基于XML的跨平台的描述规范,可以使世界范围内的企业在互联网上发布自己所提供的服务。简单的说,UDDI是访问各种WSDL的一个门面(可以参考设计模式中的门面模式)。

**提示:**关于Web Service的相关概念和知识可以在W3CSchool上找到相关的资料。

29、Java规范中和Web Service相关的规范有哪些?

答:Java规范中和Web Service相关的有三个:

JAX-WS(JSR 224):这个规范是早期的基于SOAP的Web Service规范JAX-RPC的替代版本,它并不提供向下兼容性,因为RPC样式的WSDL以及相关的API已经在Java EE5中被移除了。WS-MetaData是JAX-WS的依赖规范,提供了基于注解配置Web Service和SOAP消息的相关API。

JAXM(JSR 67):定义了发送和接收消息所需的API,相当于Web Service的服务器端。

JAX-RS(JSR 311 & JSR 339 & JSR 370):是Java针对REST(Representation State Transfer)架构风格制定的一套Web Service规范。REST是一种软件架构模式,是一种风格,它不像SOAP那样本身承载着一种消息协议, (两种风格的Web Service均采用了HTTP做传输协议,因为HTTP协议能穿越防火墙,Java的远程方法调用(RMI)等是重量级协议,通常不能穿越防火墙),因此可以将REST视为基于HTTP协议的软件架构。REST中最重要的两个概念是资源定位和资源操作,而HTTP协议恰好完整的提供了这两个点。HTTP协议中的URI可以完成资源定位,而GET、POST、OPTION、DELETE方法可以完成资源操作。因此REST完全依赖HTTP协议就可以完成Web Service,而不像SOAP协议那样只利用了HTTP的传输特性,定位和操作都是由SOAP协议自身完成的,也正是由于SOAP消息的存在使得基于SOAP的Web Service显得笨重而逐渐被淘汰。

30、介绍一下你了解的Java领域的Web Service框架

答:Java领域的Web Service框架很多,包括Axis2(Axis的升级版本)、Jersey(RESTful的Web Service框架)、CXF(XFire的延续版本)、Hessian、Turmeric、JBoss SOA等,其中绝大多数都是开源框架。

版权声明:本文为CSDN博主「Java程序鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:

https://blog.csdn.net/qq_35620342/article/details/119642114

0 留言

评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。
验证码