SpringMVC源码(七)Controller控制器5-MultiActionController

柳德义
2023-12-01
MultiActionController 多动作控制器 :

前面的控制器一般都是对应一个处理方法,如果对数据进行CRUD操作,那么就需要四个控制器,当我们随着方法的增加,控制器的数量便会大幅度增加.增加了项目的臃肿程度,是不可取的.为了解决这个难题,springmvc 的MultiActionController控制器便闪亮登场.MultiActionController是用于处理多个HTTP请求的处理器,支持在一个控制器里面定义多个功能处理方法.

同时MultiActionController 也提供了两个重要属性:

delegate  :功能处理的委托对象,即我们要调用请求处理方法所在对象。

methodNameResolver:功能处理方法名解析器,根据请求的url信息来解析它将调用的方法,需要在配置文件中定义此解析器 。

MultiActionController 是如何做到不同的url请求都能得到正确的处理方法呢?

Spring Web MVC提供的MethodNameResolver(方法名解析器) 属性 ,它会根据HTTP请求URL映射得到应该调用的处理器方法.通过反射调用处理器方法,并且封装返回结果作为模型和视图返回给简单控制器适配器。每个处理器方法可以有一个对应的最后修改方法,最后修改方法名是处理器方法名加上LastModified后缀构成的。最后修改方法也是通过反射调用并且返回结果的。

methodNameResolver提供的三种方式:
org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver
org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver
org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver
1.InternalPathMethodNameResolver
MultiActionController的默认实现,提供从请求URL路径解析功能方法的方法名,从请求的最后一个路径(/)开始,并忽略扩展名;如请求URL是“/user/list.html”,则解析的功能处理方法名为“list”,即调用list方法。该解析器还可以指定前缀和后缀,通过prefix和suffix属性,如指定prefix=”test_”,则功能方法名将变为test_list;
2. PropertiesMethodNameResolver
提供自定义的从请求URL解析功能方法的方法名,使用一组用户自定义的模式到功能方法名的映射,映射使用Properties对象存放,
3.ParameterMethodNameResolver(推荐使用)
提供从请求参数解析功能处理方法的方法名。

下面是MultiActionController的核心代码:

	//判断方法是否是功能处理方法  
private boolean isHandlerMethod(Method method) {  
    //得到方法返回值类型  
    Class returnType = method.getReturnType();  
    //返回值类型必须是ModelAndView、Map、String、void中的一种,否则不是功能处理方法  
    if (ModelAndView.class.equals(returnType) || Map.class.equals(returnType) || String.class.equals(returnType) ||  
            void.class.equals(returnType)) {  
        Class[] parameterTypes = method.getParameterTypes();  
        //功能处理方法参数个数必须>=2,且第一个是HttpServletRequest类型、第二个是HttpServletResponse  
        //且不能Controller接口的handleRequest(HttpServletRequest request, HttpServletResponse response),这个方法是由系统调用  
        return (parameterTypes.length >= 2 &&  
                HttpServletRequest.class.equals(parameterTypes[0]) &&  
                HttpServletResponse.class.equals(parameterTypes[1]) &&  
                !("handleRequest".equals(method.getName()) && parameterTypes.length == 2));  
    }  
    return false;  
}  

实现方法:

protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		try {
			String methodName = this.methodNameResolver.getHandlerMethodName(request);
			return invokeNamedMethod(methodName, request, response);
		}
		catch (NoSuchRequestHandlingMethodException ex) {
			return handleNoSuchRequestHandlingMethod(ex, request, response);
		}
	} 

通过上面的代码可以很清楚的看到利用反射获取到了方法的名称,然后再去调用这个方法.如果没有这个方法的存在,那么就会抛出handleNoSuchRequestHandlingMethod异常.

invokeNamedMethod的具体方法:

protected final ModelAndView invokeNamedMethod(
			String methodName, HttpServletRequest request, HttpServletResponse response) throws Exception {

		Method method = this.handlerMethodMap.get(methodName);
		if (method == null) {
			throw new NoSuchRequestHandlingMethodException(methodName, getClass());
		}

		try {
			Class<?>[] paramTypes = method.getParameterTypes();
			List<Object> params = new ArrayList<Object>(4);
			params.add(request);
			params.add(response);

			if (paramTypes.length >= 3 && paramTypes[2].equals(HttpSession.class)) {
				HttpSession session = request.getSession(false);
				if (session == null) {
					throw new HttpSessionRequiredException(
							"Pre-existing session required for handler method '" + methodName + "'");
				}
				params.add(session);
			}

			// If last parameter isn't of HttpSession type, it's a command.
			if (paramTypes.length >= 3 &&
					!paramTypes[paramTypes.length - 1].equals(HttpSession.class)) {
				Object command = newCommandObject(paramTypes[paramTypes.length - 1]);
				params.add(command);
				bind(request, command);
			}

			Object returnValue = method.invoke(this.delegate, params.toArray(new Object[params.size()]));
			return massageReturnValueIfNecessary(returnValue);
		}
		catch (InvocationTargetException ex) {
			// The handler method threw an exception.
			return handleException(request, response, ex.getTargetException());
		}
		catch (Exception ex) {
			// The binding process threw an exception.
			return handleException(request, response, ex);
		}
	}

详细请看:

点击打开链接

 类似资料: