美文网首页
注解实现请求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方法

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