获取指定类上的@RequestMapping注解的请求信息

Posted longfei2012

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了获取指定类上的@RequestMapping注解的请求信息相关的知识,希望对你有一定的参考价值。

通过上一篇博客,我们能够轻松的得到制定类上的制定注解。现在,我们尝试获取指定类上的@RequestMapping注解,并获取该控制层的全部请求信息。在这里,提供一个实体类,用于存放请求的部分信息。

public class RequestUrlInfo implements Comparable<RequestUrlInfo>{

    private String name;                  //mapping的名称
    private String value;                 //mapping的请求路径
    private RequestMethod requestMethod;  //响应请求的方法
    
    public RequestUrlInfo() {
        
    }

    public RequestUrlInfo(String name) {
        super();
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public RequestMethod getRequestMethod() {
        return requestMethod;
    }

    public void setRequestMethod(RequestMethod requestMethod) {
        this.requestMethod = requestMethod;
    }

    @Override
    public String toString() {
        return "RequestUrlInfo [name=" + name + ", value=" + value
                + ", requestMethod=" + requestMethod + "]" + "\n";
    }

    /**
     * 根据请求路径对请求信息进行区分。
     */
    @Override
    public int compareTo(RequestUrlInfo o) {
        return this.value.compareTo(o.getValue());
    }
    
}

在这里,为了后续的排序操作,实现Comparable接口,并根据请求路径进行排序依据。

接下来,就是获取指定类的RequestMapping注解信息。通过以下代码,能够轻松的获取类级别以及方法级别的注解。

Annotation classAnnotation = AnnotationHelper.getInstance().getClassAnnotation(scannerClass, RequestMapping.class);
List<Annotation> methodAnnotations = AnnotationHelper.getInstance().getMethodAnnotation(scannerClass, RequestMapping.class);

现在,注解已经得到,需要通过得到的注解,获取该注解相关的信息。要获取RequestMapping注解的信息,就需要了解该注解的相关信息。

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {

    /**
     * Assign a name to this mapping.
     * <p><b>Supported at the type level as well as at the method level!</b>
     * When used on both levels, a combined name is derived by concatenation
     * with "#" as separator.
     * @see org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder
     * @see org.springframework.web.servlet.handler.HandlerMethodMappingNamingStrategy
     */
    String name() default "";

    /**
     * The primary mapping expressed by this annotation.
     * <p>In a Servlet environment this is an alias for {@link #path}.
     * For example {@code @RequestMapping("/foo")} is equivalent to
     * {@code @RequestMapping(path="/foo")}.
     * <p>In a Portlet environment this is the mapped portlet modes
     * (i.e. "EDIT", "VIEW", "HELP" or any custom modes).
     * <p><b>Supported at the type level as well as at the method level!</b>
     * When used at the type level, all method-level mappings inherit
     * this primary mapping, narrowing it for a specific handler method.
     */
    @AliasFor("path")
    String[] value() default {};

    /**
     * In a Servlet environment only: the path mapping URIs (e.g. "/myPath.do").
     * Ant-style path patterns are also supported (e.g. "/myPath/*.do").
     * At the method level, relative paths (e.g. "edit.do") are supported within
     * the primary mapping expressed at the type level. Path mapping URIs may
     * contain placeholders (e.g. "/${connect}")
     * <p><b>Supported at the type level as well as at the method level!</b>
     * When used at the type level, all method-level mappings inherit
     * this primary mapping, narrowing it for a specific handler method.
     * @see org.springframework.web.bind.annotation.ValueConstants#DEFAULT_NONE
     * @since 4.2
     */
    @AliasFor("value")
    String[] path() default {};

    /**
     * The HTTP request methods to map to, narrowing the primary mapping:
     * GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.
     * <p><b>Supported at the type level as well as at the method level!</b>
     * When used at the type level, all method-level mappings inherit
     * this HTTP method restriction (i.e. the type-level restriction
     * gets checked before the handler method is even resolved).
     * <p>Supported for Servlet environments as well as Portlet 2.0 environments.
     */
    RequestMethod[] method() default {};

    /**
     * The parameters of the mapped request, narrowing the primary mapping.
     * <p>Same format for any environment: a sequence of "myParam=myValue" style
     * expressions, with a request only mapped if each such parameter is found
     * to have the given value. Expressions can be negated by using the "!=" operator,
     * as in "myParam!=myValue". "myParam" style expressions are also supported,
     * with such parameters having to be present in the request (allowed to have
     * any value). Finally, "!myParam" style expressions indicate that the
     * specified parameter is <i>not</i> supposed to be present in the request.
     * <p><b>Supported at the type level as well as at the method level!</b>
     * When used at the type level, all method-level mappings inherit
     * this parameter restriction (i.e. the type-level restriction
     * gets checked before the handler method is even resolved).
     * <p>In a Servlet environment, parameter mappings are considered as restrictions
     * that are enforced at the type level. The primary path mapping (i.e. the
     * specified URI value) still has to uniquely identify the target handler, with
     * parameter mappings simply expressing preconditions for invoking the handler.
     * <p>In a Portlet environment, parameters are taken into account as mapping
     * differentiators, i.e. the primary portlet mode mapping plus the parameter
     * conditions uniquely identify the target handler. Different handlers may be
     * mapped onto the same portlet mode, as long as their parameter mappings differ.
     */
    String[] params() default {};

    /**
     * The headers of the mapped request, narrowing the primary mapping.
     * <p>Same format for any environment: a sequence of "My-Header=myValue" style
     * expressions, with a request only mapped if each such header is found
     * to have the given value. Expressions can be negated by using the "!=" operator,
     * as in "My-Header!=myValue". "My-Header" style expressions are also supported,
     * with such headers having to be present in the request (allowed to have
     * any value). Finally, "!My-Header" style expressions indicate that the
     * specified header is <i>not</i> supposed to be present in the request.
     * <p>Also supports media type wildcards (*), for headers such as Accept
     * and Content-Type. For instance,
     * <pre class="code">
     * &#064;RequestMapping(value = "/something", headers = "content-type=text/*")
     * </pre>
     * will match requests with a Content-Type of "text/html", "text/plain", etc.
     * <p><b>Supported at the type level as well as at the method level!</b>
     * When used at the type level, all method-level mappings inherit
     * this header restriction (i.e. the type-level restriction
     * gets checked before the handler method is even resolved).
     * <p>Maps against HttpServletRequest headers in a Servlet environment,
     * and against PortletRequest properties in a Portlet 2.0 environment.
     * @see org.springframework.http.MediaType
     */
    String[] headers() default {};

    /**
     * The consumable media types of the mapped request, narrowing the primary mapping.
     * <p>The format is a single media type or a sequence of media types,
     * with a request only mapped if the {@code Content-Type} matches one of these media types.
     * Examples:
     * <pre class="code">
     * consumes = "text/plain"
     * consumes = {"text/plain", "application/*"}
     * </pre>
     * Expressions can be negated by using the "!" operator, as in "!text/plain", which matches
     * all requests with a {@code Content-Type} other than "text/plain".
     * <p><b>Supported at the type level as well as at the method level!</b>
     * When used at the type level, all method-level mappings override
     * this consumes restriction.
     * @see org.springframework.http.MediaType
     * @see javax.servlet.http.HttpServletRequest#getContentType()
     */
    String[] consumes() default {};

    /**
     * The producible media types of the mapped request, narrowing the primary mapping.
     * <p>The format is a single media type or a sequence of media types,
     * with a request only mapped if the {@code Accept} matches one of these media types.
     * Examples:
     * <pre class="code">
     * produces = "text/plain"
     * produces = {"text/plain", "application/*"}
     * produces = "application/json; charset=UTF-8"
     * </pre>
     * <p>It affects the actual content type written, for example to produce a JSON response
     * with UTF-8 encoding, {@code "application/json; charset=UTF-8"} should be used.
     * <p>Expressions can be negated by using the "!" operator, as in "!text/plain", which matches
     * all requests with a {@code Accept} other than "text/plain".
     * <p><b>Supported at the type level as well as at the method level!</b>
     * When used at the type level, all method-level mappings override
     * this produces restriction.
     * @see org.springframework.http.MediaType
     */
    String[] produces() default {};

}

现在,我们知道RequestMapping注解的name、value以及method的返回值,分别是String、String[]、RequestMethod[]。接下来,就是获取将注解的内容填充进实体类中。

private List<RequestUrlInfo> getRequestUrlInfos(Annotation annotation) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        List<RequestUrlInfo> infos = new ArrayList<RequestUrlInfo>();
        
