通過上一篇博客,我們能夠輕松的得到制定類上的制定注解。現在,我們嘗試獲取指定類上的@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"> * @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; } }