# 路由设计 现代 Web 应用的 URL 十分优雅,易于人们辨识记忆。 路由的表现形式如下: ``` /resources/:resource/actions/:action http://bladejava.com http://bladejava.com/docs/modules/route ``` 那么我们在java语言中将他定义一个 `Route` 类, 用于封装一个请求的最小单元, 在Mario中我们设计一个路由的对象如下: ```java /** * 路由 * @author biezhi */ public class Route { /** * 路由path */ private String path; /** * 执行路由的方法 */ private Method action; /** * 路由所在的控制器 */ private Object controller; public Route() { } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public Method getAction() { return action; } public void setAction(Method action) { this.action = action; } public Object getController() { return controller; } public void setController(Object controller) { this.controller = controller; } } ``` 所有的请求在程序中是一个路由,匹配在 `path` 上,执行靠 `action`,处于 `controller` 中。 Mario使用一个Filter接收所有请求,因为从Filter过来的请求有无数,如何知道哪一个请求对应哪一个路由呢? 这时候需要设计一个路由匹配器去查找路由处理我们配置的请求, 有了路由匹配器还不够,这么多的路由我们如何管理呢?再来一个路由管理器吧,下面就创建路由匹配器和管理器2个类: ```java /** * 路由管理器,存放所有路由的 * @author biezhi */ public class Routers { private static final Logger LOGGER = Logger.getLogger(Routers.class.getName()); private List routes = new ArrayList(); public Routers() { } public void addRoute(List routes){ routes.addAll(routes); } public void addRoute(Route route){ routes.add(route); } public void removeRoute(Route route){ routes.remove(route); } public void addRoute(String path, Method action, Object controller){ Route route = new Route(); route.setPath(path); route.setAction(action); route.setController(controller); routes.add(route); LOGGER.info("Add Route:[" + path + "]"); } public List getRoutes() { return routes; } public void setRoutes(List routes) { this.routes = routes; } } ``` 这里的代码很简单,这个管理器里用List存储所有路由,公有的 `addRoute` 方法是给外部调用的。 ```java /** * 路由匹配器,用于匹配路由 * @author biezhi */ public class RouteMatcher { private List routes; public RouteMatcher(List routes) { this.routes = routes; } public void setRoutes(List routes) { this.routes = routes; } /** * 根据path查找路由 * @param path 请求地址 * @return 返回查询到的路由 */ public Route findRoute(String path) { String cleanPath = parsePath(path); List matchRoutes = new ArrayList(); for (Route route : this.routes) { if (matchesPath(route.getPath(), cleanPath)) { matchRoutes.add(route); } } // 优先匹配原则 giveMatch(path, matchRoutes); return matchRoutes.size() > 0 ? matchRoutes.get(0) : null; } private void giveMatch(final String uri, List routes) { Collections.sort(routes, new Comparator() { @Override public int compare(Route o1, Route o2) { if (o2.getPath().equals(uri)) { return o2.getPath().indexOf(uri); } return -1; } }); } private boolean matchesPath(String routePath, String pathToMatch) { routePath = routePath.replaceAll(PathUtil.VAR_REGEXP, PathUtil.VAR_REPLACE); return pathToMatch.matches("(?i)" + routePath); } private String parsePath(String path) { path = PathUtil.fixPath(path); try { URI uri = new URI(path); return uri.getPath(); } catch (URISyntaxException e) { return null; } } } ``` 路由匹配器使用了正则去遍历路由列表,匹配合适的路由。当然我不认为这是最好的方法, 因为路由的量很大之后遍历的效率会降低,但这样是可以实现的,如果你有更好的方法可以告诉我 :) 在下一章节我们需要对请求处理做设计了~ ## links * [目录]() * 上一节: [项目规划](<1.plan.md>) * 下一节: [控制器设计](<3.controller.md>)