3.初始化映射关系
有了框架以后,所有请求都会交给框架,框架会根据映射关系调用controller的指定方法,前面我们说到映射关系可以使用xml配置,也可以使用注解配置,那么每次请求映射都需要读取一次配置信息吗?
答案显然是不!我们只需要在服务器启动的时候读取一次,并存入缓存即可。
* static{}
* servlet.init
配置信息来自两部分,①是配置文件,目前我们规定的只有mapping请求映射信息;
②是注解,详见下一文档
package web;import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.SAXReader;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.Method;import java.util.HashMap;import java.util.List;import java.util.Map;public class DispatcherServlet extends HttpServlet {/*缓存请求映射信息key="/test1"*/Map<String,MappingInfo> infoMap=new HashMap<>();/*** 读取各种配置信息* (目前只有请求映射信息)** @throws ServletException*/@Overridepublic void init() throws ServletException {//目前配置信息来自于两个部分,而且两个部分都可能存在String xmlPath = super.getInitParameter("classpath");if (xmlPath != null && !"".equals(xmlPath)) {//指定了配置文件,需要读取配置文件readXml(xmlPath);}String packagePath = super.getInitParameter("controller-scan");if (packagePath != null && !"".equals(packagePath)) {//指定了包路径,需要读取包下类中的注解信息}}//读取xml配置文件public void readXml(String path) {//我们要的完整路径String realPath = Thread.currentThread().getContextClassLoader().getResource(path).getPath();try {InputStream is = new FileInputStream(realPath);//使用dom,sax读取xml内容SAXReader reader=new SAXReader();Document document=reader.read(is);//解析mapping标签parseMappingElement(document);//解析其他标签} catch (Exception e) {e.printStackTrace();}}//读取xml中<mapping>请求映射信息private void parseMappingElement(Document document)throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException {//按照标签的嵌套规则,找到需要的所有mapping标记List<Element> mappingElements = document.selectNodes("mvc/mapping");for (Element element : mappingElements) {/*** 获取标签指定的属性值,标签中记载着请求与响应对应关系* 此时(初始化)获得这个对应关系是为了后面请求的时候用* 所以需要存储起来*/String path = element.attributeValue("path");String className = element.attributeValue("class");String methodName = element.attributeValue("method");//反射创建目标对象Class clazz = Class.forName(className);Object obj = clazz.newInstance();//通过反射,根据方法名获取方法对象,方法可能存在重载,参数在<type></type>标签里面List<Element> typeElements = element.selectNodes("type");Class[] types = new Class[typeElements.size()];int i=0;for (Element typeEle : typeElements) {String typeStr = typeEle.getText();types[i++]=castStringToClass(typeStr);}Method method = clazz.getMethod(methodName, types);MappingInfo info = new MappingInfo(path, obj, method);System.out.println(method);infoMap.put(path,info);}}//将字符串表示的类型转换成对应的Classprivate Class castStringToClass(String typeStr) throws ClassNotFoundException {if("int".equals(typeStr)){return int.class;}if("long".equals(typeStr)){return long.class;}if("double".equals(typeStr)){return double.class;}//除了8种基本类型(这里只列举了三种),其余引用类型写的都是全名,可以反射生成Classreturn Class.forName(typeStr);}//读取注解public void Annotation(String path) {//我么要的完整路径String realPath = Thread.currentThread().getContextClassLoader().getResource(path).getPath();}@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//请求映射,根据请求调用controller的具体方法//获得请求 /test1//根据请求找到对应的mappinginfo 映射信息//获得参数--》处理参数--》调用mappinginfo中的对象的方法,传递参数}}
package web;import java.lang.reflect.Method;/*** 存储请求映射关系*/public class MappingInfo {private String path;//例如 /test1 请求命令private Object controller;//例如 TestController 目标对象private Method method;//例如 t1 目标方法public MappingInfo() {}public MappingInfo(String path, Object controller, Method method) {this.path = path;this.controller = controller;this.method = method;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}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;}}
