理解并手写Spring MVC框架

一、前言

Spring框架是大多Java程序员的必修课程,而SpringMVC是里面的重头戏,它大大的简化了Servlet的繁琐操作,让开发人员得以用更多的时间去处理业务。

SpringMVC是一款经典的三层架构模式,M为Model(模型),V为View(视图),C为Controlle(控制器)。MVC是一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。

二、SpringMVC流程

流程简述:

(1)用户通过浏览器或其他途径发起请求。

(2)请求经过前端控制器DispatcherServlet,并根据请求的URI匹配到对应的处理映射器。

(3)处理映射器返回HandlerExecutionChain实体给DispatcherServlet,内部封装了拦截器HandlerInterceptor、Object类型的具体处理器handler。

(4)系统启动时,初始化了HandlerAdapter的List的集合,此步骤循环List集合,调用HandlerAdapter的support方法,找到handler支持的HandlerAdapter处理适配器。

(5)由HandlerAdapter处理参数并调用处理器的方法。

(6)返回结果是ModelAndView,即视图。其内部封装了view、model、status。

(7)将处理得到的ModelAndView返回给DispatcherServlet。

(8)将ModelAndView交由专业的视图解析器进行解析,得到页面位置。

(9)返回结果给DispatcherServlet。

(10)将数据渲染到页面。

(11)响应请求。

源码核心部分概览

protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
}
  • initMultipartResolver(context)

初始化文件上传解析功能,将普通HttpServletRequest包装成MutipartHttpServletRequest,此类可通过getFile获取到文件集合。

  • initLocaleResolver(context)

初始化与当地区域有关的设置,如视图解析器与国际化资源的配置。

  • initThemeResolver(context)

初始化主题解析,如消息国际话得配置,每个主题对应 一个properties文件。

  • initHandlerMappings(context)

初始化HandlerMappings,用来获取对应的handler与interceptor。

  • initHandlerAdapters(context)

初始化处理适配器,用来执行具体方法。

  • initHandlerExceptionResolvers(context)

对异常情况进行处理。

  • initRequestToViewNameTranslator(context)

从请求中获取视图名称。

  • initViewResolvers(context)

初始化视图解析器,将ModelAndView渲染成页面。

  • initFlashMapManager(context)

主要用处在于传递重定向参数。

三、手写Spring MVC

项目目录

源码地址:

https://github.com/hanguilin/custom-springmvc

环境配置:

maven项目中引入Servlet需要的jar包

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.0.1</version>
    <scope>provided</scope>
</dependency>

其他jar包(本人写代码用到的一些包)

        <dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.25</version>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.2.3</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-simple</artifactId>
			<version>1.7.25</version>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.21</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.4</version>
		</dependency>

注解类:

建立SpringMVC中常用注解@Controller、@RequstMapping、@Autowired、@Qualifier、@Service、@RequestParam的替代注解。区别为在自己的注解中给名字添加了Custom前缀。各个注解的区别在于注解地方,如类、方法、字段上不同。

CustomController.java

package com.hgl.mvc.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 控制层注解
 * @author guilin
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
public @interface CustomController {

	String value() default "";
}

CustomRequestMapping.java

package com.hgl.mvc.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 请求路径注解
 * @author guilin
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface CustomRequestMapping {

	String value();
}

CustomAutowired.java

package com.hgl.mvc.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自动注入
 * @author guilin
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface CustomAutowired {

	String value() default "";
}

CustomQualifer.java

package com.hgl.mvc.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 按名称自动注入
 * @author guilin
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface CustomQualifer {

	String value();
}

image.gif

CustomService.java

package com.hgl.mvc.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 业务层注解
 * @author guilin
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
public @interface CustomService {

	String value() default "";
}

CustomRequestParam.java

package com.hgl.mvc.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 参数注解
 * @author guilin
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.PARAMETER)
public @interface CustomRequestParam {

	String value() default "";
}

配置文件