        if(annotation == null) {
            return infos;
        }
        
        String name = (String) AnnotationHelper.getInstance().getAnnotationInfo(annotation, "name");
        List<String> requestUrls = Arrays.asList((String[]) AnnotationHelper.getInstance().getAnnotationInfo(annotation, "value"));
        List<RequestMethod> requestMethods = Arrays.asList((RequestMethod[]) AnnotationHelper.getInstance().getAnnotationInfo(annotation, "method"));
        
        if(requestMethods.isEmpty()) {
            for(String url : requestUrls) {
                RequestUrlInfo info = new RequestUrlInfo(name);
                
                info.setValue(url);
                info.setRequestMethod(null);
                
                infos.add(info);
            }
        } else {
            for(String url : requestUrls) {
                for(RequestMethod method : requestMethods) {
                    RequestUrlInfo info = new RequestUrlInfo(name);
                    
                    info.setValue(url);
                    info.setRequestMethod(method);
                    
                    infos.add(info);
                }
            }
        }

剩下的就是,将类级别的注解与方法级别的注解,组装成一个整体的实体类。

private List<RequestUrlInfo> getRequestUrlInfos(Annotation classAnnotation , Annotation methodAnnotation) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        
        List<RequestUrlInfo> infos = new ArrayList<RequestUrlInfo>();
        
