javaweb|对session何时生成的无用讨论
第 16 章 Never End...
学习到此为止告一段落
对教程内容和jsp有什么建议的话,可以跟叮咚姐姐联系,随着技术的进步,我们也会同步更新教程的内容。
提出问题:“session是在什么时候生成的?”
现象:
-
如果写一个servlet,在里边调用getSession(false),得到的是null。(这里的参数表示当session不存在时是否新建,返回null表示还没为当前用户生成对应的session)。
-
如果写一个jsp,在里边打印session,可以看到打印的信息,说明session已经创建了。
对现象的感性认识:jsp会自动创建HttpSession对象,而servlet则不会,只有在servlet中显式调用getSession()方法时才创建session。
对现象的理性说明
-
只有执行了getSession()才会生成session。(此处无参数,默认与getSession(true)功能相同)。
-
jsp转换成servlet后,生成的servlet代码中包含getSession()。
例子中见tomcat目录下,work\Catalina\localhost\99-01\org\apache\jsp\test1.jsp,其中包含了pageContext.getSession()。
-
我们可以在jsp中使用<%@ page session="false"%>禁止生成session的代码,这样即使访问此jsp页面,也不会创建session。
例子中见tomcat目录下,work\Catalina\localhost\99-01\org\apache\jsp\test2.jsp,这里已经看不到getSession()的代码了。
因为<%@ page session="false"%>不会生成默认的session,在这个jsp页面里就无法直接使用session变量了。下面的代码会导致jsp无法编译的错误。
<%@ page session="false"%> <% out.println(session); %> <hr />
访问test3.jsp会出现500服务器内部错误
在使用过<%@ page session="false"%>的页面里只能使用getSession()手工获得session才能使用了。
演示用代码放在99-01目录下,TestServlet映射到/请求路径,test1.jsp会自动创建session,test2.jsp禁用了session,test3.jsp直接使用session会出现错误。
这个问题是典型的无用研究之一,实际工作中会有人使用getSession(false)吗?
有同学提到了一个问题:让用户自己编写jsp上传到服务器,服务器接收后保存入数据库,在用户访问的时候从数据库中取出对应的jsp,运行后返回响应。
这个效果从理论上讲是可以实现的,我们已经对jsp的运行机制非常熟悉了,第 6.2 节 “jsp与servlet的关系”,我们只需要将jsp转换为对应servlet的java文件,再编译为class,最后使用ClassLoader加载生成的class执行即可。
好消息是tomcat为我们提供了批量转换jsp的工具,使用以下的ant脚本就可以将指定目录下的jsp转换成对应servlet的java代码。(ant的使用方法已经超出了本文的讨论范围,如果有兴趣请自行参阅官方网站。http://ant.apache.org/)
<project name="jspc" default="jspc" basedir="."> <property name="CATALINA_HOME" location="../../"/> <path id="build.lib"> <pathelement location="WEB-INF/classes"/> <fileset dir="WEB-INF/lib"> <include name="*.jar"/> </fileset> </path> <target name="jspc"> <taskdef classname="org.apache.jasper.JspC" name="jasper2"> <classpath> <pathelement location="${java.home}/../lib/tools.jar"/> <fileset dir="${CATALINA_HOME}/server/lib"> <include name="*.jar"/> </fileset> <fileset dir="${CATALINA_HOME}/common/lib"> <include name="*.jar"/> </fileset> <path refid="build.lib"/> </classpath> </taskdef> <jasper2 verbose="1" package="org.apache.jsp" uriroot="." webXmlFragment="WEB-INF/generated_web.xml" outputDir="./WEB-INF/src/" /> </target> </project>
为了顺利完成转换工作,我们需要把一系列jar文件放入classpath中:
-
WEB-INF/lib/*.jar(星号代表所有)
-
${java.home}/../lib/tools.jar(${java.home}表示jdk安装目录)
-
${CATALINA_HOME}/server/lib/*.jar(${CATALINA_HOME}表示tomcat的安装目录)
-
${CATALINA_HOME}/common/lib/*.jar
简要介绍一下jasper2的配置参数。
-
verbose="1"表示打印进度信息,如果verbose="0"则不打印信息。
-
package="org.apache.jsp"表示生成servlet中的包名(package)。
-
uriroot="."表示会把当前目录下的所有jsp都转换成servlet。
-
webXmlFragment="WEB-INF/generated_web.xml"会在WEB-INF目录下生成servlet的默认配置。
比如我们转换的test.jsp将生成如下部分。
<servlet> <servlet-name>org.apache.jsp.test_jsp</servlet-name> <servlet-class>org.apache.jsp.test_jsp</servlet-class> </servlet> <servlet-mapping> <servlet-name>org.apache.jsp.test_jsp</servlet-name> <url-pattern>/test.jsp</url-pattern> </servlet-mapping>
将这部分复制到你项目中的web.xml中就可以通过/test.jsp请求来访问生成的servlet了。
-
outputDir="./WEB-INF/src/"表示生成的servlet文件放在WEB-INF/src目录下,tomcat会自动生成package对应的目录,比如test.jsp生成的最终路径是WEB-INF/src/org/apache/jsp/test_jsp.java。
接着把生成的servlet编译成class,这次除了common/lib/servlet-api.jar以外我们还需要common/lib/jasper-runtime.jar加入classpath中,编译脚本参考WEB-INF/src/compile.bat。
现在可以删除test.jsp了,web.xml中已经将/test.jsp请求转发至对应的servlet处理,我们甚至不需要修改任何链接。
jspc的主要功能在于预先编译jsp发现其中的语法错误,有些公司也使用这种方式进行加密(毫无意义的做法,把jsp唯一的灵活性都浪费了)。
演示程序在99-02目录下,需要安装ant之后才可能执行run.bat将jsp转换成servlet。
在了解如何手工转换jsp之后,我们可以来讨论在数据库中保存jsp的问题了。
将jsp从数据库中提取出来,将这些数据保存成本地文件,使用jspc转换成servlet再编译为class,最后使用自定义的ClassLoader读取到jvm中执行。
难点在于生成文件要保证互不影响和自定义ClassLoader如何加载管理这些生成的class。
这个问题是典型的无用研究之一,任何想获得灵活模板功能的同学都应该亦然决然的抛弃jsp,jsp这种先解释编译再执行的机制不仅没有帮助我们提升效率,反而大大增加技术难度并降低响应效率。需要自定义模板的同志务必考虑velocity, freemarker此类模板引擎或者groovy一类脚本语言,这时使用jsp无异于给自己带上一副沉重的枷锁。
会话都保存在服务器端。
每个用户打开浏览器就服务器就会给它生成一个sessionId,浏览器或者把这个sessionId放到cookie里,或者每次请求都带在url后边(自动的),然后服务器就拿到这个sessionid,在内存里翻啊翻啊,翻出对应的session来,就这么对应上的。
ServletContext和session就没多大关系了,虽然ServletContext也是在服务器端,每个web应用发布的时候,就要创建这么一块空间放置ServletContext,这个web应用中的所有的servlet, jsp, filter, listener都可以访问这块空间。你可以把他看作是一个全局变量,所有共享数据都放到里边。
No comments:
Post a Comment