在classpath下建立spring-mvc.properties,指定扫描包路径

spring.scanner.base.package=com.hgl

核心配置类

SpringMVC核心配置类DispatcherServlet.java,替代类为CustomDispatcherServlet.java

package com.hgl.mvc.servlet;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hgl.mvc.annotation.CustomController;
import com.hgl.mvc.annotation.CustomQualifer;
import com.hgl.mvc.annotation.CustomRequestMapping;
import com.hgl.mvc.annotation.CustomService;
import com.hgl.mvc.resolver.ArgumentResolver;

public class CustomDispatcherServlet extends HttpServlet{

	private static final Logger LOGGER = LoggerFactory.getLogger(CustomDispatcherServlet.class);

	private static final long serialVersionUID = 1L;

	private Properties contextConfig = new Properties();

	// 所有扫描类
	private List<String> classes = new ArrayList<String>();

	// 存放bean的容器ioc
	private Map<String, Object> context = new HashMap<String, Object>();

	// 存放参数解析器
	private Map<String, ArgumentResolver> argumentResolverMap = new HashMap<String, ArgumentResolver>();

	// 根据请求url找到具体的处理器
	private List<CustomHandlerMapping> handlerMapping = new ArrayList<CustomHandlerMapping>();

	private List<CustomHandlerAdapter> handlerAdapter = new ArrayList<CustomHandlerAdapter>();

	public CustomDispatcherServlet() {
		LOGGER.info("CustomDispatcherServlet()...");
	}

	@Override
	public void init(ServletConfig config) throws ServletException {
		// 加载配置文件
		initConfig(config.getInitParameter("spring-mvc"));
		// 扫描类
		initBaseScanPackage(contextConfig.getProperty("spring.scanner.base.package"));
		// 生成bean实例,注入ioc
		initContext();
		// 初始化参数解析器
		initArgumentResolver();
		// 为controller层中service对象注入实例
		initInstance();
		// 建立URI与处理器的映射
		initHandlerMapping();
		// 处理器适配器
		initHandlerAdapter();
	}

	private void initConfig(String initParameter) {
		InputStream in = this.getClass().getClassLoader().getResourceAsStream(initParameter);
		try {
			contextConfig.load(in);
		} catch (IOException e) {
			LOGGER.error(e.getMessage(), e);
		} finally {
			if(in != null) {
				try {
					in.close();
				} catch (IOException e) {
					LOGGER.error(e.getMessage(), e);
				}
			}
		}
	}

	private void initBaseScanPackage(String basePackage) {
		URL resource = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\.", "/"));
		String packagePath = resource.getFile();
		File packageFile = new File(packagePath);
		String[] listFiles = packageFile.list();
		for (String filepPath : listFiles) {
			File file = new File(packagePath + filepPath);
			if(file.isDirectory()) {
				initBaseScanPackage(basePackage + "." + filepPath);
			}else {
				classes.add(basePackage + "." + file.getName());
			}
		}
	}

