如何处理route的数据结构 ,如何构建route

如何处理route的数据结构 ,如何构建route

route是一个在web程序中扮演很重要的角色,我们的uri其实就是一个路由,通过路由我们可以区分不同的请求,然后分派到具体的类中去执行响应具体的操作,那么怎么知道当前的uri到底是请求的哪个呢,这个就涉及到了route的数据结构的问题,慢慢的去体会他的无穷的奥妙。
Route的数据结构

public class Route {

    /**
     * HTTP Request Method
     */
    private HttpMethod httpMethod;

    /**
     * Route path
     */
    private String path;

    /**
     * Logical controller object
     */
    private Object target;

    /**
     * Controller Class Type
     */
    private Class<?> targetType;

    /**
     * Implementation logic controller method
     */
    private Method action;

    public Route() {
    }

    public Route(HttpMethod httpMethod, String path, Object target, Class<?> targetType, Method action) {
        super();
        this.httpMethod = httpMethod;
        this.path = Path.fixPath(path);
        this.target = target;
        this.targetType = targetType;
        this.action = action;
    }

    public HttpMethod getHttpMethod() {
        return httpMethod;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    public Method getAction() {
        return action;
    }

    public Class<?> getTargetType() {
        return targetType;
    }

    @Override
    public String toString() {
        return httpMethod + "\t" + path;
    }

}

//修正路径的信息哦
 public static String fixPath(String path) {
        if (path == null) {
            return "/";
        }
        if (!path.startsWith("/")) {
            path = "/" + path;
        }
        if (path.length() > 1 && path.endsWith("/")) {
            path = path.substring(0, path.length() - 1);
        }
        return path;
}

HttpMethod 枚举信息

public enum HttpMethod {
    ALL, GET, POST, PUT, PATCH, DELETE, HEAD, TRACE, CONNECT, OPTIONS, BEFORE, AFTER
}

Route的数据包含

  • 请求的类型
  • 请求的路径
  • 响应的Class
  • 响应的Object
  • 响应的Method
    根据以上的几点,我们根本上就可以根据请求的path路径找到相应的执行的的类的信息哦!你觉得了?是不是这个道理。

如何处理自己定义的Route

比如我们在使用spring mvc的时候,肯定在Controller层处理了好多的route对吧,那么我们怎么去获取这写处理的逻辑呢,这里进行简单的说明一下子。

下面的这个是Blade的核心类,里面包含的很多,什么Ioc,全局配置信息,自然也是拥有Routers和RouterBuilder( this.routeBuilder = new RouteBuilder(this.routers)),这里的保存的都是核心的东西,Blade是一个单例,这两个类到底是有何用?慢慢的揭晓这个答案,体会别人是怎么书写,怎么去构造这么大的工程的。

/**
 * Blade Core Class 
 */
public final class Blade {

    /**
     * Framework Global Configuration
     */
    private BConfig bConfig;

    /**
     * Default ioc container
     */
    private Ioc ioc = new SimpleIoc();

    /**
     * Default routes 这个就是route的一个集合
     */
    private Routers routers = new Routers();

    /**
     * Route builder  这里估计就是为了构建工程中书写的route放置在routers中去
     */
    private RouteBuilder routeBuilder;

    /**
     * filters
     */
    private Map<Class<? extends Filter>, String[]> filters = CollectionKit.newHashMap(8);

    /**
     * servlets
     */
    private Map<Class<? extends HttpServlet>, String[]> servlets = CollectionKit.newHashMap(8);

    /**
     * embed web server e.g:jetty/tomcat
     */
    private EmbedServer embedServer;

    private Blade() {
        this.bConfig = new BConfig();
        this.plugins = CollectionKit.newHashSet();
        this.routeBuilder = new RouteBuilder(this.routers);
    }

    private static final class BladeHolder {
        private static final Blade $ = new Blade();
    }

    /**
     * @return Single case method returns Blade object
     */
    public static Blade $() {
        return BladeHolder.$;
    }

    /**
     * @return return route manager
     */
    public Routers routers() {
        return routers;
    }

    /**
     * Setting Ioc packages, e.g:com.bladejava.service
     *
     * @param packages All need to do into the package, can be introduced into a
     *                 number of
     * @return return blade
     */
    public Blade scan(String... packages) {
        bConfig.addScanPackage(packages);
        return this;
    }

