打造一款属于自己的web服务器——配置controller

这天一热,胖子的的世界就是一片凄惨啊,随便动动身子,就跟洗个澡似得,心情固然烦躁,一烦躁就很难静下心来写东西了......所以这一段没咋用心写,就稍微水点吧,同时,我又打算要减肥了!>_<!。

上一次我们介绍了session的实现,使web服务器(现在总觉得准确来说应该叫可独立部署的web框架,称不上服务器)具备了基本功能,但是仔细一想就会发现一个严重的问题:每当实现一个新的controller,那么就需要在invokController方法里边增加判断,以便url能够找到对应controller。对于一个web服务器(或是框架)而言,内部应该是封闭的,即使是为了支持拓展也应该是提供外部接口实现,通过改代码来修改,绝对是极其糟糕的设计。下边我们来看几种实现方式:

一、如何实现

首先我们来思考一个url请求时如何映射到对应的处理方法的,常规思路如下:

可以看到这里边有两个关键点:1、路由表的结构是怎么实现的?2、查到对应类的对象是怎么加载的(实例化)?只要弄清楚了这两个问题,其实一切就迎刃而解了。

   
路由表的主要作用是通过url能够找到对应的对应处理类的对应方法,我们很容易就能想到的结构就是key-value结构的map了,实际上多数情况也的确如此。但是要注意到是,在实际存储的时候,具体的内容要看情况而定,在简单情况下直接key存uri,value存处理类信息就行,但是当你想支持多种映射规则时(如前缀匹配、正则匹配等等),就要归类存储了。总体而言这一部分还是很容易实现的。

   
下边我们来看一下第二个问题,路由表里边一般存储的应该是类信息(至于到具体方法的映射大同小异),而且多数情况下只是类名,但是实际处理却需要类对象来执行。那么我们就需要考虑这些类对象时如何加载的。一种方法就是在web服务器启动时就实例化所有注册的类(应该也是通过反射),而另一种则是在使用的时候根据类名通过反射动态生成对象,其实两者区别只是对象实例化的时机。(这里突然想到一个问题,一般而言这里实例化的对象应该是单例的,也就是相同请求用的对象都是一个,并发处理时在器内部实现的,本项目目前暂未考虑这点,之后会改进的)

二、功能设计

还是先来设计一下,这一次比较简单,对于路由表我们目前只需要支持uri和controller一一对应的情况,所以直接使用一个map存储就好。而配置为了方便直接通过注解实现(之前实现了配置文件配置,后来去掉了,需要的话可以自己加上),web服务器启动时先获取制定目录下所有controller类型注解,然后根据注解信息将映射存入map。而url请求时,从map中查找到对应类名,通过反射实例化并调用对应方法。

三、实现代码

首先来看一下注解的定义( java自定义注解 ),这里定义了两个注解,一个是url的映射,另一个则是方法映射(只有声明该注解的方法会被映射
):


