1 背景
之前编写的项目,每个请求都需要一个servlet来响应。我们可以优化代码结构,让多个请求可以被一个servlet接收,然后分发给不同的方法进行处理。
2 原理
- 定义一个servlet,接收某一大类的请求(比如
*.do ); - 定义一个分发处理类,保存请求地址uri和处理方法之间的映射关系;
- 由servlet接收请求,调用分发处理类的get方法,找到能够处理此请求的对象(注意java是面向对象的,因此能够处理请求的方法一定是封装在对象里的)
- 由对象.方法进行请求处理。
2.1 方法绑定请求地址
通过注解的形式,可以实现
- 请求地址与方法之间的绑定。
- 确定方法的返回值是一个返回页面地址,还是一段文字内容。(通过定义两个类型的注解实现区分)
3 实现
3.1 定义servlet:
3.1.1 web.xml
- 定义哪一类请求会进到此servlet中
- 定义properties的地址
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>a</servlet-name>
<servlet-class>com.kkb.xzk.mvc.DispatcherServlet</servlet-class>
<init-param>
<param-name>contentConfigLocation</param-name>
<param-value>application.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>a</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
3.1.2 DispatcherServlet
- init方法:在servlet加载时自动执行。在这个方法中将url和方法(类对象)之间的绑定关系读到map里
- service方法:根据请求uri地址找到map中的对象方法调用执行即可
package com.kkb.xzk.mvc;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class DispatcherServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
String path = config.getInitParameter("contentConfigLocation");
InputStream in = DispatcherServlet.class.getClassLoader().getResourceAsStream(path);
HandlerMapping.load(in);
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uri = req.getRequestURI();
HandlerMapping.MVCMapping mapping = HandlerMapping.get(uri);
if(mapping == null){
resp.sendError(404, "请求未找到!");
return;
}
Method method = mapping.getMethod();
Object obj = mapping.getObj();
ResponseType responseType = mapping.getResponseType();
try {
String result = (String) method.invoke(obj, req, resp);
if(responseType == ResponseType.TEXT){
resp.getWriter().print(result);
}else if(responseType == ResponseType.VIEW){
resp.sendRedirect(result);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
3.2 定义properties:保存参与请求处理的类
properties里的每一行的value都代表一个类名,表示这个类的0-n个方法会参与到请求处理之中。
#在这里保存所有会参与处理请求的类名称
a=com.kkb.xzk.test.Login
3.3 注解定义:区分方法的返回值是一段文本还是返回页面
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {
String value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseView {
String value();
}
3.4 枚举定义:以枚举的形式区分方法的返回值类型
public enum ResponseType {
TEXT,VIEW;
}
3.5 定义映射池类:将uri和方法做绑定
package com.kkb.xzk.mvc;
import com.kkb.xzk.mvc.annotation.ResponseBody;
import com.kkb.xzk.mvc.annotation.ResponseView;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class HandlerMapping {
private static Map<String, MVCMapping> data = new HashMap<>();
public static HandlerMapping.MVCMapping get(String uri){
return data.get(uri);
}
public static void load(InputStream in){
Properties ppt = new Properties();
try {
ppt.load(in);
} catch (IOException e) {
e.printStackTrace();
}
Collection<Object> classPaths = ppt.values();
for (Object classPath : classPaths) {
String className = (String) classPath;
try {
Class clazz = Class.forName(className);
Object obj = clazz.getDeclaredConstructor().newInstance();
Method[] methods = clazz.getMethods();
for (Method method : methods) {
Annotation[] annotations = method.getAnnotations();
if(annotations != null && annotations.length > 0) {
for (Annotation annotation : annotations) {
if (annotation instanceof ResponseBody) {
String uri = ((ResponseBody) annotation).value();
MVCMapping mapping = new MVCMapping();
mapping.setMethod(method);
mapping.setObj(obj);
mapping.setResponseType(ResponseType.TEXT);
Object o = data.put(uri, mapping);
if(o != null){
throw new RuntimeException("请求重复:"+uri);
}
} else if (annotation instanceof ResponseView) {
String uri = ((ResponseView) annotation).value();
MVCMapping mapping = new MVCMapping();
mapping.setMethod(method);
mapping.setObj(obj);
mapping.setResponseType(ResponseType.VIEW);
Object o = data.put(uri, mapping);
if(o != null){
throw new RuntimeException("请求重复:"+uri);
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static class MVCMapping{
private Object obj;
private Method method;
private ResponseType responseType;
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public ResponseType getResponseType() {
return responseType;
}
public void setResponseType(ResponseType responseType) {
this.responseType = responseType;
}
}
}
3.6 自定义请求处理方法
public class Login {
@ResponseBody("/login.do")
public String login(HttpServletRequest req, HttpServletResponse resp){
return "登录成功!";
}
@ResponseView("/reg.do")
public String reg(HttpServletRequest req, HttpServletResponse resp){
return "success.jsp";
}
}
3.7 流程总结
请求 -> DispatcherServlet -> 根据uri找到MVCMapping对象 -> 方法调用处理请求
|