DispatcherServlet - 设置中央控制器
创建核心控制器,拦截所有请求进行处理,然后将请求发送给相应 xxController = 调用 xxController 方法处理请求
将原本的 xxServlet 改为 xxController,不拦截请求,只处理转发到控制器的请求 = 通过控制器方法处理请求
-
@WebServlet("*.do") //拦截所有以 .do 结尾的请求 /xxx.do public class DispatcherServlet extends ViewBaseServlet{ @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置编码 request.setCharacterEncoding("UTF-8"); //获取请求的 url 即 /xxx.do String url = request.getServletPath(); // url = /xxx.do //字符串截取 获取 *的值 url = url.substring(1); // 去除 / int lastDotIndex = url.lastIndexOf(".do"); // 获取最后一个 .do 中 . 的索引 url = url.substring(0,lastDotIndex); // 去除 .do // 最后 url = xxx System.out.println(url); } }
配置 applicationContext.xml 文件,设置相应 url:/xxx.do 的请求由对应的 xxxController 来处理
-
<?xml version="1.0" encoding="utf-8" ?> <beans> <!-- 设置 servletpath 的名字是 fruit 的请求由 FruitController 来处理 --> <bean id="fruit" class="com.atguigu.fruit.controllers.FruitController"/> </beans>
在 DispatcherServlet 的构造方法中解析 applicationContext.xml 文件
将 bean 标签中的 id 和 class 取出,形成一一对应的键值对,key = id,value = 反射获取的class对应的类对象
-
//创造键值对存储对象,存放 url:/xxx.do 与 xxxController 的类对象一一对应关系 private Map<String,Object> beanMap = new HashMap<String,Object>(); //在构造方法中解析 applicationContext.xml 文件 public DispatcherServlet() { //创建 InputStream 输入流对象 读取文件 InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml"); //创建 DocumentBuilderFactory 对象 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); //创建 DocumentBuilder 对象 DocumentBuilder documentBuilder = null; try { documentBuilder = documentBuilderFactory.newDocumentBuilder(); } catch (ParserConfigurationException e) { throw new RuntimeException(e); } //创建 Document 对象 try { Document document = documentBuilder.parse(inputStream); //按标签读取文件中的内容 获取所有的 bean 节点 NodeList beanNodeList = document.getElementsByTagName("bean"); for (int i = 0; i < beanNodeList.getLength(); i++) { //遍历所有节点 Node beanNode = beanNodeList.item(i); //判断节点的类型,将元素类型的节点强转为元素节点 //从而可以调用元素节点的方法,获取节点中的 Attribute if(beanNode.getNodeType() == Node.ELEMENT_NODE){ Element beanElement = (Element) beanNode; String beanId = beanElement.getAttribute("id"); String className = beanElement.getAttribute("class"); //根据获取到的 className 创建类实例对象,id 与类实例对象一一对应 Object beanObj = Class.forName(className).newInstance(); //作为键值对存储, key=beanId,value=beanOjb beanMap.put(beanId,beanObj); } } } catch(...){...} }
-
解析一个文件的固定步骤
- 创建 InputStream 输入流对象 读取文件
- 创建 DocumentBuilderFactory 对象
- 创建 DocumentBuilder 对象
- 创建 Document 对象
- 使用 Document 对象读取文件内容
然后就可以在 Map 中通过 key 获取对应的 value,value 是 url 对应的 controller 类对象
-
Object controllerBeanObj = beanMap.get(url);
-
如此一来,我们就得到了请求需要调用的 controller 类,再通过 operate 调用 controller类中需要调用的方法
在 DispatcherController 中 判断请求传入的 operate 参数值
-
Object controllerBeanObj = beanMap.get(url); String operate = request.getParameter("operate"); if(StringUtil.isEmpty(operate)){ operate = "index"; } try { // 直接通过 operate 获取指定方法 method Method method = controllerBeanObj.getClass().getDeclaredMethod(operate, HttpServletRequest.class, HttpServletResponse.class); if(method != null){ // .invoke method.invoke(controllerBeanObj, request, response); }else{ throw new RuntimeException("operate值非法!"); } } catch (...) {...}
-
整个过程通过反射实现,controllerBeanObj 是类对象,operate 是方法的 className,通过反射得到类中的相应方法,通过 Method 对象的 .invoke(obj,...params) 方法调用 method 方法,传入参数是 方法所在的类的实例对象 和 调用方法需要传入的参数,