Servlet3.0下组件化开发——静态文件的刷新
来源:互联网 发布:手机数据分析公交 编辑:程序博客网 时间:2024/06/03 12:11
接上一篇博客Servlet3.0之组件化开发,在我们选择将静态文件打包到JAR中后,就会面临这样一个问题:Tomcat会在启动时将JAR中的静态资源载入内存,之后的读取直接进行内存操作。所以作出的修改必须重启才能看到效果;这样无疑是非常影响开发效率的。
1. 引言
在上一篇博客中我们就如何从JAR中获取静态资源这一操作进行了讨论,而对于客户端的请求Tomcat是如何进行处理的呢?
这里我们借用下上一篇博客里的参考链接里的例子,作者给出的示例项目里是让Spring来负责静态资源请求的。通过跟踪一次完整的资源请求,我们可以发现对于静态资源,Spring最终是包装为ServletContextResource
实例;而ServletContextResource
类中对getInputStream
方法的实现就是直接调度给ServletContext
接口的getResourceAsStream
方法;也就是将读取静态资源的逻辑最终还是由容器Tomcat来完成的。
2. Tomcat如何读取静态资源
Tomcat是以ApplicationContextFacade
实例向外暴露ServletContext
接口实现的;而ApplicationContextFacade
是ApplicationContext
的门面模式,所有的操作最终都是调度给了ApplicationContext
。所以最终 ServletContext
接口的getResourceAsStream
方法实现还得到ApplicationContext
类中查找。
2.1 ApplicationContext对getResourceAsStream方法的实现
@Overridepublic InputStream getResourceAsStream(String path) { if (path == null) return (null); if (!path.startsWith("/") && GET_RESOURCE_REQUIRE_SLASH) return null; String normalizedPath = RequestUtil.normalize(path); if (normalizedPath == null) return (null); // 这里的context正是容器StandardContext; // 而返回的resources类型为ProxyDirContext; // 这也和Mybatis中的MixedSqlNode类类似,内部定义的SqlNode实现类最终都是以MixedSqlNode形式向外暴露,这样就统一了门面。 DirContext resources = context.getResources(); if (resources != null) { try { // 所以这里就是我们所要找的核心了,ProxyDirContext类的lookup方法的实现 Object resource = resources.lookup(normalizedPath); if (resource instanceof Resource) return (((Resource) resource).streamContent()); } catch (NamingException e) { // Ignore } catch (Exception e) { // Unexpected log(sm.getString("applicationContext.lookup.error", path, getContextPath()), e); } } return (null);}
2.2 ProxyDirContext类中对lookup方法的实现
@Overridepublic Object lookup(Name name) throws NamingException { // 先从缓存里面找, 注意这里的cacheLookup方法,可以通过配置<context>中的cachingAllowed="false"来保证返回null CacheEntry entry = cacheLookup(name.toString()); if (entry != null) { if (!entry.exists) { throw NOT_FOUND_EXCEPTION; } if (entry.resource != null) { // Check content caching. return entry.resource; } else { return entry.context; } } // 因为ProxyDirContext本身只是个代理,真正的实现还是交给底层的被代理对象dirContext Object object = dirContext.lookup(parseName(name)); if (object instanceof InputStream) return new Resource((InputStream) object); else return object;}
2.3 BaseDirContext类实现的lookup方法
// lookup方法的实现是在BaseDirContext类中完成的;// 而BaseDirContext抽象类是FileDirContext,VirtualDirContext, WARDirContext的基类; 子类并没有对这个方法进行覆盖// 这是tomncat7.0.50里的逻辑!!!! @Override public final Object lookup(String name) throws NamingException { // First check for aliases if (!aliases.isEmpty()) { AliasResult result = findAlias(name); if (result.dirContext != null) { return result.dirContext.lookup(result.aliasName); } } // Next do a standard lookup // 先来一次标准的查询; 这是一个抽象方法, 交由子类去实现各自的逻辑 ; Object obj = doLookup(name); if (obj != null) return obj; // Check the alternate locations for (DirContext altDirContext : altDirContexts) { try { // 这里就是我们本次所要关注的重点了;也就是说tomcat每次读取资源时候,都是以这样的规则尝试从altDirContexts字段所代表的资源容器中读取到匹配的资源。 obj = altDirContext.lookup("/META-INF/resources" + name); if (obj != null) return obj; } catch ( NamingException ex) { // ignore } } // Really not found throw new NameNotFoundException( sm.getString("resources.notFound", name)); }
2.4 altDirContexts字段是如何被赋值的
// BaseDirContext类对addResourcesJar方法的实现,正是对altDirContexts字段进行了填充// 而这个方法只有一个被调用的位置,就是StandardContext类的addResourceJarUrl方法, 这样整个逻辑就通了// 读取到的资源被存放到了BaseDirContext实例的altDirContexts字段中,// 而上面的BaseDirContext实例则是存在与StandardContext实例中/** * Add a resources JAR. The contents of /META-INF/resources/ will be used if * a requested resource can not be found in the main context. */public void addResourcesJar(URL url) { try { JarURLConnection conn = (JarURLConnection) url.openConnection(); JarFile jarFile = conn.getJarFile(); ZipEntry entry = jarFile.getEntry("/"); WARDirContext warDirContext = new WARDirContext(jarFile, new WARDirContext.Entry("/", entry)); warDirContext.loadEntries(); altDirContexts.add(warDirContext); } catch (IOException ioe) { log.warn(sm.getString("resources.addResourcesJarFail", url), ioe); }}
3. 思路
自此,结合上一篇文章,JAR中静态资源的存和取我们应该有了基本印象;我们也大概能想到一个解决所提出的问题的方法了
1. 启动时获取到tomcat容器中代表本项目的那个StandardContext;
2. 同时监控我们自己的jar,如果它们发生了改变,就重新读取新的静态资源,并用它们刷新Context中代表资源的那个字段值。
4. 实现
因为涉及到公司资源,这里我只给思路;详细实现代码就不贴了。
public class StaticResourceRefresher extends ContextConfig implements ContainerServlet, Servlet, Runnable { // 略}
5. 不完美
- StaticResourceRefresher 目前是整个
WEB-INF/lib
目录刷新一次;后期可以优化为只刷新单个jar, 甚至只刷新jar中的那个被修改的文件。 - tomcat不同版本中的
ContextConfig
类中的方法的签名是不一样的;例如 tomcat 7.0.5和tomcat 7.0.50就存在这样的差异。
6. 参考
这次是真没有。
- Servlet3.0下组件化开发——静态文件的刷新
- Servlet3.0之组件化开发
- Servlet3.0学习总结(三)——基于Servlet3.0的文件上传
- Servlet3.0学习总结(三)——基于Servlet3.0的文件上传
- Servlet3.0学习总结(三)——基于Servlet3.0的文件上传
- Servlet3.0学习总结(三)——基于Servlet3.0的文件上传
- Servlet3.0学习总结(三)——基于Servlet3.0的文件上传
- Servlet3.0学习总结(三)——基于Servlet3.0的文件上传
- Servlet3.0的文件上传
- servlet3.0下监听器的使用
- 基于Servlet3.0的文件上传
- 基于servlet3.0的文件上传
- 利用Servlet3.0的特性上传文件
- servlet3.0 实现文件的上传
- servlet3.0上传文件
- servlet3.0文件上传
- Servlet3.0文件上传
- servlet3.0 文件上传
- Hadoop集群搭建
- 深度学习训练时GPU温度过高?几个命令,为你的GPU迅速降温。
- 图书转借系统
- 使用灵云进行交互型机器人开发日志
- Ubuntu搜狗输入法安装问题
- Servlet3.0下组件化开发——静态文件的刷新
- git命令大全
- 编写一个Singleton类(单例模式)
- 设计性思考维模型及步骤(上)
- 现代操作系统之文件系统(下)
- BZOJ1919: [Ctsc2010]性能优化(FFT循环卷积)
- 多态
- Python学习
- 自定义对话框设置密码