上一篇文章中详细讲解了Spring MVC的运行流程源码(点击传送),现在我们自己动手来模仿写一个,主要是为了加深理解。
一、效果预览
1、配置前端控制器,指定配置文件路径
2、配置Controller扫描包和静态资源
3、使用自定义注解声明控制器和访问路径、请求方式
4、成功时
5、 请求路径不存在或者参数不正确时
二、整体流程和项目结构
1、请求处理流程
首先不管是什么方式的请求,都会交由前端控制器的doDispatch方法处理。先是根据请求路径找到对应的处理器,然后根据处理器获取对应的适配器,最后使用适配器去调用真正的目标(即我们写的控制器或者静态资源)。
2、项目结构
项目使用个人惯用的Maven多模块结构:一个核心模块,一个测试模块
先看父工程的pom.xml,只摘取了比较重要的两部分。第一部分是两个模块都要依赖的javax.servlet-api;第二部分是引入了编译插件,需要关注的地方是-parameters,这是为了编译时保留方法参数的名称,Java8之后提供的功能(值得注意的是,需要自己手动开启,参考文章https://blog.csdn.net/qq_31142553/article/details/85637154)。
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<modules>
<module>zmvc-core</module>
<module>zmvc-test</module>
</modules>
<build>
<finalName>zmvc</finalName>
<plugins>
<!-- compiler plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
然后是zmvc-core模块的pom.xml配置,增加了spring-core的依赖,这是为了使用它的路径匹配工具AntPathMatcher。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.18.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
最后是zmvc-core模块的pom.xml配置,测试时再说。
3、核心模块目录结构
三、前端控制器的初始化
我们需要在ZDispatcherServlet的init方法里面加载配置文件、加载处理器映射器、加载处理器适配器。
/**
* 处理器适配器
*/
private List<HandlerAdapter> handlerAdapters = new ArrayList<>();
public void init(ServletConfig config) throws ServletException {
// 1、加载配置文件
loadConfig(config.getInitParameter("contextConfigLocation"));
// 2、初始化处理器映射器
initHandlerMappings();
// 3、初始化处理器适配器
initHandlerAdapters();
}
1、加载配置文件:将配置文件的属性设置到ZDispatcherServlet的成员变量Properties里面
/**
* 加载配置
* @param confLocation
* @throws ServletException
*/
private void loadConfig(String confLocation) throws ServletException {
// 把web.xml中的contextConfigLocation对应value值的文件加载到流里面
if (confLocation.startsWith("classpath:")) {
confLocation = confLocation.replace("classpath:", "");
} else if (confLocation.contains("/")) {
int lastSplitIndex = confLocation.lastIndexOf('/');
confLocation = confLocation.substring(lastSplitIndex + 1, confLocation.length());
}
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(confLocation);
try {
//用Properties文件加载文件里的内容
properties.load(resourceAsStream);
} catch (IOException e) {
throw new ServletException("加载配置文件异常!", e);
} finally {
//关流
if(null != resourceAsStream){
try {
resourceAsStream.close();
} catch (IOException e) {
throw new ServletException("加载配置文件异常!", e);
}
}
}
}
2、加载处理器映射器
/**
* 处理器映射器
*/
private List<HandlerMapping> handlerMappings = new ArrayList<>();
/**
* 初始化处理器映射器
*/
private void initHandlerMappings() {
// 1、初始化URL处理器映射器
HandlerMapping urlHandlerMapping = UrlHandlerMapping.init(properties.getProperty("staticResources"));
handlerMappings.add(urlHandlerMapping);
// 2、初始化方法处理器映射器
String packageName = Objects.requireNonNull(properties.getProperty("scanPackage"), "scanPackage属性不能为空!");
HandlerMapping methodHandlerMapping = MethodHandlerMapping.init(packageName);
handlerMappings.add(methodHandlerMapping);
}
(1)定义一个父类HandlerMapping,提供一个抽象方法getHandler。
/**
* 处理器映射器
* @author z_hh
* @time 2019年1月25日
*/
public abstract class HandlerMapping {
public abstract Handler getHandler(HttpServletRequest request);
}
(2)定义一个子类MethodHandlerMapping,负责查找控制类方法的处理器。初始化时,扫描指定包下的控制器方法,将它们加到一个自己的Map集合中,并返回一个单例对象。
/**
* 方法处理器映射器
*
* @author z_hh
* @date 2019年1月21日
*/
public class MethodHandlerMapping extends HandlerMapping {
private static final MethodHandlerMapping HANDLER_MAPPING = new MethodHandlerMapping();
/**
* 处理器集合
*/
private HashMap<String, MethodHandler> handlers = new HashMap<>();
private MethodHandlerMapping() {
}
/**
* 初始化处理器映射器并返回一个实例
*
* @param packageName
* 处理器扫描包名
* @return
*/
public static MethodHandlerMapping init(String packageName) {
HANDLER_MAPPING.doScanHandler(packageName);
return HANDLER_MAPPING;
}
/**
* 查找处理器
*
* @param request
* @return
*/
@Override
public Handler getHandler(HttpServletRequest request) {
// 1、获取请求路径
String url = request.getRequestURI(), contextPath = request.getContextPath();
String path = url.replace(contextPath, "").replaceAll("/+", "/");// 多个斜杠转为单杠
// 2、根据路径找处理器
MethodHandler handler = handlers.get(path);
if (Objects.isNull(handler)) {
return null;
}
// 3、获取请求方式
String method = request.getMethod();
// 4、校验请求方式
List<RequestMethod> methods = handler.getMethods();
if (method != null && !methods.isEmpty()) {
boolean anyMatch = methods.parallelStream().anyMatch(m -> method.toUpperCase().equals(m.toString()));
if (!anyMatch) {
return null;
}
}
// 5、返回处理器
return handler;
}
/**
* 扫描指定包下面的所有类,对符合的方法创建处理器对象
*
* @param packageName
* 处理器扫描包名
*/
private void doScanHandler(String packageName) {
// 1、递归获取指定包下所有类的全类名
List<String> classNames = new ArrayList<>();
doScanClass(packageName, classNames);
// 2、遍历所有类,对符合的类方法创建处理器对象
for (String className : classNames) {
try {
Class<?> clazz = Class.forName(className);
// 2.1类上面没有ZController注解,忽略
if (!clazz.isAnnotationPresent(ZController.class)) {
continue;
}
// 2.2类上面的ZRequestMapping注解的value作为根路径
String baseUrl = "";
if (clazz.isAnnotationPresent(ZRequestMapping.class)) {
baseUrl = clazz.getAnnotation(ZRequestMapping.class).value();
}
// 3.3获取所有公开方法,每个带ZRequestMapping注解的都可以作为一个处理器
Method[] methods = clazz.getMethods();
for (Method method : methods) {
// 3.4没有ZRequestMapping注解,忽略
if (!method.isAnnotationPresent(ZRequestMapping.class)) {
continue;
}
ZRequestMapping zRequestMapping = method.getAnnotation(ZRequestMapping.class);
// 3.5创建处理器
MethodHandler handler = new MethodHandler();
handler.setInstance(clazz.newInstance());
handler.setMethodName(method.getName());
setHandlerParameters(handler, method.getParameters());
handler.setRtType(method.getReturnType());
handler.setUrl((baseUrl + zRequestMapping.value()).replaceAll("/+", "/"));
handler.setMethods(Arrays.asList(zRequestMapping.method()));
// 3.6加入到处理器集合映射中
handlers.put(handler.getUrl(), handler);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 递归扫描指定包下面所有类,并将全类名放进集合
*
* @param packageName
* @param classNames
*/
private void doScanClass(String packageName, List<String> classNames) {
// 把所有的.替换成/
URL url = this.getClass().getClassLoader().getResource(packageName.replaceAll("\\.", "/"));
File dir = new File(url.getFile());
for (File file : dir.listFiles()) {
if (file.isDirectory()) {
// 递归读取包
doScanClass(packageName + "." + file.getName(), classNames);
} else {
if (!file.getName().endsWith(".class")) {
continue;
}
String className = packageName + "." + file.getName().replace(".class", "");
classNames.add(className);
}
}
}
/**
* 设置处理器方法参数
*
* @param handler
* @param parameters
*/
private void setHandlerParameters(MethodHandler handler, Parameter[] parameters) {
LinkedHashMap<String, Class<?>> handlerParameters = new LinkedHashMap<>();
for (Parameter parameter : parameters) {
String name = parameter.getName();
Class<?> type = parameter.getType();
// 存在ZRequestParam注解,设置别名
if (parameter.isAnnotationPresent(ZRequestParam.class)) {
String alias = parameter.getAnnotation(ZRequestParam.class).value();
name = alias;
}
handlerParameters.put(name, type);
}
handler.setParameters(handlerParameters);
}
}
对应处理器类MethodHandler
/**
* 方法处理器
* @author z_hh
* @time 2019年1月20日
*/
public class MethodHandler extends Handler {
/** 实例 */
private Object instance;
/** 方法名 */
private String methodName;
/** 参数列表(有序):K-类型 V-名称 */
private LinkedHashMap<String, Class<?>> parameters;
/** 返回值类型 */
private Class<?> rtType;
/** 可以匹配的url */
private String url;
/** 可以匹配的请求方式 */
private List<RequestMethod> methods;
// 省略getter/setter方法
}
(3)定义一个子类UrlHandlerMapping,负责查找静态资源的处理器。初始化时,将配置的静态资源Pattern加到自己的List集合中,并返回一个单例对象。
/**
* URL处理器映射器
* @author z_hh
* @date 2019年1月21日
*/
public class UrlHandlerMapping extends HandlerMapping {
private static final UrlHandlerMapping HANDLER_MAPPING = new UrlHandlerMapping();
/**
* 处理器集合
*/
private List<UrlHandler> handlers = new ArrayList<>();
/**
* 路径匹配器
*/
private AntPathMatcher antPathMatcher = new AntPathMatcher();
private UrlHandlerMapping() {}
/**
* 初始化处理器映射器并返回一个实例
* @param packageName 处理器扫描包名
* @return
*/
public static UrlHandlerMapping init(String resourcesStr) {
if (resourcesStr != null) {
// 将配置参数按英文逗号分割,去掉两边空白字符,然后放进集合(Java8Lambda表达式)
String[] resources = resourcesStr.split(",");
Arrays.stream(resources)
.map(String::trim)
.map(resource -> {
UrlHandler handler = new UrlHandler();
handler.setUrlPattern(resource);
return handler;
})
.forEach(HANDLER_MAPPING.handlers::add);
}
return HANDLER_MAPPING;
}
/**
* 查找处理器
* @param request
* @return
*/
@Override
public Handler getHandler(HttpServletRequest request) {
// 1、获取请求路径
String url =request.getRequestURI(),
contextPath = request.getContextPath();
String path = url.replace(contextPath, "").replaceAll("/+", "/");// 多个斜杠转为单杠
// 2、匹配处理器
for (UrlHandler urlHandler : handlers) {
String urlPattern = urlHandler.getUrlPattern();
boolean match = antPathMatcher.match(urlPattern, path);
if (match) {
urlHandler.setPath(url);
return urlHandler;
}
}
return null;
}
}
3、加载处理器适配器
分别创建控制器方法处理器的适配器和静态资源处理器的适配器。
/**
* 初始化处理器适配器
*/
private void initHandlerAdapters() {
// 1、初始化URL处理器适配器
HandlerAdapter urlHandlerAdapter = UrlHandlerAdapter.getInstance();
handlerAdapters.add(urlHandlerAdapter);
// 2、初始化方法处理器适配器
HandlerAdapter methodHandlerAdapter = MethodHandlerAdapter.getInstance();
handlerAdapters.add(methodHandlerAdapter);
}
/**
* 方法处理器适配器,用于反射(方法句柄)调用处理器方法
* @author z_hh
* @date 2019年1月21日
*/
public class MethodHandlerAdapter extends HandlerAdapter {
private static final HandlerAdapter HANDLER_ADAPTER = new MethodHandlerAdapter();
private MethodHandlerAdapter() {}
/**
* 获取单例对象
* @return
*/
public static HandlerAdapter getInstance() {
return HANDLER_ADAPTER;
}
/**
* 调用处理器
* @param handler 处理器
* @param request 方法参数
* @return 方法执行结果
* @throws Throwable
*/
@Override
public Object invoke(Handler handler, HttpServletRequest request, HttpServletResponse response) throws Throwable {
MethodHandler methodHandler = (MethodHandler) handler;
// 参数校验
check(methodHandler, request);
// 实例对象
Object instance = methodHandler.getInstance();
// 方法名
String methodName = methodHandler.getMethodName();
// 参数列表
LinkedHashMap<String, Class<?>> methodParamMap = methodHandler.getParameters();
// 参数值
Object[] args = getMethodArgs(methodParamMap, request, response);
Class<?>[] array = new Class[methodParamMap.size()];
/* 方法句柄的方式,效率更高 */
/*MethodType methodType = MethodType.methodType(handler.getRtType(), parameters.values().toArray(array));
MethodHandle methodHandle = MethodHandles.lookup().findVirtual(instance.getClass(), methodName, methodType);
return methodHandle.invoke(instance, args);*/
// 获取方法
Method method = instance.getClass().getMethod(methodName, methodParamMap.values().toArray(array));
// 调用方法
return method.invoke(instance, args);
}
private void check(MethodHandler methodHandler, HttpServletRequest request) throws ZmvcException {
// 1、获取请求参数
Map<String, String[]> parameterMap = request.getParameterMap();// 指定名称的参数可能值有多个
// 2、校验请求参数,不符合返回400
LinkedHashMap<String, Class<?>> parameters = methodHandler.getParameters();
for (Object nameValue : parameterMap.entrySet()) {
Entry entry = (Entry) nameValue;
// 2.1指定名称的参数不存在
Class<?> targetClazz = parameters.get(entry.getKey());
if (Objects.isNull(targetClazz)) {
throw new ZmvcException(HttpServletResponse.SC_BAD_REQUEST, "请求参数不正确!");
}
// 2.2类型不匹配(这个问题,不会处理)
/*if (!targetClazz.isAssignableFrom(entry.getKey().getClass())) {
throw new ZmvcException(HttpServletResponse.SC_BAD_REQUEST, "参数类型不匹配!");
}*/
}
}
private Object[] getMethodArgs(LinkedHashMap<String, Class<?>> methodParamMap, HttpServletRequest request, HttpServletResponse response) {
// 1、获取请求参数集合
Map<String, String[]> parameterMap = request.getParameterMap();// 指定名称的参数可能值有多个
// 2、创建方法参数值数组
Object[] args = new Object[methodParamMap.size()];
// 3、按顺序设值,methodParamMap的key顺序就是参数顺序
int index = 0;
for (Entry<String, Class<?>> paramNT : methodParamMap.entrySet()) {
Class<?> paramType = paramNT.getValue();
// 3.1如果是ServletRequest类型的参数,赋值request
if (ServletRequest.class.isAssignableFrom(paramType)) {
args[index] = request;
// 3.2如果是ServletResponse类型的参数,赋值response
} else if (ServletResponse.class.isAssignableFrom(paramType)) {
args[index] = response;
// 3.3其它,根据名称设值
} else {
String paramName = paramNT.getKey();
// 这个会得到一个数组,这里处理得不是很好,大家有什么建议
String[] values = parameterMap.get(paramName);
if (values != null) {
args[index] = values.length > 1 ? values : values[0];
}
}
index++;
}
return args;
}
@Override
public boolean supports(Handler handler) {
return handler instanceof MethodHandler;
}
}
/**
* URL处理器适配器
* @author z_hh
* @time 2019年1月25日
*/
public class UrlHandlerAdapter extends HandlerAdapter {
private static final HandlerAdapter HANDLER_ADAPTER = new UrlHandlerAdapter();
private UrlHandlerAdapter() {}
/**
* 获取单例对象
* @return
*/
public static HandlerAdapter getInstance() {
return HANDLER_ADAPTER;
}
@Override
public Object invoke(Handler handler, HttpServletRequest request, HttpServletResponse response)
throws Throwable {
UrlHandler urlHandler = (UrlHandler) handler;
response.sendRedirect(urlHandler.getPath());
return null;
}
@Override
public boolean supports(Handler handler) {
return handler instanceof UrlHandler;
}
}
四、处理请求
重写doGet、doPost、doPut方法等,将它们统一引入到doService方法,然后交由doDispatch处理并处理抛出的异常。
/**
* 方法入口
* @param request
* @param response
* @throws IOException
*/
protected void doService(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
doDispatch(request, response);
} catch (ZmvcException e) {
response.sendError(e.getStatus(), e.getMessage());
} catch (Throwable t) {
t.printStackTrace();
response.getWriter().write("500! Server Exception");
}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Throwable {
// 1、获取处理器
Handler handler = getHandler(request);
// 2、获取处理器适配器
HandlerAdapter handlerAdapter = getAdapter(handler);
// 3、处理器适配器调用处理器
Object result = handlerAdapter.invoke(handler, request, response);
// 4、回写结果
response.getWriter().println(result);
}
1、获取处理器
遍历处理器映射器集合,调用它们的getHandler方法查找处理器,没有时抛出404异常。
/**
* 获取匹配的处理器
* @param request
* @return 处理器
* @throws ZmvcException
*/
private Handler getHandler(HttpServletRequest request) throws ZmvcException {
for (HandlerMapping handlerMapping : handlerMappings) {
Handler handler = handlerMapping.getHandler(request);
if (handler != null) {
return handler;
}
}
throw new ZmvcException(HttpServletResponse.SC_NOT_FOUND, "您访问的URL不存在!");
}
2、获取处理器适配器
遍历处理器适配器集合,调用它们的supports方法判断是否支持调用指定的处理器。
private HandlerAdapter getAdapter(Handler handler) throws ZmvcException {
for (HandlerAdapter handlerAdapter : handlerAdapters) {
if (handlerAdapter.supports(handler)) {
return handlerAdapter;
}
}
throw new ZmvcException(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "服务器不能处理该请求!");
}
3、处理器适配器调用处理器
适配器调用invoke方法。如果是控制器方法类型的处理器,使用反射或者方法句柄的方式调用;如果是静态资源,使用请求转发或者重定向访问(循环请求的问题,还没处理)。
五、测试模块
测试模块引入了核心模块,并使用Tomcat插件(可选)。
<dependencies>
<dependency>
<groupId>cn.zhh</groupId>
<artifactId>zmvc-core</artifactId>
</dependency>
</dependencies>
<build>
<finalName>zmvc-test</finalName>
<plugins>
<!-- tomcat plugin -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
</configuration>
</plugin>
</plugins>
</build>
在web.xml里面配置前端控制器,处理所有请求。
<servlet>
<servlet-name>ZDispatcherServlet</servlet-name>
<servlet-class>cn.zhh.servlet.ZDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- config file path -->
<param-value>classpath:app.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>ZDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
配置文件写上扫描的包和静态资源。
scanPackage=cn.zhh.controller
staticResources=/*.html
编写测试控制器
/**
* 测试控制器
* @author z_hh
* @date 2019年1月21日
*/
@ZController
@ZRequestMapping("/test")
public class TestController {
@ZRequestMapping
public String test(String name, HttpServletResponse response) throws IOException {
return "Hello " + name;
}
@ZRequestMapping(value="/test1")
public String test1(String name, HttpServletResponse response) throws IOException {
return "Hello " + name;
}
@ZRequestMapping(value="/test2", method=RequestMethod.GET)
public String test2(String name, HttpServletResponse response) throws IOException {
return "Hello " + name;
}
@ZRequestMapping(value="/test3", method=RequestMethod.POST)
public String test3(String name, HttpServletResponse response) throws IOException {
return "Hello " + name;
}
@ZRequestMapping(value="/test4")
public String test4(@ZRequestParam("name") String xxx, HttpServletResponse response) throws IOException {
return "Hello " + xxx;
}
@ZRequestMapping(value="/test5")
public String test5(String name, Long i, HttpServletResponse response) throws IOException {
return "Hello " + name + i;
}
@ZRequestMapping(value="/test6")
public String test6(String name, String i, HttpServletResponse response) throws IOException {
return "Hello " + name + i;
}
}
六、完整代码
1、ZDispatcherServlet类
/**
* ZMVC框架前端控制器
* @author z_hh
* @date 2019年1月21日
*/
public class ZDispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* 配置文件属性
*/
private Properties properties = new Properties();
/**
* 处理器映射器
*/
private List<HandlerMapping> handlerMappings = new ArrayList<>();
/**
* 处理器适配器
*/
private List<HandlerAdapter> handlerAdapters = new ArrayList<>();
public void init(ServletConfig config) throws ServletException {
// 1、加载配置文件
loadConfig(config.getInitParameter("contextConfigLocation"));
// 2、初始化处理器映射器
initHandlerMappings();
// 3、初始化处理器适配器
initHandlerAdapters();
}
/**
* 方法入口
* @param request
* @param response
* @throws IOException
*/
protected void doService(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
doDispatch(request, response);
} catch (ZmvcException e) {
response.sendError(e.getStatus(), e.getMessage());
} catch (Throwable t) {
t.printStackTrace();
response.getWriter().write("500! Server Exception");
}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Throwable {
// 1、获取处理器
Handler handler = getHandler(request);
// 2、获取处理器适配器
HandlerAdapter handlerAdapter = getAdapter(handler);
// 3、处理器适配器调用处理器
Object result = handlerAdapter.invoke(handler, request, response);
// 4、回写结果
response.getWriter().println(result);
}
/**
* 加载配置
* @param confLocation
* @throws ServletException
*/
private void loadConfig(String confLocation) throws ServletException {
// 把web.xml中的contextConfigLocation对应value值的文件加载到流里面
if (confLocation.startsWith("classpath:")) {
confLocation = confLocation.replace("classpath:", "");
} else if (confLocation.contains("/")) {
int lastSplitIndex = confLocation.lastIndexOf('/');
confLocation = confLocation.substring(lastSplitIndex + 1, confLocation.length());
}
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(confLocation);
try {
//用Properties文件加载文件里的内容
properties.load(resourceAsStream);
} catch (IOException e) {
throw new ServletException("加载配置文件异常!", e);
} finally {
//关流
if(null != resourceAsStream){
try {
resourceAsStream.close();
} catch (IOException e) {
throw new ServletException("加载配置文件异常!", e);
}
}
}
}
/**
* 初始化处理器映射器
*/
private void initHandlerMappings() {
// 1、初始化URL处理器映射器
HandlerMapping urlHandlerMapping = UrlHandlerMapping.init(properties.getProperty("staticResources"));
handlerMappings.add(urlHandlerMapping);
// 2、初始化方法处理器映射器
String packageName = Objects.requireNonNull(properties.getProperty("scanPackage"), "scanPackage属性不能为空!");
HandlerMapping methodHandlerMapping = MethodHandlerMapping.init(packageName);
handlerMappings.add(methodHandlerMapping);
}
/**
* 初始化处理器适配器
*/
private void initHandlerAdapters() {
// 1、初始化URL处理器适配器
HandlerAdapter urlHandlerAdapter = UrlHandlerAdapter.getInstance();
handlerAdapters.add(urlHandlerAdapter);
// 2、初始化方法处理器适配器
HandlerAdapter methodHandlerAdapter = MethodHandlerAdapter.getInstance();
handlerAdapters.add(methodHandlerAdapter);
}
/**
* 获取匹配的处理器
* @param request
* @return 处理器
* @throws ZmvcException
*/
private Handler getHandler(HttpServletRequest request) throws ZmvcException {
for (HandlerMapping handlerMapping : handlerMappings) {
Handler handler = handlerMapping.getHandler(request);
if (handler != null) {
return handler;
}
}
throw new ZmvcException(HttpServletResponse.SC_NOT_FOUND, "您访问的URL不存在!");
}
private HandlerAdapter getAdapter(Handler handler) throws ZmvcException {
for (HandlerAdapter handlerAdapter : handlerAdapters) {
if (handlerAdapter.supports(handler)) {
return handlerAdapter;
}
}
throw new ZmvcException(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "服务器不能处理该请求!");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doService(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doService(request, response);
}
@Override
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doService(request, response);
}
@Override
protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doService(request, response);
}
@Override
protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doService(request, response);
}
@Override
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doService(request, response);
}
@Override
protected void doTrace(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doService(request, response);
}
}
2、自定义注解
/**
* 表明当前类是处理器类
* @author z_hh
* @time 2019年1月20日
*/
@Documented
@Retention(RUNTIME)
@Target(TYPE)
public @interface ZController {
}
/**
* 表明当前方法是处理器方法
* @author z_hh
* @time 2019年1月20日
*/
@Documented
@Retention(RUNTIME)
@Target({ TYPE, METHOD })
public @interface ZRequestMapping {
/**
* 请求路径(支持一个)
*/
String value() default "";
/**
* 请求方式(支持多个,为空时不限制)
*/
RequestMethod[] method() default {};
public enum RequestMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}
}
/**
* 方法参数别名
* @author z_hh
* @time 2019年1月20日
*/
@Documented
@Retention(RUNTIME)
@Target(PARAMETER)
public @interface ZRequestParam {
/**
* 参数的别名,必填
*/
String value();
}
3、自定义异常类
/**
* 自定义异常
* @author z_hh
* @time 2019年1月20日
*/
public class ZmvcException extends Exception {
private static final long serialVersionUID = -5129476850780832850L;
/** HTTP状态码 */
private int status;
public ZmvcException(int status) {
super();
this.status = status;
}
public ZmvcException(int status, String message) {
super(message);
this.status = status;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
}
放假了,忙着准备回家,所以就介绍的比较粗糙。不过结合上一篇讲解SpringMVC源码的文章的话,应该很容易看懂~