前两章主要贴出了服务器通讯核心和资源执行器的实现,这两个东西其实都不难,只要花些时间,我相信大部分的程序员都能做出来。两章结束后我们的服务器应该已经能够实现html等资源的执行了,而我们也即将遇到服务器开发中的第一个难点:远端类加载!
远端类加载是所有服务器都无法回避的第一道门槛,web服务器与web 项目是两个不同的域(或者说是两个不同的项目),app server又需要在运行中动态解析项目(包括拆war包),因此我们无法够预先定义web服务器的classPath使其支持web 项目中的类,所以远端加载势在必行。
java里面所有的类加载,都必须使用classloader,既然如此,我们就重载一个:
tomcat的代码里面做了三个classloader,分别对应系统类、外部类和jar包,我的实现里只做了一个,这样会牺牲一些效率,但是在调用加载器的时候就不必在做复杂的判断了。
下面是servlet执行器代码,其中调用了新的classLoader
呵呵,这样我们的服务器就从web服务器进化到servlet容器了:》
不过我贴出来的代码只是比较关键的类,至于数据结构,有经验的程序员应该很容易猜出来——全局有一个appRuntime类,里面包含了若干个projectInfo类(储存项目信息),projectInfo里储存了每个web项目中web.xml文件的信息,包括servlet与路径的对应关系等等。
好久不写东西了,语言和思维有一些僵硬,慢慢来吧,下一章打算贴一点http协议解析的东西。
远端类加载是所有服务器都无法回避的第一道门槛,web服务器与web 项目是两个不同的域(或者说是两个不同的项目),app server又需要在运行中动态解析项目(包括拆war包),因此我们无法够预先定义web服务器的classPath使其支持web 项目中的类,所以远端加载势在必行。
java里面所有的类加载,都必须使用classloader,既然如此,我们就重载一个:
package util; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.Hashtable; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import server.AppRunTime; /** * 这个类专门用来解析web工程里用到的class文件 * 包括jar文件,classes文件夹,和系统类文件 * @author 刘宇航 */ public class LocalClassLoader extends ClassLoader { private Map<String, Class<?>> loadedClasses = new Hashtable<String, Class<?>>(); private static Map<String,LocalClassLoader> loaders= new Hashtable<String, LocalClassLoader>(); private String projectPath = ""; int cacheSize=6;//默认的class文件大小 /** * 防止其他初始化途径 */ private LocalClassLoader(){ cacheSize = (Integer)AppRunTime.getInstance().getProperty(Constant.SERVER_CACHE); } /** * 针对每一个工程,返回一个单例 * @param projPath 传入一个项目根路径 用来区分不同项目的LocalClassLoader * @return LocalClassLoader */ public static LocalClassLoader getInstance(String projPath){ if(loaders.containsKey(projPath)==false) { LocalClassLoader instance = new LocalClassLoader(); instance.projectPath = projPath; loaders.put(projPath, instance); } return loaders.get(projPath); } /** * 加载相关类 */ public synchronized Class<?> loadClass(String className,boolean resolve)throws ClassNotFoundException{ Class<?> newClass ; byte[] classData=null; //检查是否已经加载 newClass = (Class<?>) loadedClasses.get(className); if(newClass!=null){ if(resolve) this.resolveClass(newClass); return newClass; } //如果还没有加载,那么第一件事则是去系统中加载 //之所以这么费事,是因为被加载的类可能用到系统类,根据约定这些 //系统类会被同一个classloader加载进来,所以必须加入以下代码 try { newClass =super.findSystemClass(className); if(resolve) this.resolveClass(newClass); return newClass; } catch (ClassNotFoundException e) { System.out.println(className.trim()+"并非系统类,将调用外部加载程序……"); } //下面的是针对外部工程的加载程序 try{ String classFileName = projectPath+Constant.PROJECT_CLASSES_PATCH+className.trim(); classData = getClassData(classFileName); }catch(Exception e) { e.printStackTrace(); } //如果是jsp if(null==classData){ System.out.println(className+" 不在工程目录下,开始尝试加载work文件夹"); String classFileName = projectPath+Constant.FILE_SP+className; System.out.println("尝试加载"+classFileName); classData = getClassData(classFileName); } //对jar文件和路径还需要特殊加载 if(null==classData){ System.out.println(className+" 不在/classes目录下,开始尝试加载jar文件"); classData = loadFromJarFile(className); } //其他操作 newClass = defineClass(null,classData,0,classData.length); loadedClasses.put(className, newClass); if(resolve) this.resolveClass(newClass); return newClass; } /** * 从jar文件中返回class文件的字节数组,当返回值为null时,表示没找到对应类 * @param className 类名 * @return 以字节数组形式保存的类的内容 */ private synchronized byte[] loadFromJarFile(String className) { String jarFilePatch = projectPath+Constant.PROJECT_JAR_PATCH; File patch = new File (jarFilePatch); if(patch.isDirectory()) { File[] fileArray = patch.listFiles(new FileTypeFilter(Constant.FileType_SP_jar)); for(File jar:fileArray) { byte[] values = getDataFromFile(jar,className); if(values!=null&&values.length>0) return values; } } //否则返回null return null; } private byte[] getDataFromFile(File jar,String className) { className = className.replace(".", "/")+Constant.FileType_SP_Class; int length=0; byte[] data = new byte[1024*8];//8m的class文件,大小应该在改为从配置文件读取 try { ZipFile jarFile = new ZipFile(jar); ZipEntry entity = jarFile.getEntry(className); InputStream in = jarFile.getInputStream(entity); length= in.read(data); //数据处理 byte[] returnValue = new byte[length]; System.arraycopy(data, 0, returnValue, 0, length); return returnValue; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 获得类的内容 ,当返回值为null时,表示没找到对应类 * 但是这个类现在有一个问题,当classFileName中含有 * 作为非类分隔符而存在的'.'字符时,路径解析会不正确 * @param classFileName * @return 字节数组 */ private synchronized byte[] getClassData(String classFileName) { byte[] data = new byte[1024*cacheSize];//8m的class文件,应该够大了吧 int length=0; classFileName = classFileName.replace(".", "\\"); classFileName+=Constant.FileType_SP_Class; System.out.println("正在加载" +classFileName); File classFile = new File(classFileName); if(!classFile.exists()||!classFile.canRead()) return null; try { FileInputStream in = new FileInputStream(classFile); length = in.read(data); } catch (Exception e) { e.printStackTrace(); } byte[] returnValue = new byte[length]; System.arraycopy(data, 0, returnValue, 0, length); return returnValue; } }
tomcat的代码里面做了三个classloader,分别对应系统类、外部类和jar包,我的实现里只做了一个,这样会牺牲一些效率,但是在调用加载器的时候就不必在做复杂的判断了。
下面是servlet执行器代码,其中调用了新的classLoader
/** * @author: 刘宇航 @create: 2009-4-13 * @modifier: 刘宇航 @modify: 2009-4-13 * @reviewer: 刘宇航 @review: 2009-4-13 * 执行servlet的执行器 */ package executor.impl; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.FilterChain; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import project.ProjectInfo; import servlet.ServletInfo; import util.Constant; import util.LocalClassLoader; import executor.CheckableExecutor; public class ServletExecutor implements CheckableExecutor { private ProjectInfo project; private ServletInfo info; public void init(ProjectInfo project) { this.project = project; } /** * 执行servlet的方法体 */ @Override public void forward(HttpServletRequest request, HttpServletResponse response) throws Exception { // 执行部分 ClassLoader loader = LocalClassLoader.getInstance(project.getPatch()); HttpServlet servlet = (HttpServlet) loader.loadClass( info.getServletClass()).newInstance(); // 先执行filter 然后是servlet本身 FilterChain chain = info.getFilterChain(); chain.doFilter(request, response); servlet.init(info.getServletConfig()); servlet.service(request, response); servlet.destroy(); } /** * 判断资源是否可以执行 */ @Override public boolean isExecutable(HttpServletRequest request) { //先创建ServletInfo对象 ServletInfo info =getServletInfo(request); if(info != null) { this.info = info; return true; } else return false; } /** * 匹配servlet * @param request * @return */ private ServletInfo getServletInfo(HttpServletRequest request) { for(ServletInfo info :project.getServletInfo()) { Pattern p = Pattern.compile(Constant.WEB_SP+project.getWebPath()+info.getUrl_pattern()); Matcher m = p.matcher(request.getRequestURI()); if(m.matches()) return info; } return null; } }
呵呵,这样我们的服务器就从web服务器进化到servlet容器了:》
不过我贴出来的代码只是比较关键的类,至于数据结构,有经验的程序员应该很容易猜出来——全局有一个appRuntime类,里面包含了若干个projectInfo类(储存项目信息),projectInfo里储存了每个web项目中web.xml文件的信息,包括servlet与路径的对应关系等等。
好久不写东西了,语言和思维有一些僵硬,慢慢来吧,下一章打算贴一点http协议解析的东西。
相关推荐
作为一个处于开发和测试阶段的小型容器,Fried Cake Server为开发者提供了在JSP(JavaServer Pages)环境中的部署和管理应用的新选择。 J2EE,全称Java 2 Platform, Enterprise Edition,是由Oracle公司主导的Java...
- 萝卜糕:Turnip Cake 或 Fried White Radish Patty - 盐鸭蛋:Salted Duck Egg - 锅贴:Fried Dumpling - 馍头:Steamed Bun - 葱油饼:Spring Onion Pancake - 饺子:Dumpling - 稀饭(粥):Rice ...
"transLucid (was Lucid Fried Eggs)" 这个标题表明这是一个名为 "transLucid" 的项目,之前可能被称为 "Lucid Fried Eggs"。这可能是因为项目经历了改名或更新,通常这样的变化伴随着软件的发展或品牌重塑。"开源" ...
GOMake a dent in the universeScratch your own itchStart making somethingNo time is no excuseDraw a line in the sandMission statement impossibleOutside money is Plan ZYou need less than you thinkStart ...
Fried衰弱评估方法.pdf
QTip QTip是通过命令行管理的自托管... qtip serve /recipe/fried-chicken fried-chicken-recipe.md 产品特点 Markdown文件支持 自动将markdown中嵌入的本地图像上传和存储到您的首选CDN 100%自托管 :rocket: 演示版
- 蔬菜类:如蒜蓉西兰花(Stir-Fried Broccoli with Garlic) - 豆腐类:如豆腐烧肉(Stewed Tofu with Pork) - 燕窝类:如燕窝羹(Bird's Nest Soup) - 羹汤煲类:如西红柿牛腩汤(Beef Stew with Tomatoes)...
自动正确地回答来自www.freerice.com的问题,从而向联合国提供了大量大米,以养活世界。
5. 煎蛋火鸡 - Fried egg with turkey 6. 饺子 - Dumplings 7. 甜品 - Dessert 8. 三明治 - Sandwich 9. 比萨 - Pizza 10. 汉堡 - Hamburger 11. 蛋糕 - Cake 12. 西瓜 - Watermelon 13. 梨 - Pear 14. 橙子 - ...
- French fries:炸薯条,切片的土豆油炸而成。 - baked potato:烘马铃薯,整个土豆烘烤至软糯。 - mashed potatoes:马铃薯泥,捣碎的煮熟土豆。 - omelette:简蛋卷,煎熟的鸡蛋卷。 - pudding:布丁,甜点,通常...
- "I recommend crispy and fried duck."(我推荐香酥鸭。) 这些对话涵盖了餐厅服务的基本流程,包括迎接顾客、询问需求、推荐菜品、预订和等待座位、以及就座后的点餐过程。了解这些常用表达对于在餐厅工作的...
环0-3内核与用户空间分离 交流会 APIC / IOAPIC APIC计时器 完整的vsprintf实现(无浮点数:) 进行中 PS / 2键盘驱动程序(8042) 待办事项 添加测试框架 先决条件 编译:$ sudo pacman -S grub mtools nasm ...
- `油炸食品`: fried food - `体检`: physical examination - `减肥`: lose weight - `通常;大体上`: usually; generally - `加糖的咖啡`: sugary coffee - `a bit of`: 一点点 - `would rather do sth.`: ...
* 萝卜糕(Fried white radish patty) * 芋头糕(Taro cake) 特殊饮料 * 果汁甘蔗汁(Sugar cane juice) * 酸梅汁(Plum juice) * 杨桃汁(Star fruit juice) * 青草茶(Herb juice) 特殊点心 * 当归鸭...
- 我不愿意吃太多的油炸食品:I am unwilling to eat too much fried food. - 主要是膳食平衡:mainly a balanced diet - 我同意多吃水果和蔬菜对身体好:I agree that eating more fruits and vegetables is ...
- **Deep Fried Food**:油炸食品。 #### 七、注册办公室场景词汇 - **Fieldwork/Research**:实地调查或研究项目。 - **Academic Record**:学业成绩记录。 - **Financial Aid**:经济援助,包括奖学金和其他形式...
cbevins /消防设施一种基于1995年杰里米·弗里德(Jeremy S.Fried)和伯顿·弗雷德(Burton D.Fried)论文的火遏制算法和初始攻击模型,题为“用现实战术模拟野火遏制”。 它是根据Jeremy S.Fried(1991)的FCAT防火...
- **Deep-fried food**:油炸食品。 - **Registrar’s Office**:注册办公室,处理课程注册和成绩记录。 - **Fieldwork/research**:实地研究,学术研究的一部分。 - **Academic record**:学业成绩记录。 - **...
- fried:油炸的,指通过热油烹制的食物。 - balanced:平衡的,形容饮食均衡,包含各种必要的营养成分。 - slim:苗条的,形容身材瘦削,符合现代审美标准。 2. 单项选择题: - interested:感兴趣的,用于...