package org.eh.core.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Controller注解
* @author guojing
* @date 2014-3-5
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Controller {

/**
* controller名,暂时无用
*/
public String name();

/**
* 对应url请求路径,如htp://127.0.0.1/test/list.do 则对应 controller为:/test/,对应方法为:list
*/
public String url();
}
/**
* 方法映射注解
* @author guojing
* @date 2014-3-13
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface RequestMapping {

}

定义完了注解,再来实现一个 AnnocationHandler处理注解信息 ,该类主要实现三个功能:1.
 获取指定包下的所有类名(包含包名),2. 将所有注解Controller加入Constants.UrlClassMap,3.
获取类的指定方法 ,其实现如下:


/**
* 注解处理类
* @author guojing
* @date 2014-3-5
*/
public class AnnocationHandler {

/**
* 将所有注解Controller加入Constants.UrlClassMap
* @param parkage 类名(包含包路径)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public void paserControllerAnnocation(String parkage) throws ClassNotFoundException {
List<String> classlist = getPkgClass(parkage);
for (String str : classlist) {
Class c = Class.forName(str);
if (c.isAnnotationPresent(Controller.class)) {
Controller desc = (Controller) c.getAnnotation(Controller.class);
Constants.UrlClassMap.put(desc.url(), str);
}
}
}

/**
* 获取指定包下的所有类名(包含包名)
* @param parkage 指定包名
* @return
*/
public List<String> getPkgClass(String parkage) {
String path = Constants.CLASS_PATH + parkage.replace(".", "/") + "/";
List<String> list = new ArrayList<String>();

File file = new File(path);
for (String str : file.list()) {
if (str.endsWith(".class")) {
list.add(parkage + "." + str.replace(".class", ""));
} else if (str.indexOf(".") == -1) {
list.addAll(getPkgClass(parkage + "." + str));
}
}

return list;
}

/**
* 获取类的指定方法
* @param c
* @param methodName
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public Method getMethod(Class c, String methodName) throws NoSuchMethodException,
SecurityException {
Method method = c.getMethod(methodName, Map.class);
return method.isAnnotationPresent(RequestMapping.class) ? method : null;
}
}

然后要在 EHHttpHandler的
invokController方法中通过反射( java反射 )调用方法,如下:


/**
* 调用对应Controller处理业务
*/
private ResultInfo invokController(HttpExchange httpExchange) {
String path = httpExchange.getRequestURI().getPath();

String classPath = Constants.UrlClassMap.get(path.substring(0,
path.lastIndexOf("/") + 1));
if (classPath == null || classPath.length() == 0) {
return null;
}
Class controllerClass = Class.forName(classPath);
Controller controller = (Controller) controllerClass.newInstance();

String methodName = path.substring(path.lastIndexOf("/") + 1,
path.lastIndexOf("."));
// 通过反射获取对应方法
AnnocationHandler annocationHandler = new AnnocationHandler();
Method method = annocationHandler
.getMethod(controllerClass, methodName);

Map<String, Object> map = null; // 参数
map = analysisParms(httpExchange);

// 设置session
HttpSession httpSession = ApplicationContext.getApplicationContext()
.getSession(httpExchange);
map.put("session", httpSession);

return (ResultInfo) method.invoke(controller, new Object[] { map });
}

最后还要在启动时加载配置信息,在 EHServer的 startServer中添加


// 加载注解配置的controller
AnnocationHandler annocationHandler = new AnnocationHandler();
try {
annocationHandler
.paserControllerAnnocation("org.eh.core.web.controller");
} catch (Exception e) {
log.error("加载controller配置出错!", e);
return;
}

至此,此次功能完成。

四、测试

下边我们来测试下该功能是否好用,还是用 IndexController吧,我们加入注解信息:


/**
* 主页对应的contoller
* @author guojing
*/
@org.eh.core.annotation.Controller(name = "session", url = "/session/")
public class IndexController implements Controller{

@RequestMapping
public ResultInfo process(Map<String, Object> map){
ResultInfo result =new ResultInfo();

// 这里我们判断请求中是否有name参数,如果有则放入session,没有则从session中取出name放入map
HttpSession session = (HttpSession) map.get("session");
if (map.get("name") != null) {
Object name = map.get("name");
session.addAttribute("name", name);
} else {
Object name = session.getAttribute("name");
if (name != null) {
map.put("name", name);
}
}

result.setView("index");
result.setResultMap(map);
return result;
}
}

然后浏览器打开 http://localhost:8899/session/process.do?name=guojing,结果又看到这个熟悉的页面了,说明一切ok,你也可以多写几个controller,直接加入注解就能访问,不用改其他任何代码

五、总结

照例最后来点废话,至此其实本项目才真正能拿去用了,不过如果你真打算用,就会发现很多不爽的地方,比如本项目代码和业务代码混在一起、模板支持太差、配置信息写在代码里等等,那么下次我们将来解决这些问题。
 
