JFinal使用技巧-配置访问路由新增PathKey注解

标题党了,记得以前也有人反馈想把@ActionKey("/login") 里面的 / 去掉时,变为Controller路径的续接子级,有相对路径的感觉。波总说由于ActionKey历史问题,很多人并没有写前缀  / ,JF有自动补全 / ,如果变更规则的话会导致很麻烦的升级问题。今天看到又有人反馈这个问题,可能场景是不同,想着其实可以再加一个@PathKey ,搭配@Path也未尝不可。复用@Path也可以,但是可能需要再支持一下viewPath的值才是完善的,所以先加@PathKey吧

不多说上码:
拷贝ActionKey的代码,命名为PathKey:

import java.lang.annotation.*;

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PathKey {
    String value();
}


再绑定一下Action
继承ActionMapping类为MyActionMapping,覆写方法 buildActionMapping:加入 method.getAnnotation(PathKey.class) 

import com.jfinal.aop.Interceptor;
import com.jfinal.aop.InterceptorManager;
import com.jfinal.config.Routes;
import com.jfinal.core.*;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class MyActionMapping extends ActionMapping {
    public MyActionMapping(Routes routes) {
        super(routes);
    }

    protected void buildActionMapping() {
        mapping.clear();
        Class<?> dc;
        InterceptorManager interMan = InterceptorManager.me();
        for (Routes routes : getRoutesList()) {
            for (Routes.Route route : routes.getRouteItemList()) {
                Class<? extends Controller> controllerClass = route.getControllerClass();
                Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass);

                boolean declaredMethods = routes.getMappingSuperClass()
                        ? controllerClass.getSuperclass() == Controller.class
                        : true;

                Method[] methods = (declaredMethods ? controllerClass.getDeclaredMethods() : controllerClass.getMethods());
                for (Method method : methods) {
                    if (declaredMethods) {
                        if (!Modifier.isPublic(method.getModifiers()))
                            continue ;
                    } else {
                        dc = method.getDeclaringClass();
                        if (dc == Controller.class || dc == Object.class)
                            continue ;
                    }

                    if (method.getAnnotation(NotAction.class) != null) {
                        continue ;
                    }

                    Interceptor[] actionInters = interMan.buildControllerActionInterceptor(routes.getInterceptors(), controllerInters, controllerClass, method);
                    String controllerPath = route.getControllerPath();

                    String methodName = method.getName();
                    ActionKey ak = method.getAnnotation(ActionKey.class);
                    PathKey pk = method.getAnnotation(PathKey.class);
                    String actionKey;
                    if (ak != null) {
                        actionKey = ak.value().trim();
                        if ("".equals(actionKey))
                            throw new IllegalArgumentException(controllerClass.getName() + "." + methodName + "(): The argument of ActionKey can not be blank.");

                        if (!actionKey.startsWith(SLASH))
                            actionKey = SLASH + actionKey;
                    }
                    else if (pk != null){
                        actionKey = pk.value().trim();
                        if ("".equals(actionKey))
                            throw new IllegalArgumentException(controllerClass.getName() + "." + methodName + "(): The argument of ActionKey can not be blank.");

                        if (!actionKey.startsWith(SLASH))
                            actionKey = controllerPath + SLASH + actionKey;
                    }
                    else if (methodName.equals("index")) {
                        actionKey = controllerPath;
                    }
                    else {
                        actionKey = controllerPath.equals(SLASH) ? SLASH + methodName : controllerPath + SLASH + methodName;
                    }

                    Action action = new Action(controllerPath, actionKey, controllerClass, method, methodName, actionInters, route.getFinalViewPath(routes.getBaseViewPath()));
                    if (mapping.put(actionKey, action) != null) {
                        throw new RuntimeException(buildMsg(actionKey, controllerClass, method));
                    }
                }
            }
        }
        routes.clear();

        // support url = controllerPath + urlParas with "/" of controllerPath
        Action action = mapping.get("/");
        if (action != null) {
            mapping.put("", action);
        }
    }
}


JFinalConfig 子类配置的时候,需要设置一下:

@Override
public void configConstant(Constants me) {
    me.setActionMapping(routes -> new MyActionMapping(routes));
}


好了,开始测试:

import com.jfinal.core.Controller;
import com.jfinal.core.Path;

@Path("/path/key")
public class PathKeyController extends Controller {

    public void index(){
        renderText("INDEX");
    }

    public void a(){
        renderText("A");
    }

    @PathKey("/action/b")
    public void b(){
        renderText("B");
    }

    @PathKey("test/c")
    public void c(){
        renderText("C");
    }

}

启动时打印一下看下:

@Override
public void onStart() {
    System.out.println(JFinal.me().getAllActionKeys());
}

打印结果:

[/action/b, /path/key, /path/key/a, /path/key/test/c]

说明成功:

@PathKey("test/c") 不写 /  时会继承 @Path("/path/key") 的参数,拼接为:/path/key/test/c
@PathKey("/action/b") 写了 /  时, 就是顶级路由了。

好了,分享完毕。

评论区

Leo.du

2022-08-28 11:54

感谢分享

Leo.du

2022-08-28 12:27

结尾加上一段输出日志
if (JFinal.me().getConstants().getDevMode()) {
Map sortedMapping = new LinkedHashMap<>();
mapping.entrySet().stream().sorted(Map.Entry.comparingByKey())
.forEachOrdered(x -> sortedMapping.put(x.getKey(), x.getValue()));
sortedMapping.forEach((k, v) -> {
log.debug(k + " --> " + v.getControllerClass().getName() + ":" + v.getMethodName());
});
}

zzutligang

2022-08-28 17:01

不错,不错,借鉴了!

JFinal

2022-08-29 00:36

点赞收藏一波

北流家园网

2022-08-29 08:23

收藏,谢谢大神分享

山东小木

2022-08-29 09:12

我是自定义actionMapping直接修改了原来的实现 如果没有前缀/ 就controllerpath
+/+actionKey 设置规范更简单了 actionkey有前缀就绝对路径 没有就是相对路径
image.png

山东小木

2022-08-29 09:13

你这个自定义的 可以兼容一些早期系统写的不规范的情景

SuperEric

2022-09-01 18:59

感谢分享,这个需求其实还是蛮正常的,尤其是对url有执着要求的时候。

happyboy

2022-09-02 09:46

我虽然也升级到最新版了,但是路由用的还是最开始的手动注册,O(∩_∩)O哈哈~

星矢

2022-09-08 09:02

@happyboy 俺也一样