标题党了,记得以前也有人反馈想把@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") 写了 /  时, 就是顶级路由了。
好了,分享完毕。
 
 
 
 
 
 
 
 
 
 
 
 
 
