【经典】servlet中常见的问题与解答
ervlet是javaee技术当中很重要的⼀部分。所有web应⽤框架诸如spring,struts都建⽴在其上。这使得servlet成为java⾯试中的⼀个热门话题。
这⾥笔者罗列了⼀些常见的跟servlet相关的⾯试问题与解答,希望能够帮助⼤家:
1.web服务器(web server)和应⽤服务器(appliction server)的区别是什么?
web服务器的任务是处理客户端的http请求并响应相对应的html页⾯。web服务器能理解http语⾔并运⾏在http协议之上。Apache web服务器就是⼀种web服务器,此外能够执⾏servlet和jsp的容器我们称之为servlet容器,⽐如tomcat。
应⽤服务器提供⼀些额外的特性⽐如jms⽀持,事务管理的⽀持等等。所以我们说应⽤服务器就是⼀个web服务器加上⼀些额外的功能以帮助开发者开发⼀些企业级应⽤。
<和post⽅法的区别是什么?
(1)get⽅法提交的参数会通过url传递,⽽post提交的内容是放在请求体中的。
(2)get传送的数据量⽐较⼩,post传送的数据量⽐较⼤,通常认为不受限制。
(3)get⽅法不安全因为将传送数据暴露在url上,⽽post则不存在这个问题。
(4)超链接和表单的默认提交⽅式都是get。
3.MIME类型是什么?
响应头的“content-type”值就是⼀个典型的MIME类型。那什么是mime类型呢?服务器发送mime类型给客户端使其知道服务端返回的数据类型是什么。这帮助客户端如何显⽰数据。⼀些常⽤的mime类型有text/html,text/html,application/xml等。
我们可以使⽤ServletContext对象的getMimeType(file)⽅法获取⼀个⽂件正确的mime类型,然后使⽤这个类型去设置响应头的content type (即response.setContentType())。当需要从服务器下载⽂件时这种⽅法⾮常有⽤。
4.什么是web应⽤,其⽬录结构是怎样的?
web应⽤就是⼀个可以运⾏在服务器上的提供静态和动态内容给客户端浏览器的⼀个模型。⽬录结构如下:
5.servlet容器的任务有哪些?
servlet容器也叫web容器,⽐如tomcat。它的主要职责如下:
(1)提供通信⽀持(communication support):容器为web服务器和servlet/jsp提供了便捷的通信⽅式。正是由于web容器,我们不再需在服务端创建⼀个服务端socket对象监听、解析任何请求以及⽣成响应。所有这些重要且复杂的⼯作统统由容器为我们完成,我们仅仅需要关注我们⾃⼰应⽤的业务逻辑⽽已。(2)⽣命周期和资源管理(lifecycle and resource management):容器管理所有servlet的⽣命周期,它还负责加载servlet到内存,初始化servler,执⾏servlet中的⽅法以及销毁servlet,容器同时还提供诸如JNDI(java命名与⽬录服务)的资源管理⼯具。
(3)多线程⽀持(multitheading support):容器为每个请求创建⼀个新的线程并当请求结束时及时摧毁线程。servlet只有⼀个实例,容器并不会为每个请求⽣成单独的servlet,这必然节省了时间和空间。
(4)⽀持jsp(jsp support):jsp跟普通java类还是有区别的,web容器对此也是提供⽀持的。每个jsp页⾯在被容器编译后会⽣成对应的servlet,然后容器就像管理servlet⼀样管理它们。
(5)多任务(miscellaneous task):web容器管理资源池,做内存优化,运⾏gc,提供安全配置,提供多应⽤的⽀持,热部署,以及其他⼀些我们没法看到的⼯作。
6.ServletContext对象和ServletConfig对象的区别?
(1)每个servlet都会有⾃⼰独有的servletConfig对象⽽servletContext对象是整个web应⽤共享的。
(2)servletConfig提供servlet的初始化参数(init-param),仅该servlet可以访问。⽽servletContext提供的初始化参数整个web应⽤的所有servlet都可以访问。(3)servletContext对象提供了setAttribute⽅法设置共享参数,⽽servletConfig并没有对应的set⽅法。
7.RequestDispatcher是什么?
RequestDispatcher接⼝⽤来将请求转发给同⼀个应⽤下的另⼀资源⽐如servlet,jsp,html等(forward) 。
当然我们也可以将另⼀资源处理的结果包含进来⼀起返回给客户端(include).这个接⼝⽤于同⼀上下⽂环境下的servlet间的通讯。
8.PrintWriter和ServletOutPutStream类有什么区别?
PrintWriter是字符流,ServletOutputStream是字节流。可以通过 PrintWriter向浏览器输出字符数组或者是字符串。也可以通过ServletOutPutStream向浏览器端输出字节数组。
PrintWriter对象在servlet中可以通过Writer()⽅法获取
ServletOutputStream对象通过OutputStream⽅法获取。
9.在⼀个servlet能否同时获取PrintWriter和ServletOutputStream对象。
不可以,如果同时获取,将会抛出java.lang.IllegalStateException异常。
10.在servlet中能否产⽣类似死锁情况?
可以的,你在doPost⽅法中调⽤doGet⽅法,在doGet⽅法中调⽤doPost⽅法,将产⽣死锁(最终会抛出stackoverflow异常)。
11.servlet包装类有什么⽤?
servletAPI提供了两个包装类:HttpServletRequestWrapper类和HttpServletResponseWrapper类,这些包装类帮助开发者给出request和response的⼀般实现。我们可以继承它们并选择我们需要复写的⽅法进⾏复写(包装设计模式),⽽不⽤复写所有的⽅法。
12.SingleThreadModel接⼝是什么?
这个接⼝提供了线程安全机制,它保证了同⼀时刻不可能有两个线程并发执⾏servlet的service⽅法。但是这个⽅法并没有解决所有的线程安全问题,⽐如同⼀时刻两个线程仍然可以访问到session和静态常量中的内容。⽽且这个⽅法也让servlet丧失多线程的优势,所以在servlet2.4中这个⽅法被声明为过时。
13.是否有必要重写service⽅法?
⼀般情况下是没有必要的,因为service⽅法会根据请求的类型(get、post等)将请求分发给doxxx⽅法去执⾏。即使我们需要在处理请求之前需要做⼀些额外的事,我们也可以通过过滤器或完成。
14.Servlet是否线程安全?如何创建线程安全的servlet?
HttpServlet的init和destroy⽅法在servlet声明周期中仅调⽤⼀次,所以不⽤担⼼它们的线程安全。但是s
ervice⽅法以及doget,dopost等⽅法是存在线程安全问题的,因为servlet容器为每⼀个客户端请求都创建⼀个线程,这些线程在同⼀时刻可能访问同⼀个servlet的service⽅法,所以我们在使⽤这些⽅法时务必⼩⼼。
如何创建线程安全的servlet?(SingleThreadModel⽅法不算)
1 .尽量使⽤局部变量,减少全局变量的使⽤。
2.对于共享变量,加上关键字synchronized。
注:servlet中常见线程安全与不安全的对象:
线程安全:ServletRequest,ServletResponse
线程不安全:ServletContext,HttpSession。
java中常用的设计模式有哪些对于 ServletContext,我们应尽量减少该对象中属性的修改。
⽽HttpSession对象在⽤户会话期间存在,只能在处理属于同⼀个Session的请求的线程中被访问,因此Session对象的属性访问理论上是线程安全的。但是当⽤户打开多个同属于⼀个进程的浏览器窗⼝,在
这些窗⼝的访问属于同⼀个Session,会出现多次请求,需要多个⼯作线程来处理请求,可能造成同时多线程读写属性
15.sevlet中的属性域。
servlet提供了⼀些域对象⽅便内部servlet之间的通信,我们可以通过set/get⽅法为web应⽤设置或取出属性值。servlet提供3个域(和jsp区分开来):
2.session scope
3.application scope
分别由ServletRequest,HttpSession,ServletContext对象提供对应的set/get/remove⽅法去操作者三个域。
注:这跟l中为servletConfig(针对单个servlet)和servletContext(针对web应⽤)定义的初始化参数不⼀样。
16如何调⽤另⼀个web应⽤的servlet/jsp等资源?
不能使⽤RequestDispatcher类,因为这个类只能访问本应⽤本⾝的资源。可以使⽤Response的sendRediect(url)⽅法,这个访问会返回状态码302给浏览器然后请求⼀个新的url。如果我们需要携带⼀些数据,可以在response对象中加上cookie。
17.请求转发(forward)和请求重定向(sendRediect)的区别?
转发是服务器⾏为,重定向是客户端⾏为。两个动作的⼯作流程如下:
转发过程:客户浏览器发送http请求----》web服务器接受此请求--》调⽤内部的⼀个⽅法在容器内部完成请求处理和转发动作----》将⽬标资源发送给客户;在这⾥,转发的路径必须是同⼀个web容器下的url,其不能转向到其他的web路径上去,中间传递的是⾃⼰的容器内的request。在客户浏览器路径栏显⽰的仍然是其第⼀次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发⾏为是浏览器只做了⼀次访问请求。
重定向过程:客户浏览器发送http请求----》web服务器接受后发送302状态码响应及对应新的location给客户浏览器--》客户浏览器发现是302响应,则⾃动再发送⼀个新的http请求,请求url是新的location地址----》服务器根据此请求寻资源并发送给客户。在这⾥location可以重定向到任意URL,既然是浏览器重新发出了请求,则就没有什么request传递的概念了。在客户浏览器路径栏显⽰的是其重定向的路径,客户可以观察到地址的变化的。重定向⾏为是浏览器做了⾄少两次的访问请求的。
18.HttpServlet为什么声明为抽象类?
httpServlet类虽然是抽象类但却没有抽象⽅法,之所以这样设计,是因为doget,dopost等⽅法并没有业务逻辑,开发者⾄少应该重写⼀个service中的⽅法,这就是我们不能实例化HttpServlet的原因。
19.servlet的⽣命周期?
servlet容器负责管理servlet的⽣命周期,servlet⽣命周期如下:
(1) 加载和实例化
Servlet 容器装载和实例化⼀个 Servlet。创建出该 Servlet 类的⼀个实例。
(2) 初始化
在 Servlet 实例化完成之后,容器负责调⽤该 Servlet 实例的 init() ⽅法,在处理⽤户请求之前,来做⼀些额外的初始化⼯作。
(3) 处理请求
当 Servlet 容器接收到⼀个 Servlet 请求时,便运⾏与之对应的 Servlet 实例的 service() ⽅法,service()
⽅法再派遣运⾏与请求相对应的
doXX(doGet,doPost) ⽅法来处理⽤户请求。
(4) 销毁
当 Servlet 容器决定将⼀个 Servlet 从服务器中移除时 ( 如 Servlet ⽂件被更新 ),便调⽤该 Servlet 实例的 destroy() ⽅法,在销毁该 Servlet 实例之前,
来做⼀些其他的⼯作。
其中,(1)(2)(4) 在 Servlet 的整个⽣命周期中只会被执⾏⼀次。
1.servlet⼯作过程时序图:
2.servlet⽣命周期流程图:
20.servlet⽣命周期⽅法有哪些?
共有3个⽅法:
public void init(ServletConfig config):
这个⽅法是由servlet容器调⽤⽤来初始化servlet的,这个⽅法在servlet⽣命周期中仅被调⽤⼀次。
public void service(ServletRequest request, ServletResponse response)
servlet容器为每个客户端创建线程,然后都会执⾏service⽅法,但执⾏此⽅法前init⽅法必须已经被执⾏了。
public void destroy()
servlet从servlet容器中被移除时会调⽤此⽅法,仅被调⽤⼀次。
21.我们为什么应该重写⽆参的init⽅法(⽽不是有参的)?
当我们希望servlet在处理请求之前加载⼀些资源时,我们应该重写init⽅法。但是GenericServlet类中定义了两个init⽅法:
public void init(ServletConfig config)
public void init()
官⽅推荐我们重写⽆参的init⽅法,因为通过查看源码我们可以知道,其实有参的init⽅法会调⽤⽆参的init⽅法,如果我们重写有参的init⽅法,那么必须先执⾏super.init(config),否则可能会产⽣空指针。
22.什么是URL重写?
我们知道HttpSession是基于Cookie⼯作的,如果浏览器禁⽤Cookie将导致session失效。ServletAPI提供了⼀种URL重写的机制⽤于处理这种情形。
我们只需调⽤HttpServletResponse对象的encodeURL⽅法即可,⽽且这种⽅法仅当浏览器禁⽤cookie才会编码,否则返回原url。当我们希望携带session信息重定向到另⼀资源上时,我们可以使⽤encodeRediectURL⽅法。
23.servlet API对cookie的⽀持?
cookie在c/s通信⽅⾯应⽤很⼴泛,并⾮java独有。cookie是服务器端发给浏览器并在浏览器上保存的⼀段⽂本数据。
ServletAPI中的javax.servlet.http.Cookie类为cookie提供了⽀持。HttpServletRequest  对象的getCookies⽅法从request中获取cookie数组。HttpServletResponse对象的addCookie⽅法将⼀个cookie返回给浏览器。
24.如何当session失效时唤醒⼀些对象?
可以让这个对象实现javax.servlet.http.HttpSessionBindingListener接⼝,这是⼀个,有两个⽅法:valueBound和valueUnbound⽅法,具体使⽤请查看⽂档说明。
25.servlet过滤器的作⽤?
1.打印⼀些请求参数到⽇志中
2.授权请求访问资源
3.在将请求提交给servlet之前,对request请求头或请求体进⾏⼀些格式化的处理
4.将响应数据进⾏压缩再返回给浏览器。
5.解决乱码问题。
26.servlet的作⽤?
监听客户端的请求,服务器端的操作等。通过,可以⾃动激发⼀些操作,⽐如监听在线的⽤户数量( 当增加⼀个HttpSession时,就⾃动触发sessionCreated(HttpSessionEvent se)⽅法,在这个⽅法中
就可以统计在线⼈数了 ),另外还可以⽤来初始化⼀些资源,⽐如数据库连接池等(l中配置的context-param只能是字符串不能使对象,这时就得使⽤ServletContextListener了,注意,有读者可能说ServletConext的setAttribute不是可以设置对象吗?但是这是在servlet创建之后才能调⽤的⽅法,如果希望web应⽤⼀启动就产⽣初始参数必须使⽤)。
l中组件的加载顺序
context-param -> listener -> filter -> servlet
⽽同个类型之间的实际程序调⽤的时候的顺序是根据对应的 mapping 的顺序进⾏调⽤的
28.如何处理servlet抛出的异常?
记住,不可以将异常信息⽇志显⽰给浏览器。当出现异常时,可以返回主页,或者显⽰出错的原因。可以在l中配置错误页⾯:
<error-page>
<error-code>404</error-code>
<location>/AppExceptionHandler</location>
</error-page>
<error-page>
<exception-type>javax.servlet.ServletException</exception-type>
<location>/AppExceptionHandler</location>
</error-page>
29.如何确保servlet在应⽤启动之后被加载到内存?
通常情况下都是客户端请求⼀个servlet,这个servlet才会被加载到内存,但对于某些很⼤加载很耗时的servlet我们希望应⽤启动时就加载它们,这时我们可以在l⽂件中配置或者使⽤webServlet注解(servlet3.0)告诉容器系统⼀启动就加载:
<servlet>
<servlet-name>foo</servlet-name>
<servlet-class>com.foo.servlets.Foo</servlet-class>
<load-on-startup>5</load-on-startup>
</servlet>
load-on-startup节点中必须配置⼀个整数,整数代表应⽤⼀启动就会被加载,负数表⽰当客户端请求之后才加载,正数的值越⼩,说明越先被加载。
30.如何获取资源在⽂件系统中的绝对路径?
getServletContext().getRealPath(path),path是以‘/’开头的相对路径。
31.如何获取服务器的信息?
getServletContext().getServerInfo()⽅法,⼀般返回服务器明,版本号,⽐如:Apache Tomcat/6.0.37
32.如何在servlet中使⽤数据库?
如果web应⽤使⽤数据库的地⽅很多,最好在ServletContextListener中初始化它,并设置为context 属性以便其他servlet使⽤。
⽐如:
33.如何在servlet中获取客户端的ip地址?
34.servlet3.0的重要特性?
异步处理⽀持:有了该特性,Servlet 线程不再需要⼀直阻塞,直到业务处理完毕才能再输出响应,最后才结束该 Servlet 线程。在接收到请求之后,Servlet 线程可以将耗时的操作委派给另⼀个线程来完成,⾃⼰在不⽣成响应的情况下返回⾄容器。针对业务处理较耗时的情况,这将⼤⼤减少服务器资源的占⽤,并且提⾼并发处理速度。
新增的注解⽀持:该版本新增了若⼲注解,⽤于简化 Servlet、过滤器(Filter)和(Listener)的声明,这使得 l 部署描述⽂件从该版本开始不再是必选的了。
可插性⽀持:熟悉 Struts2 的开发者⼀定会对其通过插件的⽅式与包括 Spring 在内的各种常⽤框架的整合特性记忆犹新。将相应的插件封装成 JAR 包并放在类路径
下,Struts2 运⾏时便能⾃动加载这些插件。现在 Servlet 3.0 提供了类似的特性,开发者可以通过插件的⽅式很⽅便的扩充已有 Web 应⽤的功能,⽽不需要修改原有的应⽤。
HttpServletRequest 对⽂件上传的⽀持:此前,对于处理上传⽂件的操作⼀直是让开发者头疼的问题,因为 Servlet 本⾝没有对此提供直接的⽀持,需要使⽤第三⽅框架来实现(file-upload),⽽且使⽤起来也不够简单。如今这都成为了历史,Servlet 3.0 已经提供了这个功能。
----------------------------------------END------------------------------------------------------

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。