路由设计

现代 Web 应用的 URL 十分优雅,易于人们辨识记忆。 路由的表现形式如下:

/resources/:resource/actions/:action
http://bladejava.com
http://bladejava.com/docs/modules/route

那么我们在java语言中将他定义一个 Route 类, 用于封装一个请求的最小单元, 在Mario中我们设计一个路由的对象如下:

/**
 * 路由
 * @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个类:

/**
 * 路由管理器,存放所有路由的
 * @author biezhi
 */
public class Routers {

    private static final Logger LOGGER = Logger.getLogger(Routers.class.getName());

    private List<Route> routes = new ArrayList<Route>();

    public Routers() {
    }

    public void addRoute(List<Route> 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<Route> getRoutes() {
        return routes;
    }

    public void setRoutes(List<Route> routes) {
        this.routes = routes;
    }

}

这里的代码很简单,这个管理器里用List存储所有路由,公有的 addRoute 方法是给外部调用的。

/**
 * 路由匹配器,用于匹配路由
 * @author biezhi
 */
public class RouteMatcher {

    private List<Route> routes;

    public RouteMatcher(List<Route> routes) {
        this.routes = routes;
    }

    public void setRoutes(List<Route> routes) {
        this.routes = routes;
    }

    /**
     * 根据path查找路由
     * @param path    请求地址
     * @return        返回查询到的路由
     */
    public Route findRoute(String path) {
        String cleanPath = parsePath(path);
        List<Route> matchRoutes = new ArrayList<Route>();
        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<Route> routes) {
        Collections.sort(routes, new Comparator<Route>() {
            @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;
        }
    }

}

路由匹配器使用了正则去遍历路由列表,匹配合适的路由。当然我不认为这是最好的方法, 因为路由的量很大之后遍历的效率会降低,但这样是可以实现的,如果你有更好的方法可以告诉我 :)

在下一章节我们需要对请求处理做设计了~

results matching ""

    No results matching ""