当前位置: 首页 > 科技观察

Java网络编程基本功:Servlet与Servlet容器

时间:2023-03-15 22:30:27 科技观察

Servlet与Servlet容器的关系Servlet比较这两者的区别,首先要搞清楚Servlet的含义,Servlet(/?s?rvlit/)翻译成中文是小applicationorsmallservice程序类似于Server(/?s??rv?r/),翻译过来就是服务器的意思。可以看出,两者承担的功能相似,只是Servlet更轻。Web开发的本质就一句话:客户端和服务端交换数据。于是使用JavaSocket套接字进行编程处理客户端的tcp请求,通过codec处理读取请求体,得到请求行,然后找到请求行对应的处理逻辑,进入服务端的处理,并处理完成返回相应结果给当前Socket连接,响应完成后关闭Socket。在上面的过程中,建立连接、传输数据、关闭连接等过程都是由tomcat容器帮你完成的,而拿到请求行后找到对应的url路由,这部分谁来负责呢?是Servlet!简单的说,Servlet就是一段处理web请求的逻辑。具体来说,Servlet具有以下特点:Servlet是用Java编写的服务器端程序,与协议和平台无关。Servlets在支持Java的Web服务器上运行。JavaServlet可以动态扩展Server的能力,以请求-响应的方式提供Web服务。最早支持Servlet技术的是JavaSoft的JavaWebServer。从那时起,其他一些基于Java的Web服务器也开始支持标准的ServletAPI。Servlet的主要功能是交互式地浏览和修改数据,生成动态的Web内容。在以上六点中,最需要记住的是Servlet可以动态扩展Server的能力,以请求-响应的方式提供Web服务。JDK中的Servlet是一个接口:publicinterfaceServlet{publicvoidinit(ServletConfigconfig)throwsServletException;公共ServletConfiggetServletConfig();publicvoidservice(ServletRequestreq,ServletResponseres)throwsServletException(ServletException,IOExcept)voiddestroy();}可以看出Servlet是一个接口,规定了请求从容器到达web服务器的规范。具体细节会在后面的Servlet生命周期中详细梳理。下面简单总结一下这三个重要步骤:init():发起请求时做什么。service():收到请求后做什么。destroy():当请求被销毁时做什么。所有实现Servlet的实现方都是在这个规范的基础上开发的。那么Servlet中的数据从何而来呢?答案是Servlet容器。容器是实际与客户端打交道的容器。一个容器中可以有多个Servlet。Tomcat,一个普通的Servlet容器,监听客户端的请求端口,根据请求行信息,决定由哪个Servlet处理请求,找到处理后的Servlet,调用Servlet的service()方法,处理后,对应处理结果会被打包成一个ServletResponse对象返回给客户端。Servlet容器现在我们来谈谈Servlet容器。前面说过,Servlet只是一个接口或者说规范,所以必须要有具体的实现,而Servlet的具体实现或者说包装器就是Wrapper。直接管理Wrapper的容器是Context,一个Context对应一个Web项目,也就是说,Context容器如何运行,将直接影响到Servlet的工作。从图中可以看出,Tomcat的底层是Context,Context负责管理Servlet封装类Wrapper。下面创建一个实例对象,调用start方法即可轻松启动Tomcat。我们还可以通过这个对象来添加和修改Tomcat的配置参数,比如动态添加Context、Servlet等。我们选择Tomcat7自带的examplesWeb项目,看看它是如何添加到Context容器中的。//给Tomcat添加一个Web项目:Tomcattomcat=getTomcatInstance();FileappDir=newFile(getBuildDirectory(),"webapps/examples");tomcat.addWebapp(null,"/examples",appDir.getAbsolutePath());tomcat.启动();ByteChunkres=getUrl("http://localhost:"+getPort()+"/examples/servlets/servlet/HelloWorldExample");assertTrue(res.toString().indexOf("

HelloWorld!

")>0);上面的代码是创建一个Tomcat实例并添加一个新的Web应用程序,然后启动Tomcat并调用其中一个HelloWorldExampleServlets来查看是否正确返回了预期的数据。//Tomcat的addWebapp方法代码如下:publicContextaddWebapp(Hosthost,Stringurl,Stringpath){silence(url);上下文ctx=newStandardContext();ctx.setPath(网址);ctx.setDocBase(路径);if(defaultRealm==null){initSimpleAuth();}ctx.setRealm(defaultRealm);ctx.addLifecycleListener(newDefaultWebXmlListener());ContextConfigctxCfg=newContextConfig();ctx.addLifecycleListener(ctxCfg);ctxCfg.setDefaultWebXml("org/apache/catalin/startup/NO_DEFAULT_XML");if(host==null){getHost().addChild(ctx);}else{host.addChild(ctx);}返回ctx;}添加web应用时会创建一个StandardContext容器,并为Context容器设置必要的参数(url代表应用在Tomcat中的访问路径;path代表应用的实际物理路径)。最重要的配置之一是ContextConfig,[ContextConfiglistener]继承自[LifecycleListenerlistenerinterface],在调用List2时添加到StandardContext容器中。当Context容器的初始化状态设置为init时,会调用添加到Context容器中的Listener。[ContextConfiglistener]会负责解析整个web应用的配置文件。最后将这个Context容器添加到父容器Host中。Servlet生命周期Servlet生命周期分为四个部分:实例化==>初始化==>执行处理==>销毁。new实例化时,第一次访问服务器时,会加载一个Servlet容器,只会加载一次。初始化init:Servlet容器创建后,会调用只执行一次的init()初始化方法来初始化Servlet对象。服务器运行时无论有多少客户端访问服务器,都不会再执行init()方法。在继承的GenericServlet的抽象类中可以看到初始化方法:publicvoidinit()throwsServletException{}而这个方法应该在我们的Servlet类中继承调用:publicvoidinit()throwsServletException{super.init();}创建Servlet对象的时间:Servlet容器启动时:读取web.xml配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,调用Servlet对象的init方法,ServletConfig对象为一个参数。Servlet容器启动后:客户第一次向Servlet发送请求,Servlet容器会判断内存中是否有指定的Servlet对象,如果没有则创建,然后根据创建HttpRequest和HttpResponse对象根据客户的请求,从而调用Servlet对象的服务方法。Servlet:Servlet容器在启动时自动创建一个Servlet,由web.xml文件中为Servlet设置的属性决定。从这里我们也可以看出,同类型的Servlet对象作为单例存在于Servlet容器中。执行处理执行处理——service()方法是Servlet的核心,负责响应客户的请求。每当客户端请求一个HttpServlet对象时,该对象的Service()方法就会被调用,一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象将作为参数传递给这个方法。Service()方法已存在于HttpServlet中。默认的服务函数是调用HTTP请求的方法对应的do函数。HttpServlet的抽象类提供了doGet()、doPost()...等方法。对应request请求的发送方法,匹配它:Stringmsg=lStrings.getString("http.method_get_not_supported");如果(protocol.endsWith("1.1")){resp.sendError(405,msg);}else{resp.sendError(400,msg);}}protectedvoiddoPost(HttpServletRequestreq,HttpServletResponseresp)抛出ServletException,IOException{Stringprotocol=req.getProtocol();Stringmsg=lStrings.getString("http.method_post_not_supported");如果(protocol.endsWith("1.1")){resp.sendError(405,msg);}else{分别。发送错误(400,味精);}}以上是操作性最强的部分。DestructionDestruction——destroy:当服务器关闭或重启时,Servlet会调用destroy方法进行销毁,将Servlet容器标记为垃圾文件,让GC进行回收。我们写的Servlet调用了GenericServlet抽象类的destroy方法:@Overridepublicvoiddestroy(){super.destroy();}Servlet工作原理1.首先简单说明一下Servlet接收和响应客户请求的过程:客户发送Request,Servlet调用service()方法响应请求,service()方法匹配请求方法。选择调用doGet、doPost等方法,然后进入相应的方法调用逻辑层的方法,实现对客户的响应。Servlet接口和GenericServlet中没有doGet()、doPost()等方法。这些方法都是在HttpServlet中定义的,但是它们都返回错误信息。因此,我们每次定义一个Servlet,都必须实现doGet或doPost等这些方法。2、每一个自定义的Servlet都必须实现Servlet接口。Servlet接口中定义了五个方法,其中最重要的三个方法涉及到Servlet的生命周期,分别是上面提到的init()和service。(),destroy()方法。GenericServlet是一个通用的、不特定于任何协议的Servlet,它实现了Servlet接口。HttpServlet继承自GenericServlet,所以HttpServlet也实现了Servlet接口。所以我们在定义Servlet的时候,只需要继承HttpServlet即可。3、Servlet接口和GenericServlet不特定于任何协议,而HttpServlet是特定于HTTP协议的类,所以在HttpServlet中实现了service()方法,将请求ServletRequest和ServletResponse强制为HttpRequest和HttpResponse。4、另外,Servlet是单例模式,线程不安全,所以尽量不要在service()方法中操作全局变量。但实际上可以用session和application代替全局变量,但是会增加服务器负载。Servlet处理请求的过程客户端向服务器发送请求。容器根据request和web.xml判断对应的Servlet是否存在,不存在则返回404。容器根据request和web.xml判断对应的Servlet是否已经实例化。如果对应的Servlet还没有被实例化,容器会加载对应的Servlet到Java虚拟机并实例化。调用实例对象的service()方法,并开启一个新线程进行相关处理。调用servce方法判断是调用doGet方法还是doPost方法。业务完成后,将响应相关页面发送给客户端。