	private void initContext() {
		if(classes.isEmpty()) {
			LOGGER.error("do scan failed.");
			return;
		}
		for (String className : classes) {
			String classPath = className.substring(0, className.lastIndexOf(".class"));
			try {
				Class<?> clazz = Class.forName(classPath);
				String simpleName = clazz.getSimpleName();
				if(clazz.isAnnotationPresent(CustomController.class)) {
					CustomController controller = clazz.getAnnotation(CustomController.class);
					String key = controller.value();
					if(StringUtils.isBlank(key)) {
						key = toLowerCaseFirstOne(simpleName);
					}
					Object instance = clazz.newInstance();
					context.put(key, instance);
				} else if(clazz.isAnnotationPresent(CustomService.class)) {
					CustomService service = clazz.getAnnotation(CustomService.class);
					String key = service.value();
					if(StringUtils.isBlank(key)) {
						key = toLowerCaseFirstOne(simpleName);
					}
					Object instance = clazz.newInstance();
					context.put(key, instance);
				} else {
					continue;
				}
			} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
				LOGGER.error(e.getMessage(), e);
			}
		}
	}

	private void initArgumentResolver() {
		if(context.isEmpty()) {
			return;
		}
		for(Map.Entry<String, Object> entry : context.entrySet()) {
			Object bean = entry.getValue();
			// 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口
			if(ArgumentResolver.class.isAssignableFrom(bean.getClass())) {
				argumentResolverMap.put(entry.getKey(), (ArgumentResolver) bean);
			}
		}
	}

	private void initInstance() {
		if(context.isEmpty()) {
			LOGGER.error("no bean is instanced.");
			return;
		}
		for(Map.Entry<String, Object> entry : context.entrySet()) {
			Object bean = entry.getValue();
			Class<? extends Object> clazz = bean.getClass();
			if(clazz.isAnnotationPresent(CustomController.class)) {
				Field[] declaredFields = clazz.getDeclaredFields();
				for (Field field : declaredFields) {
					if(field.isAnnotationPresent(CustomQualifer.class)) {
						CustomQualifer qualifer = field.getAnnotation(CustomQualifer.class);
						String beanName = qualifer.value();
						Object value = context.get(beanName);
						try {
							if(!field.isAccessible()) {
								field.setAccessible(true);
							}
							field.set(bean, value);
						} catch (IllegalArgumentException | IllegalAccessException e) {
							LOGGER.error(e.getMessage(), e);
						}
					}
				}
			}
		}
	}

	private void initHandlerMapping() {
		if(context.isEmpty()) {
			LOGGER.error("no bean is instanced.");
			return;
		}
		for(Map.Entry<String, Object> entry : context.entrySet()) {
			Object bean = entry.getValue();
			Class<? extends Object> clazz = bean.getClass();
			if(clazz.isAnnotationPresent(CustomController.class)) {
				String classRequestMappingVal = "";
				if(clazz.isAnnotationPresent(CustomRequestMapping.class)) {
					CustomRequestMapping classRequestMapping = clazz.getAnnotation(CustomRequestMapping.class);
					classRequestMappingVal = classRequestMapping.value();
				}
				Method[] declaredMethods = clazz.getDeclaredMethods();
				List<String> uris = new ArrayList<String>();
				for (Method method : declaredMethods) {
					String methodRequestMappingVal = "";
					if(method.isAnnotationPresent(CustomRequestMapping.class)) {
						CustomRequestMapping methodRequestMapping = method.getAnnotation(CustomRequestMapping.class);
						methodRequestMappingVal = classRequestMappingVal + methodRequestMapping.value();
					}
					if(StringUtils.isNotBlank(methodRequestMappingVal)) {
						if(uris.contains(methodRequestMappingVal)) {
							throw new RuntimeException("Duplicate mapping for " + methodRequestMappingVal);
						}
						handlerMapping.add(new CustomHandlerMapping(bean, method, Pattern.compile(methodRequestMappingVal)));
						uris.add(methodRequestMappingVal);
					}
				}
				uris = null;
			}
		}
	}

	private void initHandlerAdapter() {
		if(context.isEmpty()) {
			LOGGER.error("no bean is instanced.");
			return;
		}
		for(Map.Entry<String, Object> entry : context.entrySet()) {
			Object bean = entry.getValue();
			// 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口
			if(CustomHandlerAdapter.class.isAssignableFrom(bean.getClass())) {
				handlerAdapter.add((CustomHandlerAdapter) bean);
			}
		}
	}

	private String toLowerCaseFirstOne(String s){
		if(Character.isLowerCase(s.charAt(0)))
			return s;
		else
			return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
	}

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		CustomHandlerMapping handler = getHandler(request);
		CustomHandlerAdapter handlerAdapter = getHandlerAdapter(handler);
		CustomModelAndView modelAndView = handlerAdapter.handle(request, response, handler, argumentResolverMap);
	}

	private CustomHandlerAdapter getHandlerAdapter(CustomHandlerMapping handler) {
		for (CustomHandlerAdapter customHandlerAdapter : handlerAdapter) {
			if(customHandlerAdapter.support(handler)) {
				return customHandlerAdapter;
			}
		}
		throw new RuntimeException("There is no handlerAdapter for " + handler);
	}

	private CustomHandlerMapping getHandler(HttpServletRequest request) {
		String requestURI = request.getRequestURI();
		String path = requestURI.replaceAll(request.getContextPath(), "");
		for (CustomHandlerMapping handler : handlerMapping) {
			Pattern pattern = handler.getPattern();
			Matcher matcher = pattern.matcher(path);
			if(matcher.matches()) {
				return handler;
			}
		}
		throw new RuntimeException("There is no mapping for " + path);
	}

}

处理映射器

CustomHandlerMapping.java

package com.hgl.mvc.servlet;

import java.lang.reflect.Method;
import java.util.regex.Pattern;

/**
 * 自定义handlerMapping
 * 
 * @author guilin
 *
 */
public class CustomHandlerMapping{

	/**
	 * controller对应的bean
	 */
	private Object controller;

	/**
	 * 具体处理方法
	 */
	private Method method;

	/**
	 * 用来验证是否是当前url对应的处理方法
	 */
	private Pattern pattern;

	public CustomHandlerMapping(Object controller, Method method, Pattern pattern) {
		super();
		this.controller = controller;
		this.method = method;
		this.pattern = pattern;
	}

	public Object getController() {
		return controller;
	}

	public void setController(Object controller) {
		this.controller = controller;
	}

	public Method getMethod() {
		return method;
	}

	public void setMethod(Method method) {
		this.method = method;
	}

	public Pattern getPattern() {
		return pattern;
	}

	public void setPattern(Pattern pattern) {
		this.pattern = pattern;
	}
}

处理适配器

CustomHandlerAdapter.java,作为接口,由实现类实现。

package com.hgl.mvc.servlet;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.hgl.mvc.resolver.ArgumentResolver;

/**
 * @author guilin
 * 自定义适配器
 *
 */
public interface CustomHandlerAdapter {

	/**
	 * 是否支持处理
	 * 是则调用本类handle方法
	 * 
	 * @param handler 处理器
	 * @return boolean
	 */
	public boolean support(Object handler);

	/**
	 * 具体处理方法
	 * 
	 * @param request
	 * @param response
	 * @param handler
	 * @param argumentResolverMap
	 * @return CustomModelAndView
	 */
	public CustomModelAndView handle(HttpServletRequest request, HttpServletResponse response, CustomHandlerMapping handler,
			Map<String, ArgumentResolver> argumentResolverMap);

}

实现类CustomSimpleHandlerAdapter.java

package com.hgl.mvc.servlet;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hgl.mvc.annotation.CustomService;
import com.hgl.mvc.resolver.ArgumentResolver;

/**
 * 对处理适配器的实现
 * 
 * @author guilin
 *
 */
@CustomService("customSimpleHandlerAdapter")
public class CustomSimpleHandlerAdapter implements CustomHandlerAdapter{

	private static final Logger LOGGER = LoggerFactory.getLogger(CustomDispatcherServlet.class);

	@Override
	public CustomModelAndView handle(HttpServletRequest request, HttpServletResponse response, CustomHandlerMapping handler,
			Map<String, ArgumentResolver> argumentResolverMap) {
		Method method = handler.getMethod();
		Object controller = handler.getController();
		Class<?>[] parameterTypes = method.getParameterTypes();
		Object[] args = new Object[parameterTypes.length];
		for (int i = 0; i < parameterTypes.length; i++) {
			Class<?> parameterClass = parameterTypes[i];
			for (Map.Entry<String, ArgumentResolver> entry : argumentResolverMap.entrySet()) {
				ArgumentResolver argumentResolver = entry.getValue();
				if(argumentResolver.support(parameterClass, i, method)) {
					Object resolver = argumentResolver.argumentResolver(request, response, parameterClass, i, method);
					args[i] = resolver;
					break;
				}
			}
		}
		try {
			method.invoke(controller, args);
		} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			LOGGER.error(e.getMessage(), e);
		}
		return new CustomModelAndView();
	}

	@Override
	public boolean support(Object handler) {
		// 暂定实现为true
		return true;
	}

}

参数解析器

ArgumentResolver.java,作为上层接口,具体由实现类实现。

package com.hgl.mvc.resolver;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 参数解析器
 * @author guilin
 *
 */
public interface ArgumentResolver {

	public boolean support(Class<?> type, int paramIndex, Method method);

	public Object argumentResolver(HttpServletRequest request, HttpServletResponse response,
			Class<?> type, int paramIndex, Method method);
}

HttpServletRequestArgumentResolver.java,用于解析HttpServletRequest参数

package com.hgl.mvc.resolver;

import java.lang.reflect.Method;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.hgl.mvc.annotation.CustomService;

/**
 * HttpServletRequest参数解析器
 * @author guilin
 *
 */
@CustomService("httpServletRequestArgumentResolver")
public class HttpServletRequestArgumentResolver implements ArgumentResolver {

	@Override
	public boolean support(Class<?> type, int paramIndex, Method method) {
		return ServletRequest.class.isAssignableFrom(type);
	}

	@Override
	public Object argumentResolver(HttpServletRequest request, HttpServletResponse response, Class<?> type,
			int paramIndex, Method method) {
		return request;
	}

}

HttpServletResponseArgumentResolver.java,用于解析HttpServletResponse参数

package com.hgl.mvc.resolver;

import java.lang.reflect.Method;

import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.hgl.mvc.annotation.CustomService;

/**
 * HttpServletResponse参数解析器
 * @author guilin
 *
 */
@CustomService("httpServletResponseArgumentResolver")
public class HttpServletResponseArgumentResolver implements ArgumentResolver {

	@Override
	public boolean support(Class<?> type, int paramIndex, Method method) {
		return ServletResponse.class.isAssignableFrom(type);
	}

	@Override
	public Object argumentResolver(HttpServletRequest request, HttpServletResponse response, Class<?> type,
			int paramIndex, Method method) {
		return response;
	}

}

RequestParamArgumentResolver.java,用于解析CustomRequestParam定义的参数

package com.hgl.mvc.resolver;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.hgl.mvc.annotation.CustomRequestParam;
import com.hgl.mvc.annotation.CustomService;

/**
 * RequestParam参数解析器
 * @author guilin
 *
 */
@CustomService("requestParamArgumentResolver")
public class RequestParamArgumentResolver implements ArgumentResolver {

	@Override
	public boolean support(Class<?> type, int paramIndex, Method method) {
		Annotation[][] annotations = method.getParameterAnnotations();
		Annotation[] currentField = annotations[paramIndex];
		for (Annotation annotation : currentField) {
			if(CustomRequestParam.class.isAssignableFrom(annotation.getClass())) {
				return true;
			}
		}
		return false;
	}

	@Override
	public Object argumentResolver(HttpServletRequest request, HttpServletResponse response, Class<?> type,
			int paramIndex, Method method) {
		Annotation[][] annotations = method.getParameterAnnotations();
		Annotation[] currentField = annotations[paramIndex];
		for (Annotation annotation : currentField) {
			if(CustomRequestParam.class.isAssignableFrom(annotation.getClass())) {
				CustomRequestParam requestParam = (CustomRequestParam) annotation;
				String parameterName = requestParam.value();
				String parameterVal = request.getParameter(parameterName);
				return parameterVal;
			}
		}
		return null;
	}

}

原文地址:https://www.cnblogs.com/yl-space/p/13507651.html