  最后献上源码,learn-3源码(对应的 master 为完整项目): 源码

时间: 2024-08-02 08:28:50

打造一款属于自己的web服务器——配置controller的相关文章

打造一款属于自己的web服务器——最后的一点完善

上一篇我们通过反射实现了动态加载多个controller,就功能上来说整个项目已经基本上完成了,但是目前我们仍然还有一些问题,例如模板支持不好.很多配置信息硬编码不好修改.此外,我们预期的目标是实现一个可嵌入的jar,以实现web服务,而就目前而言明显是不行的.那么我们现在就来解决这些问题. 一.使用velocity拓展模板    想要实现一套完善的模板还是比较麻烦的,所以目前我们考虑使用java支持的模板来实现,目前比较常用的有Freemaker,Velocity等,因为比较熟悉velocit

打造一款属于自己的web服务器——从简单开始

距离开篇已经过了很久,期间完善了一下之前的版本,目前已经能够完好运行,基本上该有的功能都有了,此外将原来的测试程序改为示例项目,新项目只需按照示例项目结构实现controller和view即可,详情见: easy-httpserver. demo-httpsrever.    这次我们将首先来实现一个简单版本,该版本只包括一些基本的功能,后续版本也将在此基础上一步步改进. 一.准备工作 俗话说的好,工欲善其事,必先利其器.我们在开始开发之前应做好如下准备(真的很简单): java开发环境,IDE

打造一款属于自己的web服务器——实现Session

上一次我们已经实现了一个简单的web服务器版本,能够实现一些基本功能,但是在最后也提到了这个版本由于不支持session并不能实现真正的动态交互,这一次我们就来完成这一功能. 一.Session实现原理 凡是搞过web开发的都知道,多数情况下浏览器请求服务器使用的是http请求,而http请求是无状态的,也就是说每次请求服务器都会新建连接,当得到响应后连接就关闭了,虽然http1.1支持持久连接(keep-alive),但是其最用主要是避免每次重建连接,而非解决用户在线状态等业务上的需求.而如果

Linux Apache web服务器 配置详细教程

3 Linux Apache web服务器 v2.4.29学习要点: 1.apache用途,工作模式,httpd.conf的配置重要参数2.虚拟主机 工作模式的参数优化 3.1 Apache 概述: 3.1.1 Apache 概述Apache是世界使用排名第一的Web服务器软件.它可以运行在几乎所有广泛使用的计算机平台上,由于其跨平台和安全性被广泛使用,是最流行的Web服务器端软件之一.它快速.可靠并且可通过简单的API扩充,将Perl/Python等解释器编译到服务器中.同时Apache音译为

Linux下四款Web服务器压力测试工具(http_load、webbench、ab、siege)介绍

一.http_load程序非常小,解压后也不到100Khttp_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载.但是它不同于大多数压力测试工具,它可以以一个单一的进程运行,一般不会把客户机搞死.还可以测试HTTPS类的网站请求. 下载地址:http://soft.vpser.net/test/http_load/http_load-12mar2006.tar.gz 安装#tar zxvf http_load-12mar2006.tar.gz#cd http_load-12mar

Java 并发专题 : Executor详细介绍 打造基于Executor的Web服务器

适配器模式,将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 应用场景:系统的数据和行为都正确,但接口不符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配.适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况. 代码实现: //Adapter.h #include "stdafx.h" #include <iostream> class Adaptee

几款Web服务器性能压力测试工具

一.http_load 程序非常小,解压后也不到100Khttp_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载.但是它不同于大多数压力测试工具,它可以以一个单一的进程运行,一般不会把客户机搞死.还可以测试HTTPS类的网站请求.下载地址:http_load-12mar2006.tar.gz 安装很简单 #tar zxvf http_load-12mar2006.tar.gz #cd http_load-12mar2006 #make && make install 基本

九款Web服务器性能压力测试工具

一.http_load 程序非常小,解压后也不到100Khttp_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载.但是它不同于大多数压力测试工具,它可以以一个单一的进程运行,一般不会把客户机搞死.还可以测试HTTPS类的网站请求.下载地址:http_load-12mar2006.tar.gz安装很简单 #tar zxvf http_load-12mar2006.tar.gz#cd http_load-12mar2006#make && make install 基本用法:

我叫Tomcat:一款web服务器

我叫Tomcat:一款web服务器 如何将我们的 Java 代码,运行在网络上,初学时,首先接触到的一般都是Servlet以及Jsp(或略过Jsp)而 Tomcat 就是这两者的容器,帮你处理动态网页部分 (一) 从哪来,到哪去? (1) Tomcat和它的小伙伴 JBoss:Redhat 红帽,支持所有的 JavaEE 规则,适合大型项目,收费 Weblogic:Orcale,支持所有 JavaEE 规则,适合大型项目,收费 Websphere:IBM,支持所有 JavaEE 规则,适合大型项