    /**
     * Add a route
     *
     * @param path   route path
     * @param clazz  Target object for routing
     * @param method The method name of the route (at the same time, the HttpMethod
     *               is specified: post:saveUser, if not specified, HttpMethod.ALL)
     * @return return blade
     */
    public Blade route(String path, Class<?> clazz, String method) {
        routers.route(path, clazz, method);
        return this;
    }

    /**
     * regsiter filter
     *
     * @param clazz
     * @param pathSpec
     * @return
     */
    public Blade registerFilter(Class<? extends Filter> clazz, String... pathSpec) {
        filters.put(clazz, pathSpec);
        return this;
    }

    /**
     * regsiter servlet
     *
     * @param clazz
     * @param pathSpec
     * @return
     */
    public Blade registerServlet(Class<? extends HttpServlet> clazz, String... pathSpec) {
        servlets.put(clazz, pathSpec);
        return this;
    }


    /**
     * Register a functional route
     *
     * @param path       route url
     * @param clazz      route processing class
     * @param method     route processing method name
     * @param httpMethod HttpMethod Type, GET/POST/...
     * @return Blade return blade
     */
    public Blade route(String path, Class<?> clazz, String method, HttpMethod httpMethod) {
        routers.route(path, clazz, method, httpMethod);
        return this;
    }

    /**
     * Register a GET request route
     *
     * @param path    route path, request url
     * @param handler execute route Handle
     * @return return blade
     */
    public Blade get(String path, RouteHandler handler) {
        routers.route(path, handler, HttpMethod.GET);
        return this;
    }

    /**
     * Register a POST request route
     *
     * @param path    route path, request url
     * @param handler execute route Handle
     * @return return blade
     */
    public Blade post(String path, RouteHandler handler) {
        routers.route(path, handler, HttpMethod.POST);
        return this;
    }

    /**
     * Register a DELETE request route
     *
     * @param path    route path, request url
     * @param handler execute route Handle
     * @return return blade
     */
    public Blade delete(String path, RouteHandler handler) {
        routers.route(path, handler, HttpMethod.DELETE);
        return this;
    }

    /**
     * Register a PUT request route
     *
     * @param path    route path, request url
     * @param handler execute route Handle
     * @return return blade
     */
    public Blade put(String path, RouteHandler handler) {
        routers.route(path, handler, HttpMethod.PUT);
        return this;
    }

    /**
     * Register for any request routing
     *
     * @param path    route path, request url
     * @param handler execute route Handle
     * @return return blade
     */
    public Blade all(String path, RouteHandler handler) {
        routers.route(path, handler, HttpMethod.ALL);
        return this;
    }

    /**
     * Register for any request routing
     *
     * @param path    route path, request url
     * @param handler execute route Handle
     * @return return blade
     */
    public Blade any(String path, RouteHandler handler) {
        routers.route(path, handler, HttpMethod.ALL);
        return this;
    }



    /**
     * Register a pre routing request interceptor
     *
     * @param path    route path, request url
     * @param handler execute route Handle
     * @return return blade
     */
    public Blade before(String path, RouteHandler handler) {
        routers.route(path, handler, HttpMethod.BEFORE);
        return this;
    }

    /**
     * Register a after routing request interceptor
     *
     * @param path    route path, request url
     * @param handler execute route Handle
     * @return return blade
     */
    public Blade after(String path, RouteHandler handler) {
        routers.route(path, handler, HttpMethod.AFTER);
        return this;
    }

    /**
     * Setting the frame static file folder
     *
     * @param statics List of directories to filter, e.g: "/public,/static,/images"
     * @return return blade
     */
    public Blade addStatics(final String... statics) {
        bConfig.addStatic(statics);
        return this;
    }

    /**
     * add interceptor
     *
     * @param interceptor interceptor class
     * @return return blade obj
     */
    public Blade addInterceptor(Class<? extends Interceptor> interceptor) {
        routeBuilder.addInterceptor(interceptor);
        return this;
    }



    /**
     * @return Return blade config object
     */
    public BConfig bConfig() {
        return bConfig;
    }

    /**
     * @return Return blade config object
     */
    public Config config() {
        return bConfig.config();
    }

    /**
     * @return Return blade encoding, default is UTF-8
     */
    public String encoding() {
        return bConfig.getEncoding();
    }