        if(classAnnotation != null) {
            String[] paths = (String[]) AnnotationHelper.getInstance().getAnnotationInfo(classAnnotation, "value");
            
            for(String path : paths) {
                infos.addAll(getRequestUrlInfos(path, methodAnnotation));
            }
        } else {
            infos.addAll(getRequestUrlInfos(methodAnnotation));
        }
        
        return infos;
    }

下面,就是这个类的完整代码:

public class RequestUrlHelper {

    private static final RequestUrlHelper helper = new RequestUrlHelper();
    
    protected RequestUrlHelper() {
        
    }
    
    public static RequestUrlHelper getInstance() {
        return helper;
    }
    
    /**
     * 将RequestMapping注解信息,组装成RequestUrlInfo类中。此类方法共有三种重载方式,分别为Annotation、提供basePath、提供classAnnotation注解三种方式。
     * @param annotation
     * @return
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    private List<RequestUrlInfo> getRequestUrlInfos(Annotation annotation) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        List<RequestUrlInfo> infos = new ArrayList<RequestUrlInfo>();
        
        if(annotation == null) {
            return infos;
        }
        
        String name = (String) AnnotationHelper.getInstance().getAnnotationInfo(annotation, "name");
        List<String> requestUrls = Arrays.asList((String[]) AnnotationHelper.getInstance().getAnnotationInfo(annotation, "value"));
        List<RequestMethod> requestMethods = Arrays.asList((RequestMethod[]) AnnotationHelper.getInstance().getAnnotationInfo(annotation, "method"));
        
        if(requestMethods.isEmpty()) {
            for(String url : requestUrls) {
                RequestUrlInfo info = new RequestUrlInfo(name);
                
                info.setValue(url);
                info.setRequestMethod(null);
                
                infos.add(info);
            }
        } else {
            for(String url : requestUrls) {
                for(RequestMethod method : requestMethods) {
                    RequestUrlInfo info = new RequestUrlInfo(name);
                    
                    info.setValue(url);
                    info.setRequestMethod(method);
                    
                    infos.add(info);
                }
            }
        }

        return infos;
    }
    
    /**
     * 上一个方法的重载方法,主要是将类上面的注解中的路径添加到全部的请求信息中。
     * @param basePath
     * @param annotation
     * @return
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    private List<RequestUrlInfo> getRequestUrlInfos(String basePath , Annotation annotation) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        List<RequestUrlInfo> infos = getRequestUrlInfos(annotation);
        
        if(!StringUtils.hasText(basePath)) {
            return infos;
        }
        
        for(RequestUrlInfo info : infos) {
            info.setValue(basePath.concat("/" + info.getValue()));
        }
        
        return infos;
    }
    
    /**
     * 上一个方法的重载方法,这次,没有提供basePath,而是提供一个Annotation,并从这个注解上面获取得到类上面注解的请求路径。
     * @param classAnnotation
     * @param methodAnnotation
     * @return
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    private List<RequestUrlInfo> getRequestUrlInfos(Annotation classAnnotation , Annotation methodAnnotation) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        
        List<RequestUrlInfo> infos = new ArrayList<RequestUrlInfo>();
        
        if(classAnnotation != null) {
            String[] paths = (String[]) AnnotationHelper.getInstance().getAnnotationInfo(classAnnotation, "value");
            
            for(String path : paths) {
                infos.addAll(getRequestUrlInfos(path, methodAnnotation));
            }
        } else {
            infos.addAll(getRequestUrlInfos(methodAnnotation));
        }
        
        return infos;
    }
    
    /**
     * 获取全部的请求,将其包装成RequestUrlInfo实体类,并将其通过请求路径排序后,进行返回。
     * @param scannerClass
     * @return
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    public List<RequestUrlInfo> getAllRequestUrlInfos(Class<?> scannerClass) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Annotation classAnnotation = AnnotationHelper.getInstance().getClassAnnotation(scannerClass, RequestMapping.class);
        List<Annotation> methodAnnotations = AnnotationHelper.getInstance().getMethodAnnotation(scannerClass, RequestMapping.class);
        List<RequestUrlInfo> infos = new ArrayList<RequestUrlInfo>();
        
        for(Annotation methodAnnotation : methodAnnotations) {
            infos.addAll(getRequestUrlInfos(classAnnotation, methodAnnotation));
        }
        
        Collections.reverse(infos);
        
        return infos;
    }
}

 

以上是关于获取指定类上的@RequestMapping注解的请求信息的主要内容,如果未能解决你的问题,请参考以下文章

2.注解式开发

2.注解式开发

2.注解式开发

三SpringMVC注解—@RequestMapping与@RequestParam

SpringMVC注解解析

SpringMVC @RequestMapping注解