springboot中的RequestMappingHandlerMapping、BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping的使用方法

springboot中的HandlerMapping

在上篇文章中已经说明HandlerMapping的作用是根据当前请求request获取一个包含当前请求处理器handlerHandlerExecutionChain对象。handlerHandlerApapter适配后,可以将handler转换为一个特定的对象,以此确定哪个类的哪个个方法来处理该请求。springboot中默认注册的HandlerMapping有:RequestMappingHandlerMapping,BeanNameUrlHandlerMapping,SimpleUrlHandlerMapping,RouterFunctionMapping,在这里我们只说前三种。


RequestMappingHandlerMapping

这个就是我们常见的基于注解的映射方式,例如:

@Controller
@RequestMapping("/testA")
public class MappingTest1 {
    @ResponseBody
    @RequestMapping("/index")
    public String index(){
        return "RequestMappingHandlerMapping test!";
    }
}

对于加上上面的注解后,简单解释一下我们直接可以访问localhost/testA/index的原因:在RequestMappingHandlerMapping的源码中有一段代码如下:

protected boolean isHandler(Class<?> beanType) {
	return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
		AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

springboot在初始化RequestMappingHandlerMapping时,会扫描容器中的bean,判断它上面是否存在@Controller@RequestMapping两种注解,通过上面的方法,判断该bean是否是一个handler,如果是,则会将其注册到RequestMappingHandlerMapping,用来处理和它匹配的请求。通过上面的方法我们还可以发现,@Controller注解不是必须的,我们还可以写成下面的方式:

@Component
@RequestMapping("/testA")
public class MappingTest1 {
    @ResponseBody
    @RequestMapping("/index")
    public String index(){
        return "RequestMappingHandlerMapping test!";
    }
}
SimpleUrlHandlerMapping

这种方式直接通过简单的url匹配的方式将其映射到一个处理器。首先像容器注册一个自定义的SimpleUrlHandlerMapping

@Configuration
public class MyConfig extends SimpleUrlHandlerMapping{

    @Bean
    public SimpleUrlHandlerMapping simpleUrlHandlerMapping(){

        SimpleUrlHandlerMapping simpleUrlHandlerMapping = new SimpleUrlHandlerMapping();
        Properties properties = new Properties();
        properties.setProperty("simpleUrl","mappingTest2");
        simpleUrlHandlerMapping.setMappings(properties);
		
		//设置该handlermapping的优先级为1,否则会被默认的覆盖,导致访问无效
        simpleUrlHandlerMapping.setOrder(1);
		
        return simpleUrlHandlerMapping;
    }
}

定义一个名称为mappingTest2bean,并实现org.springframework.web.servlet.mvc.Controller接口

@Component("mappingTest2")
public class MappingTest2 implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.getWriter().write("SimpleUrlHandlerMapping test!");
        return null;
    }
}

在这个例子中,我们访问localhost/simpleUrl就会直接进入容器中名称为mappingTest2beanhandleRequest方法。

BeanNameUrlHandlerMapping

这个最简单:直接以bean的名称作为访问路径,但有个硬性条件就是bean的名称必须以/开始。

@Component("/mappingTest3")
public class MappingTest3 implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.getWriter().write("BeanNameUrlHandlerMapping test!");
        return null;
    }
}

在该例子中,访问方式为localhost/mappingTest3
为什么必须以bean的名称必须以/开头呢,我们看BeanNameUrlHandlerMapping的源码可以发现,它判断是否是一个handler的依据就是该bean的名称是否以/开头。如下:

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {

	/**
	 * Checks name and aliases of the given bean for URLs, starting with "/".
	 */
	@Override
	protected String[] determineUrlsForHandler(String beanName) {
		List<String> urls = new ArrayList<>();
		if (beanName.startsWith("/")) {
			urls.add(beanName);
		}
		String[] aliases = obtainApplicationContext().getAliases(beanName);
		for (String alias : aliases) {
			if (alias.startsWith("/")) {
				urls.add(alias);
			}
		}
		return StringUtils.toStringArray(urls);
	}

}

上面的两个例子都实现了Controller接口,为什么要实现Controller接口,必须要实现Controller吗?这个后面再进行分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章