美文网首页
注解实现请求URL映射到Java方法

注解实现请求URL映射到Java方法

作者: Jerry_ojk | 来源:发表于2019-07-29 13:09 被阅读0次

效果

收到请求并且鉴权后会自动映射请求到方法上来


image.png

实现

1、定义@Controller 注解,标识类需要被扫描

/**
 * 标识类需要被扫描
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}

2、定义@RequestMapping 注解,标识类需要被扫描

/**
 * 映射url到类和方法上
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
    //url
    String value();

    //默认为GET
    RequestMethod method() default RequestMethod.DEFAULT;

    //是否需要身份验证
    boolean auth() default true;
}

3、RequestMethod 用于枚举请求的方法

/**
 * 请求方法枚举,只枚举了我们需要的方法
 */
public enum RequestMethod {
    GET, POST, DELETE, DEFAULT
}

4、RequestHandler 用于存放url映射到方法的相关信息

/**
 * 请求处理者类
 *
 * @author Jerry
 * @date 2019/7/22 11:01
 */
public class RequestHandler {
    //映射的路径
    public String path;
    //方法GET、POST等
    public RequestMethod requestMethod;
    //类对象
    public Class clazz;
    //方法对象
    public Method method;
    //类的实例对象
    public Object instance;
    //是否需要身份验证
    public boolean isAuth;
}

5、RequestMappingProcessor 用于扫描注解

/**
 * 注解扫描器
 *
 * @author Jerry
 * @date 2019/7/22 10:51
 */
public class RequestMappingProcessor {
    //存放url到RequestHandler的映射
    public static HashMap<String, RequestHandler> requestMappingMap = new HashMap<>();

    /**
     * 扫描解析指定包下的@Controller和@RequestMapping注解
     * <p>
     * 只能扫描class文件,暂未实现扫描jar包
     *
     * @return 生成的映射表
     */
    public static HashMap<String, RequestHandler> scanRequestMapping() {
        //扫描的包路径
        final String pkgPath = "com/zlt/controller";
        //用于字符拼接
        final String pkgClassPath = pkgPath.replace("/", ".") + ".";

        requestMappingMap.clear();
        Enumeration<URL> enumeration = null;
        try {
            //获取该包下的class文件
            enumeration = Thread.currentThread().getContextClassLoader().getResources(pkgPath);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (enumeration == null) {
            System.out.println("扫描失败");
            return null;
        }
        try {
            while (enumeration.hasMoreElements()) {
                URL url = enumeration.nextElement();
                File file = new File(url.getFile());
                String[] fileList = file.list();
                if (fileList == null) {
                    System.err.println(file.getAbsolutePath() + "包没有类");
                    return null;
                }
                for (String path : fileList) {
                    Class clazz = Thread.currentThread().getContextClassLoader().loadClass(pkgClassPath + path.substring(0, path.length() - 6));
                    Controller controller = (Controller) clazz.getAnnotation(Controller.class);
                    if (controller != null) {
                        System.out.println(clazz.getName());
                        RequestMapping parentRequestMapping = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
                        //类注解上的一级路径
                        String basePath = null;
                        RequestMethod baseMethod = null;
                        if (parentRequestMapping != null) {
                            //获取类注解上的一级路径
                            basePath = ensurePath(parentRequestMapping.value());
                            baseMethod = parentRequestMapping.method();
                            //忽略类注解上的auth
                            //baseAuth = parentRequestMapping.auth();

                            //默认为RequestMethod.GET
                            if (baseMethod == RequestMethod.DEFAULT) baseMethod = RequestMethod.GET;
                        }
                        //生成类的实例对象
                        Object instance = clazz.newInstance();
                        System.out.println("扫描到类 " + instance.getClass().getCanonicalName());
                        for (Method method : clazz.getDeclaredMethods()) {
                            System.out.println("    " + method.getName() + "()");
                            RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                            if (requestMapping != null) {//获取到方法上的注解
                                method.setAccessible(true);
                                //生成RequestHandler实例
                                RequestHandler warp = new RequestHandler();
                                //拼接生成最终的url
                                warp.path = basePath + ensurePath(requestMapping.value());
                                //获取方法注解上的RequestMethod
                                RequestMethod requestMethod = requestMapping.method();
                                if (requestMethod == RequestMethod.DEFAULT) {
                                    //如果类注解设置了RequestMethod,而方法注解没有设置RequestMethod,则类注解会覆盖方法注解的请求方法
                                    warp.requestMethod = baseMethod != null ? baseMethod : RequestMethod.GET;//默认为GET
                                } else {//否则使用方法注解上的RequestMethod
                                    warp.requestMethod = requestMethod;
                                }
                                warp.method = method;
                                //默认为true
                                warp.isAuth = requestMapping.auth();
                                warp.clazz = clazz;
                                warp.instance = instance;
                                //检查参数,这里还可以再检查一下返回值类型什么的
                                //这里的方法参数类型和个数都是定死了,可以拓展支持不同参数类型和返回类型
                                if (method.getParameterCount() == 2) {
                                    Class<?>[] classes = method.getParameterTypes();
                                    if (HttpServletRequest.class == classes[0] &&
                                            HttpServletResponse.class == classes[1]) {
                                        requestMappingMap.put(warp.path, warp);
                                        continue;
                                    }
                                }
                                System.err.println(method.getName() + " 参数错误");
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (requestMappingMap != null) {
            System.out.println("url映射:");
            for (Map.Entry<String, RequestHandler> entry : requestMappingMap.entrySet()) {
                RequestHandler warp = entry.getValue();
                System.out.println(warp.path + "  " + warp.requestMethod + "  " + warp.clazz.getCanonicalName() + "." + warp.method.getName() + "()");
            }
        }
        return requestMappingMap;
    }


    /**
     * 规范url格式
     * <p>
     * 标准格式为前有ur开头l“/”而后面没有 如 /user、/user/login等
     */
    private static String ensurePath(String path) {
        if (!path.startsWith("/")) {
            path = "/" + path;
        }
        if (path.endsWith("/")) {
            path = path.substring(0, path.length() - 1);
        }
        return path;
    }

    public static HashMap<String, RequestHandler> getRequestMappingMap() {
        return requestMappingMap;
    }
}

6、定义过滤器 RequestFilter ,过滤没有映射的url

/**
 * Request过滤类
 * <p>
 * 通过url、method等过滤未映射的Request
 *
 * @author Jerry
 * @date 2019/7/23 15:57
 */
public class RequestFilter implements Filter {
    private HashMap<String, RequestHandler> handlerMap = RequestMappingProcessor.getRequestMappingMap();

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = ((HttpServletRequest) servletRequest);
        String fullPath = request.getPathInfo();

        RequestHandler handler = handlerMap.get(fullPath);
        if (handler != null) {
            //把处理者添加到request的attribute中
            request.setAttribute(Config.KEY_REQUEST_HANDLER, handler);
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            //直接就不往下传递请求了,这里可以处理一下,排除一下页面 如druid
            System.out.println(request.getRequestURI() + ":没有映射");
        }
    }
}

7、定义过滤器 AuthenticationFilter ,用于请求鉴权

/**
 * 用来过滤未登录的请求
 *
 * @author Jerry
 * @date 2019/7/22 14:16
 */
public class AuthenticationFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        if (servletRequest.getAttribute(Config.KEY_REQUEST_DRUID) != null) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        RequestHandler handler = (RequestHandler) servletRequest.getAttribute(Config.KEY_REQUEST_HANDLER);
        if (handler != null) {
            if (handler.isAuth) {//如果需要验证身份
                boolean isLogin = false;
                try {
                    //这里始终为false,需要实现鉴权逻辑
                    isLogin = false;
                } catch (Exception e) {
                    e.printStackTrace();
                }
                if (isLogin) {//如果鉴权通过
                    filterChain.doFilter(servletRequest, servletResponse);
                } else {
                        //如果鉴权没通过
                }
            } else {
                filterChain.doFilter(servletRequest, servletResponse);
            }
        } else {
            //处理者为空情况
            System.out.println("AuthenticationFilter:" + ((HttpServletRequest) servletRequest).getRequestURI());
        }
    }
}

8、DispatchServlet 用于执行分发请求到对应方法上

/**
 * 负责分发请求的Servlet
 * <p>
 * 根据配置,分发请求到不同的方法
 *
 * @author Jerry
 * @date 2019/7/20 17:41
 */
public class DispatchServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        RequestHandler handler = (RequestHandler) request.getAttribute(Config.KEY_REQUEST_HANDLER);
        if (handler != null) {
            try {
                Object data = handler.method.invoke(handler.instance, request, response);
               //自己处理返回值
            } catch (Exception e) {
                response.getWriter().write(ResponseBody.JSON_ERROR_SYSTEM);
                e.printStackTrace();
            }
        } else {
            System.err.println(request.getRequestURI());
        }
    }
}

相关文章

  • 注解实现请求URL映射到Java方法

    效果 收到请求并且鉴权后会自动映射请求到方法上来 实现 1、定义@Controller 注解,标识类需要被扫描 2...

  • MVC 常用参数绑定注解

    @PathVariable注解,用来获得请求URl中的动态参数,可以将URL中的变量映射到功能处理的参数上,若方法...

  • Retrofit基本使用

    导包 参数注解 @GET(): 注解在方法上,表示get请求,括号内为请求url @POST(): 注解在方法上,...

  • 1、注解篇@RequestMapping

    @RequestMapping 请求url注解该注解有四个参数 1)、value 对应的url值,当类与方法都存在...

  • SpringMVC入门知识2

    非注解的处理器映射器SimpleUrlHandlerMapping:简单url映射可以实现对url的集中配置多个映...

  • 日期格式化

    GET方法, 取url请求参数 @Jsonformat + java.util.Date + serializat...

  • 温故知新 – Spring MVC请求映射RequestMapp

    RequestMapping注解说明 @RequestMapping注解的作用将Web请求映射到特定处理程序类和/...

  • beego注解路由

    在router.go里面,用include注册路由 在方法上加注解 请求的url http://127.0.0.1...

  • SpringMvc的小例子

    SpringMvC 1、MVC框架要做的事情:A)将一个url映射到一个java类或者java类的方法中B)封装用...

  • Retrofit注解学习

    retrofit的注解一共3种类型,分别为网络请求方法注解,标记类注解和网络请求参数注解 网络请求方法注解@GET...

网友评论

      本文标题:注解实现请求URL映射到Java方法

      本文链接:https://www.haomeiwen.com/subject/fitbrctx.html