    /**
     * @return Return blade web root path
     */
    public String webRoot() {
        return bConfig.webRoot();
    }
}

还记得之前controller注解?初始化Ioc的时候 ,Controller也是一样的不同的是需要处理route

classInfo 中为class< ?>和 className
下面的数据都是我们Blade这个核心类中的哦

 this.blade = Blade.$();//单例获取哦
 Ioc ioc = blade.ioc();
 //RouteBuilder中的routers也是blade中的哦。
 RouteBuilder routeBuilder = blade.routeBuilder();

下面的我们仅仅关注route和拦截器也是属于route的一部分哦。

 classInfos.forEach(c -> {
    Class< ?> clazz = c.getClazz();
    if (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())) {
        Service service = clazz.getAnnotation(Service.class);
        Controller controller = clazz.getAnnotation(Controller.class);
        RestController restController = clazz.getAnnotation(RestController.class);
        Component component = clazz.getAnnotation(Component.class);
        if (null != service || null != component) {
            ioc.addBean(clazz);
        } else if (null != controller || null != restController) {
            ioc.addBean(clazz);
            routeBuilder.addRouter(clazz);//处理注解中的route
        } else if (clazz.getSuperclass().getName().equals("com.blade.aop.AbstractMethodInterceptor")) {
            aopInterceptors.add(ReflectKit.newInstance(clazz));
        } else {
            Class< ?>[] interfaces = clazz.getInterfaces();
            for (Class< ?> in : interfaces) {
                if (in.equals(Interceptor.class)) {
                    ioc.addBean(clazz);
                    routeBuilder.addInterceptor(clazz);
                } else if (in.equals(WebContextListener.class)) {
                    ctxClasses.add(c);
                } else if (in.equals(BeanProcessor.class)) {
                    processoers.add(c);
                }
            }
        }
    }
});

routerbuilder中处理Class类中的方法的注解哦,这个也是这个类的意思

基于设计模式的核心原则,单一原则(一个类只负责一件事)
addRouter,将当前类的所有的方法进行扫描,然后看看是否有注解,然后,将这个注解增加到routers中去处理涩,就是这样的逻辑。

     //Parse all routing in a controller

    public void addRouter(final Class< ?> router) {

        Method[] methods = router.getMethods();//得到当前Class的所有的方法
        if (null == methods || methods.length == 0) {
            return;
        }
        //route的空间和后缀
        String nameSpace = null, suffix = null;

        if (null != router.getAnnotation(Controller.class)) {
            nameSpace = router.getAnnotation(Controller.class).value();
            suffix = router.getAnnotation(Controller.class).suffix();
        }

        if (null != router.getAnnotation(RestController.class)) {
            nameSpace = router.getAnnotation(RestController.class).value();
            suffix = router.getAnnotation(RestController.class).suffix();
        }

        if (null == nameSpace) {
            LOGGER.warn("Route [{}] not controller annotation", router.getName());
            return;
        }
        for (Method method : methods) {
            //是否含有route的注解啊
            Route mapping = method.getAnnotation(Route.class);
            //route method
            if (null != mapping) {
                // build multiple route,一个方法对应多个路由哦
                HttpMethod methodType = mapping.method();
                String[] paths = mapping.value();
                if (paths.length > 0) {
                    for (String path : paths) {
                        String pathV = getRoutePath(path, nameSpace, suffix);
                        this.buildRoute(router, method, pathV, methodType);
                    }
                }
            }
        }
    }

修正不同的书写路径的写法

private String getRoutePath(String value, String nameSpace, String suffix) {
    String path = value.startsWith("/") ? value : "/" + value;

    nameSpace = nameSpace.startsWith("/") ? nameSpace : "/" + nameSpace;
    path = nameSpace + path;

    path = path.replaceAll("[/]+", "/");

    path = path.length() > 1 && path.endsWith("/") ? path.substring(0, path.length() - 1) : path;

    path = path + suffix;

    return path;
}

Route的注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Route {

    /**
     * @return Request url
     */
    String[] value() default {"/"};

    /**
     * @return Request HttpMethod
     */
    HttpMethod method() default HttpMethod.ALL;

}

