jsp工作原理

    技术2026-05-24  8

    一、 JSP 工作原理 JSP 在一个 JSP 文件第一次被请求时, JSP 引擎把该 JSP 文件转换成为一个 servlet 。而这个引擎本身也是一个 servlet ,在 JSWDK WEBLOGIC 中,它就是 JspServlet JSP 引擎先把该 JSP 文件转换成一个 Java 源文件,在转换时如果发现 jsp 文件有任何语法错误,转换过程将中断,并向服务端和客户端输出出错信息;如果转换成功, JSP 引擎用 javac 把该 Java 源文件编译成相应的 class 文件。然后创建一个该 SERVLET 的实例,该 SERVLET jspInit() 方法被执行, jspInit() 方法在 servlet 的生命周期中只被执行一次。然后 jspService() 方法被调用来处理客户端的请求。对每一个请求, JSP 引擎创建一个新的线程来处理该请求。如果有多个客户端同时请求该 JSP 文件,则 JSP 引擎会创建多个线程。每个客户端请求对应一个线程。以多线程方式执行可大大降低对系统的资源需求 , 提高系统的并发量及响应时间 . 但应该注意多线程的编程限制,由于该 servlet 始终驻于内存,所以响应是非常快的。 如果 .jsp 文件被修改了,服务器将根据设置决定是否对该文件重新编译,如果需要重新编译,则将编译结果取代内存中的 servlet ,并继续上述处理过程。 虽然 JSP 效率很高,但在第一次调用时由于需要转换和编译而有一些轻微的延迟。 此外,如果在任何时候如果由于系统资源不足的原因, JSP 引擎将以某种不确定的方式将 servlet 从内存中移去。当这种情况发生时 jspDestroy() 方法首先被调用 , 然后 servlet 实例便被标记加入 " 垃圾收集 " 处理。 jspInit() jspDestory() 格式如下:可在 jspInit() 中进行一些初始化工作 , 如建立与数据库的连接,或建立网络连接,从配置文件中取一些参数等,在 jspDestory() 中释放相应的资源。   <%! public void jspInit() {       System.out.println("jspinit");   }   %>   <%! public void jspDestory() {       System.out.println("jspDestory");   } %> 二、服务端的输出缓冲区 缺省情况下 : 服务端要输出到客户端的内容 , 不直接写到客户端 , 而是先写到一个输出缓冲区中 . 只有在下面三中情况下,才会把该缓冲区的内容输出到客户端上: JSP 网页已完成信息的输出   输出缓冲区已满   JSP 中调用了 out.flush() response.flushbuffer()   输出缓冲区的大小可以用 : response.setBufferSize() 设置 , 如下:   设置输出缓冲区的大小为 1KB 。或 response.setBufferSize(1);   设置输出缓冲区的大小为 0 ,即不缓冲。或 response.setBufferSize(0);   response.getBufferSize() out.getBufferSize() 可取的输出缓冲区的大小 , 单位为字节 . response.isCommitted() 可检查看服务端是否已将数据输出到客户端 . 如果返回值是 TRUE 则已将数据输出到客户端 , FALSE 则还没有 .   三、服务端输出重定向 有以下 3 种方法可以做到输出重定向 :   RESPONSE.SETREDERECT("URL") 该方法通过修改 HTTP 协议的 HEADER 部分 , 对浏览器下达重定向指令的 , 使浏览器显示重定向网页的内容 . response.sendRedirect(" http://localhost:7001/index.html");   下面的方法也能改变 HTTP HEADER 属性,它的原理和 1 是一样的 .   <%   response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);   String newLocn="/index.html";   response.setHeader("Location",newLocn);   % >   采用 <JSP:FORWORD> 该方法是利用服务器端先将数据输出到缓冲区的机制 , 在把缓冲区的内容发送到客户端之前 , 原来的不发送 , 改为发送该页面的内容 , 如果在 <JSP:FORWORD> 之前有很多输出 , 前面的输出已使缓冲区满 , 将自动输出到客户端 , 那么该语句将不起作用 , 这一点应该特别注意 . 如下面的例子中 (1) 会输出 index.html 的内容, 2 不会输出 index.html 的内容,而是输出 out.println("@@@@@@@@@@@@@@@@@"); 中的内容 , 并且在服务端会抛出 :java.lang.IllegalStateException: Response already committed 异常,但客户端没有任何错误输出。   (1) <%@page buffer="1kb"%>   <% long i=0;   for(i=0;i<10;i++) {       out.println("@@@@@@@@@@@@@@@@@"); } %>   <jsp:forward page="./index.html" />     (2) <%@page buffer="1kb"%>   <% long i=0;   for(i=0;i<600;i++) {                         out.println("@@@@@@@@@@@@@@@@@"); } %>   说明: 1. 方法 (1),(2) 可以使用变量表示重定向的地址 ; 方法 (3) 不能使用变量表示重定向的地址。 String add="./index.html";   <jsp:forward page= add />   无法重定向到 index.html 中去   String add=http://localhost:7001/index.html   response.sendRedirect(add);   可以重定向到 http://localhost:7001/index.html 中去。   2. 采用方法 (1),(2)request 中的变量 ( 通过 request.setAttribute() 保存到 request 中的值 ) 不能在新的页面中采用 , 采用方法 (3) . 综上 , 我们应该采用 (1),(2) 重定向比较好 .   四、 JSP 中正确应用类 :   应该把类当成 JAVA BEAN 来用,不要在 <% %> 中直接使用 . 如下的代码 (1) 经过 JSP 引擎转化后会变为代码 (2): 从中可看出如果把一个类在 JSP 当成 JAVA BEAN 使用 ,JSP 会根据它的作用范围把它保存到相应的内部对象中 . 如作用范围为 request, 则把它保存到 request 对象中 . 并且只在第一次调用 ( 对象的值为 null) 它时进行实例化 . 而如果在 <% %> 中直接创建该类的一个对象 , 则每次调用 JSP , 都要重新创建该对象 , 会影响性能 .   代码 (1) <jsp:useBean id="test" scope="request" class="demo.com.testdemo"> </jsp:useBean>   <% test.print("this is use java bean");   testdemo td= new testdemo(); td.print("this is use new"); %>   代码 (2) demo.com.testdemo test = (demo.com.testdemo)request.getAttribute("test");   if (test == null)   {         try         {                   test = (demo.com.testdemo) java.beans.Beans.instantiate(getClass().getClassLoader(),"demo.com.testdemo");         }         catch (Exception _beanException)         {               throw new weblogic.utils.NestedRuntimeException("cannot instantiate 'demo.com.testdemo'",_beanException);         }         request.setAttribute("test", test);         out.print("/r/n"); }   out.print("/r/n/r/n/r/n"); test.print("this is use java bean");     testdemo td= new testdemo(); td.print("this is use new");   五、 JSP 的调试   JSP 的调试比较麻烦 , 特别是当 bean 是在一个 session 中存在时,更加困难。得从好几个页面开始往里面走才行。通常是用 out.println() System.out.print() 来打一大堆的信息来查问题。如果是用 jbuilder 做开发 , 它能直接调试 JSP. 不过更重要的是知道错误产生的原因及解决方法。下面对一些 JSP 编程常见错误进行分析。   (1).java.lang.NullPointerException 异常 一般是对一个为 NULL 值的变量进行操作引起的 . 如下面的操作就会抛出 java.lang.NullPointerException String a = null;   a.substring(0,1);     为避免这种异常最好在对变量操作之前检查看它是否为 NULL . : <% String ss=Session.getAttribute("NAME")   if isnull(ss)   {   }   else   {   }   %>   (2).JSP 是用 JAVA 写的,所以它是大小写敏感的,用过其他编程语言的人最容易犯这个错误。另外在浏览器的地址栏中输入的访问 JSP 的地址也是区分大小写的 . http://localhost:7001/demo/t.jsp http://localhost:7001/Demo/t.jsp 是不一样的 (3). jsp 中判断字符串要使用 compareTo 方法,不要用 == ,因为在 java String 变量不是一个简单的变量而是一个类实例,不同的方法会得到 不同的结果,如下所示:         String str1="ABCD";      String str2="ABCD"; ( String str2="AB"+"CD";      if (str1==str2)      out.print("yes");      else      out.print("no");     结果是 "yes"          String str1,str2,str3;      str1="ABCD";      str2="AB";      str3=str2+"CD";      if (str1==str3)      out.print("yes");      else      out.print("no");     结果是 "no"   String str1=new String("ABCD");      String str2=new String("ABCD");      if (str1==str2)      out.print("yes");      else      out.print("no");     结果是 "no"   String str1=new String("ABCD");      String str2=new String("ABCD");      if (str1.compareTo(str2)==0)      out.print("yes");      else      out.print("no");     结果是 "yes"   (4) 防止 JSP SERVLET 中的输出被浏览器保存在缓冲区中 : 浏览器在默认情况下会把浏览过的网页保存在缓冲区中 , 在调试时 , 一般不希望这样 . 把下面的脚本加入程序中 , 就可防止 JSP SERVLET 中的输出被浏览器保存在缓冲区中   <%   response.setHeader("Cache-Control","no-store"); //HTTP 1.1   response.setHeader("Pragma","no-cache"); //HTTP 1.0   response.setDateHeader ("Expires", 0); //prevents caching at the proxy server   %>   IE 中也可通过设置实现:把 / 工具 /INTERNET 选项 / 常规 / 设置 / 的检察所存页面的较新版本 , 设为每次访问该页时都检查 . 六、 COOKIE HTTP COOKIE 实质是服务端与在客户端之间传送的普通 HTTP , 可保存也可不保存在客户的硬盘上 . 如果保存 , 每一个文件大小不超过 4K 的文本文件 . 多个 COOKIE 可保存到同一个文件中 . 如果从编程角度来看 , JSP COOKIE 就是 JAVA 提供的一个类 . 常用的方法如下所表示,因为客户端可能不接受 COOKIE ,所以建议不用它,改用 SESSION 等其他方式。   public class cookie   {   public String getDomain() // 返回该 COOKIE 的有效域   public int getMaxAge() // 返回该 COOKIE 的有效期 , 单位为秒   public String getName() // 返回该 COOKIE 的名称   public String getPath() // 返回该 COOKIE 的有效路径   public boolean getSecure() // 返回该 COOKIE 的安全设置   public String getvalue() // 返回该 COOKIE 的值   public void setDomain(java.lang.String pattern) // 设置该 COOKIE 的有效域   public void setMaxAge(int expiry) // 设置该 COOKIE 的有效期 , 单位为秒   public void setPath(java.lang.String uri) // 设置该 COOKIE 的有效路径   public void setSecure(boolean flag) // 设置该 COOKIE 的安全设置   public void setvalue(java.lang.String newvalue) // 设置该 COOKIE 的值   }   一个 COOKIE 包含以下五部分 :   NAME/value , 设置该 COOKIE 的名字及它保存的值   COOKIE 通常和服务器相关 , 如果将域设为 JAVA.SUN.COM, 那么该 COOKIE 就和这个域相关 , 只对该网址起作用 , 当浏览该网址时 , 浏览器将把该 COOKIE 的内容发送给服务端 ,COOKIE 是作为 HTTP HEADER 的一部分被发送的,如果没有设置域 , 那么 COOKIE 就只和创建该 COOKIE 的服务器相关 .   路径用于指定服务器上可以使用该 COOKIE 的文件所在的路径 , 它只对该网址下的该路径下的应用起作用 ."/" 表示服务器上所有目录都可以使用该 COOKIE.   COOKIE 都有一个有效期 , 有效期默认值为 -1, 这表示没有保存该 COOKIE, 当该浏览器退出时 , COOKIE 立即失效 .   安全选项 true/false, 如果设置为 true, 那么在服务端与在客户端之间传送该 COOKIE 的内容时 , 采用 HTTPS 协议 .   如何检查一个客户端是否支持 COOKIE 的方法 :   用下面的方法写一个 COOKIE 到客户端 , 并确认成功   try   {   Cookie c = new Cookie("mycookie","COOKIE TEST");   response.addCookie(c);   }   catch(Exception e)   {        System.out.println(e);   }   然后在一个新的 JSP 文件中 : 用下面的方法取客户端的 COOKIE cookies , 如果 cookies.length ==0, 说明该客户端的浏览器不支持 COOKIE   try   {   Cookie[] cookies = request.getCookies();   if(cookies.length ==0)   {        System.out.println("not support cookie");   }   }   catch(Exception e)   {        System.out.println(e);   }   七、 JSP SERVLET 的区别 :   SUN 首先发展出 SERVLET ,其功能比较强劲,体系设计也很先进,只是,它输出 HTML 语句还是采用了老的 CGI 方式,是一句一句输出,所以,编写和修改 HTML 非常不方便。 后来 SUN 推出了类似于 ASP JSP ,把 JAVA 代码嵌套到 HTML 语句中,这样,就大大简化和方便了网页的设计和修改。 ASP PHP JSP 都是嵌套型的 SCRIPT 语言。 一个分布式系统应分为三层:表示层 , 业务逻辑层 , 数据存取层 , J2EE 体系结构中 ,SERVLET 用来写业务逻辑层是很强大的,但是对于写表示层就很不方便。 JSP 则主要是为了方便写表示层而设计的。 ENTITY BEAN 实现数据存取层, SESSION BEAN 实现业务逻辑层。如果是简单的应用系统 , 可采用 JSP+BEANS 的结构进行设计 ,JSP 中应该仅仅存放与表示层有关的东西,也就是说,只放输出 HTML 网页的部份。而所有的数据计算,数据分析,数据库联结处理,统统是属于业务逻辑层,应该放在 JAVA BEANS 中。通过 JSP 调用 JAVA BEANS ,实现两层的整合。 实际上,微软的 DNA 技术,简单说,就是 ASP+COM/DCOM 技术。与 JSP+BEANS 完全类似,所有的表示层由 ASP 完成,所有的业务逻辑由 COM/DCOM 完成。 为什么要采用这些组件技术呢?因为单纯的 ASP/JSP 语言是非常低效率执行的,如果出现大量用户点击,纯 SCRIPT 语言很快就到达了他的功能上限,而组件技术就能大幅度提高功能上限,加快执行速度。另外一方面,纯 SCRIPT 语言将表示层和业务逻辑层混在一起,造成修改不方便,并且代码不能重复利用,采用组件技术就只改组件就可以了。 对于复杂的应用,应该采用 ENTITY BEAN 实现数据存取层, SESSION BEAN 实现业务逻辑层,用 JSP 来调用 SESSION BEAN ,由 SESSION BEAN 调用 ENTITY BEAN 。即采用 JSP+EJB 来构建一个复杂的分布式系统。它比 JSP+BEAN 具有更高的吞吐量,可靠性,安全性。综上所述,对简单应用,可采用 JSP+BAEN ,对复杂的应用系统,应采用 JSP+EJB SERVLET 变的无足轻重。用 JSP 完全可替代它。当然不要忘记 structs 阿:)

    最新回复(0)