模块化架构之tomcat的jsp加载处理
2015-11-09 15:06
579 查看
最近一直在思考模块化项目的架构问题,也学过一点点OSGI的东西,感觉有点高大上,自身目前还用不上,或者用的不够好。就想着能不能实现一个简单一点的模块化方式,于是便有了前一篇的阅读spring源码,解决了实体实例化上的模块化问题,下面我们来看看资源模块化的问题,主要是jsp。我们都知道,对于服务器(tomcat)来说,要求jsp都要在项目目录下,那如果我们要把jsp封装到相对应的模块jar包中呢,tomcat还能加载吗?
一开始,笔者只是简单的做了读取操作,对于jsp的请求都拦截了读取内容返回前台,结果就出现了很喜感的一幕,jsp根本就不能好好执行,这时笔者才想起来,jsp本质上是一个变种servlet,而不是一个单纯的html页面。于是我们又开始了阅读源码的过程。
通过阅读我们发现,tomcat处理jsp的关键是其默认配置了一个JSPServlet类,用于处理所有的jsp文件。通过分析Service方法和serviceJspFile方法我们发现了tomcat对于jsp的处理基本过程是:
首先,通过uri获取要处理的jsp路径。
其次,尝试从JspRuntimeContext缓存容器中获取JspWrapper如果没有,就开启线程同步去构建一个,构建时会通过context.getResource来判断是否有jsp文件(也就是该方法限定了jsp必须在项目目录下,也就是配置的docBase目录下)。
第三,如果有,就创建JSPWrapper并以uri为key加入缓存中。
第四,调用JspWrapper.service方法来完成jsp响应(该方法完成了jsp的编译存储和服务处理。每次都会判断是不是开发模式和最后一次修改时间,如果有更新就重新编译)
在阅读中我们发现,对于jsp资源获取的关键类实际上只有3个一个是JSPServlet,一个是JspWrapper一个是JspCompilationContext.这三个类。
JSPServlet说了,是用于处理*.jsp的请求,负责获取jsp的路径,并查找相关的JSPWrapper来响应。
JSPWrapper,包装了jsp文件,完成了jsp的编译,和响应
JspCompilationContext,Jsp的编辑容器,完成了jsp的资源查找和编辑器创建等操作,在jsp第一次加载的时候完成了大量的初始化工作。
下面我们就改造这三个类来实现我们自己的想法,这里我继承这三个类分别作了ModuleJspServlet,ModuleJSPWrapper和ModuleJSPCompilationContext类,来完成相关的扩展功能。
ModuleJspServlet中我们简单修改了,判断方法,也就是不在简单的使用ServletContext.getResource来判断是否存在jsp,而是通过JspCompilationContext来判断。
对于ModuleJSPWrapper我们也只是修改了其构造函数:
主要的修改在ModulJspCompilationContext中,获取资源的方法我们做了下简单修改。
我们针对自己的路径做了下处理。这样就完成了jsp的跨项目加载和模块化加载。
总结:这里通过跟踪分析,知识修改了jsp的加载方式,不在只是从项目目录下加载。其他未做修改。
在测试时发现,我们单纯的返回资源不设置请求响应码,容易造成浏览器莫名其妙的缓存问题。这点算是额外收获了。
一开始,笔者只是简单的做了读取操作,对于jsp的请求都拦截了读取内容返回前台,结果就出现了很喜感的一幕,jsp根本就不能好好执行,这时笔者才想起来,jsp本质上是一个变种servlet,而不是一个单纯的html页面。于是我们又开始了阅读源码的过程。
通过阅读我们发现,tomcat处理jsp的关键是其默认配置了一个JSPServlet类,用于处理所有的jsp文件。通过分析Service方法和serviceJspFile方法我们发现了tomcat对于jsp的处理基本过程是:
首先,通过uri获取要处理的jsp路径。
其次,尝试从JspRuntimeContext缓存容器中获取JspWrapper如果没有,就开启线程同步去构建一个,构建时会通过context.getResource来判断是否有jsp文件(也就是该方法限定了jsp必须在项目目录下,也就是配置的docBase目录下)。
第三,如果有,就创建JSPWrapper并以uri为key加入缓存中。
第四,调用JspWrapper.service方法来完成jsp响应(该方法完成了jsp的编译存储和服务处理。每次都会判断是不是开发模式和最后一次修改时间,如果有更新就重新编译)
在阅读中我们发现,对于jsp资源获取的关键类实际上只有3个一个是JSPServlet,一个是JspWrapper一个是JspCompilationContext.这三个类。
JSPServlet说了,是用于处理*.jsp的请求,负责获取jsp的路径,并查找相关的JSPWrapper来响应。
JSPWrapper,包装了jsp文件,完成了jsp的编译,和响应
JspCompilationContext,Jsp的编辑容器,完成了jsp的资源查找和编辑器创建等操作,在jsp第一次加载的时候完成了大量的初始化工作。
下面我们就改造这三个类来实现我们自己的想法,这里我继承这三个类分别作了ModuleJspServlet,ModuleJSPWrapper和ModuleJSPCompilationContext类,来完成相关的扩展功能。
ModuleJspServlet中我们简单修改了,判断方法,也就是不在简单的使用ServletContext.getResource来判断是否存在jsp,而是通过JspCompilationContext来判断。
private void serviceJspFile(HttpServletRequest request, HttpServletResponse response, String jspUri, Throwable exception, boolean precompile) throws ServletException, IOException { JspServletWrapper wrapper = rctxt.getWrapper(jspUri); <span style="color:#ff0000;"> ModuleJspCompilationContext jc = new ModuleJspCompilationContext(jspUri, false, options, context, null, rctxt);</span> if (wrapper == null) { synchronized(this) { wrapper = rctxt.getWrapper(jspUri); if (wrapper == null) { // Check if the requested JSP page exists, to avoid // creating unnecessary directories and files. if (<span style="color:#ff0000;">null == jc.getResource(jspUri)</span>) { handleMissingResource(request, response, jspUri); return; } boolean isErrorPage = exception != null; wrapper = new ModuleJspServletWrapper(config, options, jspUri, isErrorPage, rctxt); rctxt.addWrapper(jspUri,wrapper); } } } try { wrapper.service(request, response, precompile); } catch (FileNotFoundException fnfe) { handleMissingResource(request, response, jspUri); } }红色部分使我们的修改。这个类的修改比较简单。
对于ModuleJSPWrapper我们也只是修改了其构造函数:
public ModuleJspServletWrapper(ServletConfig config, Options options, String jspUri, boolean isErrorPage, JspRuntimeContext rctxt) throws JasperException { super(config, options, jspUri, isErrorPage, rctxt); this.isTagFile = false; this.config = config; this.options = options; <span style="color:#ff0000;">if(jspUri.startsWith("jar:file:")) { jarPath = jspUri.substring(0,jspUri.indexOf("!/") + 2); jspUri = jspUri.substring(jspUri.indexOf("!/") + 1); }else if(jspUri.startsWith("file:")) {//文件系统开始,以此开头处理的是开发模式下 jarPath = jspUri.substring(0,jspUri.indexOf("/") + 1); jspUri = jspUri.substring(jspUri.indexOf("/")); }</span> this.jspUri = jspUri; ctxt = new ModuleJspCompilationContext(jspUri, isErrorPage, options, config.getServletContext(), this, rctxt); }这里增加了对于jar包文件和工作空间的处理,后者是针对开发的便利性提出的开发模式来的,这样在完成开发前都可以不用打包。
主要的修改在ModulJspCompilationContext中,获取资源的方法我们做了下简单修改。
public URL getResource(String res) throws MalformedURLException { URL result = null; if (res.startsWith("/META-INF/")) { // This is a tag file packaged in a jar that is being compiled URL jarUrl = tagFileJarUrls.get(res); if (jarUrl == null) { jarUrl = tagFileJarUrl; } if (jarUrl != null) { result = new URL(jarUrl.toExternalForm() + res.substring(1)); } } else if (res.startsWith("jar:file:") || res.startsWith("file:")) { // This is a tag file packaged in a jar that is being checked // for a dependency result = new URL(res); <span style="color:#ff0000;"> } else if (this.jarPath != null){ result = new URL(jarPath + res);</span> }else { result = context.getResource(canonicalURI(res)); } return result; }
我们针对自己的路径做了下处理。这样就完成了jsp的跨项目加载和模块化加载。
总结:这里通过跟踪分析,知识修改了jsp的加载方式,不在只是从项目目录下加载。其他未做修改。
在测试时发现,我们单纯的返回资源不设置请求响应码,容易造成浏览器莫名其妙的缓存问题。这点算是额外收获了。
相关文章推荐
- Tomcat数据源
- Windows下Tomcat配置
- install plugins, such as spring (STS), maven, tomcat, etc.
- tomcat启动闪退的原因之一
- tomcat下CORS(跨域资源共享) 的配置
- Tomcat的跨区域访问问题
- CentOS 6.7 配置JSP运行环境之tomcat
- 《How Tomcat Works》学习笔记(一)
- 一个服务器多个Tomcat问题
- Tomcat启动内存设置
- MyEclipse的tomcat端口号的修改
- Tomcat热部署
- Tomcat下work文件夹的作用
- 配置tomcat,在访问时不需要加端口号和工程名
- eclipse中的WEB项目打包部署到tomcat
- TOMCAT异常 Socket bind failed: [730048]
- eclipse下的tomcat内存设置大小
- Tomcat热部署的三种方式
- tomcat加载启动越来越慢怎么解决?
- Tomcat内存溢出解决办法