将所有的route需要耳朵class,Method,path等传递过去,,routers就是处理这个的哦,里面的Map<路径对应,一个route>,将当前的route信息扔过去就好了。

     /**
     * Build a route
     *
     * @param clazz        route target execution class
     * @param execMethod    route execution method
     * @param path            route path
     * @param method        route httpmethod
     */
    private void buildRoute(Class< ?> clazz, Method execMethod, String path, HttpMethod method) {
        routers.buildRoute(path, clazz, execMethod, method);
    }

看看routerbuilder整个类的完整的信息,这里还有个处理就是处理过滤器哦,拦截器哈哈!道理是一个样子的!

public class RouteBuilder {
    private Routers routers;

    public RouteBuilder(Routers routers) {
        this.routers = routers;
    }

    /**
     * Parse Interceptor
     *
     * @param interceptor    resolve the interceptor class
     */
    public void addInterceptor(final Class<?> interceptor) {

        boolean hasInterface = ReflectKit.hasInterface(interceptor, Interceptor.class);
        if (null == interceptor || !hasInterface) {
            return;
        }

        Intercept intercept = interceptor.getAnnotation(Intercept.class);
        String partten = "/.*";
        if (null != intercept) {
            partten = intercept.value();
        }

        try {
            Method before = interceptor.getMethod("before", Request.class, Response.class);
            Method after = interceptor.getMethod("after", Request.class, Response.class);
            buildInterceptor(partten, interceptor, before, HttpMethod.BEFORE);
            buildInterceptor(partten, interceptor, after, HttpMethod.AFTER);
        } catch (Exception e) {
            LOGGER.error("", e);
        }
    }

    /**
     * Parse all routing in a controller
     *
     * @param router    resolve the routing class
     */
    public void addRouter(final Class<?> router) {

        Method[] methods = router.getMethods();
        if (null == methods || methods.length == 0) {
            return;
        }
        String nameSpace = null, suffix = null;

        if (null != router.getAnnotation(Controller.class)) {
            nameSpace = router.getAnnotation(Controller.class).value();
            suffix = router.getAnnotation(Controller.class).suffix();
        }

        if (null != router.getAnnotation(RestController.class)) {
            nameSpace = router.getAnnotation(RestController.class).value();
            suffix = router.getAnnotation(RestController.class).suffix();
        }

        if (null == nameSpace) {
            LOGGER.warn("Route [{}] not controller annotation", router.getName());
            return;
        }
        for (Method method : methods) {
            Route mapping = method.getAnnotation(Route.class);
            //route method
            if (null != mapping) {
                // build multiple route
                HttpMethod methodType = mapping.method();
                String[] paths = mapping.value();
                if (paths.length > 0) {
                    for (String path : paths) {
                        String pathV = getRoutePath(path, nameSpace, suffix);
                        this.buildRoute(router, method, pathV, methodType);
                    }
                }
            }
        }
    }

    private String getRoutePath(String value, String nameSpace, String suffix) {
        String path = value.startsWith("/") ? value : "/" + value;

        nameSpace = nameSpace.startsWith("/") ? nameSpace : "/" + nameSpace;
        path = nameSpace + path;

        path = path.replaceAll("[/]+", "/");

        path = path.length() > 1 && path.endsWith("/") ? path.substring(0, path.length() - 1) : path;

        path = path + suffix;

        return path;
    }

    /**
     * Build a route
     *
     * @param clazz        route target execution class
     * @param execMethod    route execution method
     * @param path            route path
     * @param method        route httpmethod
     */
    private void buildRoute(Class<?> clazz, Method execMethod, String path, HttpMethod method) {
        routers.buildRoute(path, clazz, execMethod, method);
    }

    /**
     * Build a route
     *
     * @param path            route path
     * @param clazz        route target execution class
     * @param execMethod    route execution method
     * @param method        route httpmethod
     */
    private void buildInterceptor(String path, Class<?> clazz, Method execMethod, HttpMethod method) {
        routers.buildRoute(path, clazz, execMethod, method);
    }

}

Routers 这个比较好理解的直接贴代码了,其实就是Map < String,Route>

我们可以自己通过函数添加,或者自动扫描添加。可以发现,routers非常的完善,可以自己随意的定义一个处理器哦!

public class Routers {

    private Map<String, Route> routes = null;
    private Map<String, Route> interceptors = null;
    private static final String METHOD_NAME = "handle";

