如何用几条代码获取项目所有的url?

时间:2024-05-31 19:20:36

当项目很大,又需要给URL做权限控制的时候,一个一个去Controller找,一条一条手动输入是有多麻烦可想而知,本篇文章旨在介绍如何用代码的形式获取项目中所有的url。

直接上代码

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.context.WebApplicationContext;

import org.springframework.web.method.HandlerMethod;

import org.springframework.web.servlet.DispatcherServlet;

import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;

import org.springframework.web.servlet.mvc.method.RequestMappingInfo;

import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;

import java.util.HashSet;

import java.util.Map;

import java.util.Set;

@Controller

public class GetAllUrl {

    @RequestMapping("/getAllUrl")

    @ResponseBody

    public Set<String> getAllUrl(HttpServletRequest request) {

        Set<String> result = new HashSet<String>();

        WebApplicationContext wc = (WebApplicationContext) request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);

        RequestMappingHandlerMapping bean = wc.getBean(RequestMappingHandlerMapping.class);

        Map<RequestMappingInfo, HandlerMethod> handlerMethods = bean.getHandlerMethods();

        for (RequestMappingInfo rmi : handlerMethods.keySet()) {

            PatternsRequestCondition pc = rmi.getPatternsCondition();

            Set<String> pSet = pc.getPatterns();

            result.addAll(pSet);

        }

        return result;

    }

}

在浏览器中输入请求的url:http://localhost:8080/getAllUrl 可以看见结果,这里直接返回的是String,可以用JSON在线格式转换工具转换,可以看见下图结果(这里只有一个url)

如何用几条代码获取项目所有的url?

小雷我获取项目的全部URL是因为小雷的好奇心!

下面讲解一下原理。

代码的核心:

WebApplicationContext wc = (WebApplicationContext)request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);  

RequestMappingHandlerMapping bean = wc.getBean(RequestMappingHandlerMapping.class);  

Map<RequestMappingInfo, HandlerMethod> handlerMethods = bean.getHandlerMethods();   

1、WebApplicationContext

WebApplicationContext是专门为web应用准备的,他允许从相对于web根目录的路径中装载配置文件完成初始化工作,从WebApplicationContext中可以获得ServletContext的引用,整个Web应用上下文对象将作为属性放置在ServletContext中,以便web应用可以访问spring上下文。

这里通过request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE)获取的WebApplicationContext。

DispatcherServlet

Spring中的DispatcherServlet除了作为处理request的前端控制器,还负责与Spring IOC容器整合并提供Spring的功能。

DispatcherServlet的工作流程

如何用几条代码获取项目所有的url?

在Spring MVC中,每个DispatcherServlet拥有其独立的WebApplicationContext,继承了root/global WebApplicationContext中所定义的所有的bean。这些继承的bean可以在DispatcherServlet中被重写;DispatcherServlet也可以定义自己特有的benas。

可以在web.xml中为DispatcherServlet指定一个对应的配置文件,当然也可以选择不指定,那么Servlet的WebApplicationContext将没有特定的benas。

2、RequestMappingHandlerMapping

从WebApplicationContext中获取RequestMappingHandlerMapping.class类型的所有bean。

源码分析:

RequestMappingHandlerMapping主要有两个功能注册和查找

注册指的是注册Interceptor和controller

查找是根据request查找与之对应的controller和Interceptor

我们知道初始化DispatcherServlet的时候首先要初始化HandlerMapping,这个时候就会初始化spring容器中注册的HandlerMapping就是RequestMappingHandlerMapping

RequestMappingHandlerMapping的初始化主要分成两个部分

初始化Interceptor和controller

初始化的工作在AbstractHandlerMapping类中完成代码如下

protected void initApplicationContext() throws BeansException {

extendInterceptors(this.interceptors);

detectMappedInterceptors(this.mappedInterceptors);

initInterceptors();

}

完成了Interceptor的初始化后,就是controller的初始化controller的初始化在AbstractHandlerMethodMapping中进行代码如下

protected void initHandlerMethods() {

if (logger.isDebugEnabled()) {

logger.debug("Looking for request mappings in application context: " + getApplicationContext());

}

String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?

BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :

getApplicationContext().getBeanNamesForType(Object.class));

 

for (String beanName : beanNames) {

if (isHandler(getApplicationContext().getType(beanName))){

detectHandlerMethods(beanName);

}

}

handlerMethodsInitialized(getHandlerMethods());

}

 

protected boolean isHandler(Class<?> beanType) {

return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||

(AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));

}

上面的代码中首先是找到spring容器中注册了的所有类的beamName然后遍历这些类,检查它们是否是controller如果是则调用detectHandlerMethods方法代码如下

protected void detectHandlerMethods(final Object handler) {

Class<?> handlerType = (handler instanceof String) ?

getApplicationContext().getType((String) handler) : handler.getClass();

 

final Class<?> userType = ClassUtils.getUserClass(handlerType);

 

Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {

public boolean matches(Method method) {

return getMappingForMethod(method, userType) != null;

}

});

 

for (Method method : methods) {

T mapping = getMappingForMethod(method, userType);

registerHandlerMethod(handler, method, mapping);

}

}

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {

RequestMappingInfo info = null;

RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);

if (methodAnnotation != null) {

RequestCondition<?> methodCondition = getCustomMethodCondition(method);

info = createRequestMappingInfo(methodAnnotation, methodCondition);

RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);

if (typeAnnotation != null) {

RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);

info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);

}

}

return info;

}

在getMappingForMethod方法里面,找到方法里面的RequestMapping注解,然后根据注解生成RequestMappingInfo完成上面的步骤之后,就会将找到的controller注册 代码如下

protected void registerHandlerMethod(Object handler, Method method, T mapping) {

HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);

HandlerMethod oldHandlerMethod = handlerMethods.get(mapping);

if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {

throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean()

+ "' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '"

+ oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");

}

 

this.handlerMethods.put(mapping, newHandlerMethod);

if (logger.isInfoEnabled()) {

logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);

}

 

Set<String> patterns = getMappingPathPatterns(mapping);

for (String pattern : patterns) {

if (!getPathMatcher().isPattern(pattern)) {

this.urlMap.add(pattern, mapping);

}

}

}

这里面有两个map一个是urlMap,一个是handlerMethods通过url我们可以找到与之对应的mapping,而通过mapping找到url对应的HandlerMethod 也就是要执行的controller和方法。