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

效果

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


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 requestMappingMap = new HashMap<>();

    /**
     * 扫描解析指定包下的@Controller和@RequestMapping注解
     * 

* 只能扫描class文件,暂未实现扫描jar包 * * @return 生成的映射表 */ public static HashMap scanRequestMapping() { //扫描的包路径 final String pkgPath = "com/zlt/controller"; //用于字符拼接 final String pkgClassPath = pkgPath.replace("/", ".") + "."; requestMappingMap.clear(); Enumeration 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 entry : requestMappingMap.entrySet()) { RequestHandler warp = entry.getValue(); System.out.println(warp.path + " " + warp.requestMethod + " " + warp.clazz.getCanonicalName() + "." + warp.method.getName() + "()"); } } return requestMappingMap; } /** * 规范url格式 *

* 标准格式为前有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 getRequestMappingMap() { return requestMappingMap; } }

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

/**
 * Request过滤类
 * 

* 通过url、method等过滤未映射的Request * * @author Jerry * @date 2019/7/23 15:57 */ public class RequestFilter implements Filter { private HashMap 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
 * 

* 根据配置,分发请求到不同的方法 * * @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方法)