    public Routers() {
        this.routes = CollectionKit.newConcurrentHashMap();
        this.interceptors = CollectionKit.newConcurrentHashMap();
    }

    public Map<String, Route> getRoutes() {
        return routes;
    }

    public Map<String, Route> getInterceptors() {
        return interceptors;
    }
   //路径的信息=path#方法名称 
    public void addRoute(Route route) {
        String path = route.getPath();
        HttpMethod httpMethod = route.getHttpMethod();
        String key = path + "#" + httpMethod.toString();

        // existent
        if (null != this.routes.get(key)) {

        }

        if (httpMethod == HttpMethod.BEFORE || httpMethod == HttpMethod.AFTER) {
            if (null != this.interceptors.get(key)) {
            }
            this.interceptors.put(key, route);
            LOGGER.debug("Add Interceptor => {}", route);
        } else {
            this.routes.put(key, route);
            LOGGER.debug("Add Route => {}", route);
        }
    }

    public void addRoutes(List<Route> routes) {
        Assert.notNull(routes);
        routes.forEach(this::addRoute);
    }

    public void addRoute(HttpMethod httpMethod, String path, RouteHandler handler, String methodName) throws NoSuchMethodException {
        Class<?> handleType = handler.getClass();
        Method method = handleType.getMethod(methodName, Request.class, Response.class);
        addRoute(httpMethod, path, handler, RouteHandler.class, method);
    }

    public void addRoute(HttpMethod httpMethod, String path, Object controller, Class<?> controllerType, Method method) {

        Assert.notBlank(path);

        String key = path + "#" + httpMethod.toString();
        // existent
        if (null != this.routes.get(key)) {
        }

        Route route = new Route(httpMethod, path, controller, controllerType, method);
        if (httpMethod == HttpMethod.BEFORE || httpMethod == HttpMethod.AFTER) {
            if (null != this.interceptors.get(key)) {
            }
            this.interceptors.put(key, route);
            LOGGER.info("Add Interceptor: {}", route);
        } else {
            this.routes.put(key, route);
            LOGGER.info("Add Route => {}", route);
        }

    }

    public void route(String path, RouteHandler handler, HttpMethod httpMethod) {
        try {
            addRoute(httpMethod, path, handler, METHOD_NAME);
        } catch (NoSuchMethodException e) {
            throw new BladeException(e);
        }
    }

    public void route(String[] paths, RouteHandler handler, HttpMethod httpMethod) {
        for (String path : paths) {
            route(path, handler, httpMethod);
        }
    }

    private Map<String, Method[]> classMethosPool = CollectionKit.newConcurrentHashMap(16);
    private Map<Class<?>, Object> controllerPool = CollectionKit.newConcurrentHashMap(16);

    public void route(String path, Class<?> clazz, String methodName) {
        Assert.notNull(methodName, "Method name not is null");
        HttpMethod httpMethod = HttpMethod.ALL;
        if (methodName.contains(":")) {
            String[] methodArr = methodName.split(":");
            httpMethod = HttpMethod.valueOf(methodArr[0].toUpperCase());
            methodName = methodArr[1];
        }
        this.route(path, clazz, methodName, httpMethod);
    }

    public void route(String path, Class<?> clazz, String methodName, HttpMethod httpMethod) {
        try {
            Assert.notNull(path, "Route path not is null!");
            Assert.notNull(clazz, "Class Type not is null!");
            Assert.notNull(methodName, "Method name not is null");
            Assert.notNull(httpMethod, "Request Method not is null");

            Method[] methods = classMethosPool.get(clazz.getName());
            if (null == methods) {
                methods = clazz.getMethods();
                classMethosPool.put(clazz.getName(), methods);
            }
            if (null != methods) {
                for (Method method : methods) {
                    if (method.getName().equals(methodName)) {
                        Object controller = controllerPool.get(clazz);
                        if (null == controller) {
                            controller = ReflectKit.newInstance(clazz);
                            controllerPool.put(clazz, controller);
                        }
                        addRoute(httpMethod, path, controller, clazz, method);
                    }
                }
            }
        } catch (Exception e) {
            LOGGER.error("", e);
        }
    }

    public void buildRoute(String path, Class<?> clazz, Method method, HttpMethod httpMethod) {
        addRoute(httpMethod, path, null, clazz, method);
    }

}
public interface RouteHandler {

    void handle(Request request, Response response);

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值