现代 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;
}
}
}
路由匹配器使用了正则去遍历路由列表,匹配合适的路由。当然我不认为这是最好的方法, 因为路由的量很大之后遍历的效率会降低,但这样是可以实现的,如果你有更好的方法可以告诉我 :)
在下一章节我们需要对请求处理做设计了~