21. 用30个类手写V2.0版本之MVC实现
21. 用30个类手写V2.0版本之MVC实现
MVC顶层设计
一、请求调用
public class GPDispatcherServlet extends HttpServlet { //保存Controller中URL和Method的对应关系 private List<GPHandlerMapping> handlerMappings = new ArrayList<GPHandlerMapping>(); private Map<GPHandlerMapping,GPHandlerAdapter> handlerAdapters = new HashMap<GPHandlerMapping,GPHandlerAdapter>(); private List<GPViewResolver> viewResolvers = new ArrayList<GPViewResolver>(); //IoC容器的访问上下文 private GPApplicationContext applicationContext = null; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //6、根据URL委派给具体的调用方法 try { doDispatch(req,resp); } catch (Exception e) { e.printStackTrace();// resp.getWriter().write("500 Exception,Detail: " + Arrays.toString(e.getStackTrace())); Map<String,Object> model = new HashMap<String, Object>(); model.put("detail","500 Exception,Detail: "); model.put("stackTrace",Arrays.toString(e.getStackTrace())); try { processDispatchResult(req,resp,new GPModelAndView("500",model)); } catch (Exception exception) { exception.printStackTrace(); } } } private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception { //1、根据URL 拿到对应的Handler GPHandlerMapping handler = getHandler(req); if(null == handler){ processDispatchResult(req,resp,new GPModelAndView("404")); return; } //2、根据HandlerMapping拿到HandlerAdapter GPHandlerAdapter ha = getHandlerAdapter(handler); //3、根据HandlerAdapter拿到对应的ModelAndView GPModelAndView mv = ha.handle(req,resp,handler); //4、根据ViewResolver找到对应View对象 //通过View对象渲染页面,并返回 processDispatchResult(req,resp,mv); } private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, GPModelAndView mv) throws Exception { if(null == mv){return;} if(this.viewResolvers.isEmpty()){return;} for (GPViewResolver viewResolver : this.viewResolvers) { GPView view = viewResolver.resolveViewName(mv.getViewName()); view.render(mv.getModel(),req,resp); return; } } private GPHandlerAdapter getHandlerAdapter(GPHandlerMapping handler) { if(this.handlerAdapters.isEmpty()){ return null;} GPHandlerAdapter ha = this.handlerAdapters.get(handler); return ha; } private GPHandlerMapping getHandler(HttpServletRequest req) { String url = req.getRequestURI(); String contextPath = req.getContextPath(); url = url.replaceAll(contextPath,"").replaceAll("/+","/"); for (GPHandlerMapping handlerMapping : this.handlerMappings) { Matcher matcher = handlerMapping.getPattern().matcher(url); if(!matcher.matches()){ continue;} return handlerMapping; } return null; } @Override public void init(ServletConfig config) throws ServletException { applicationContext = new GPApplicationContext(config.getInitParameter("contextConfigLocation")); //========== MVC ========== initStrategies(applicationContext); System.out.println("GP Spring framework is init."); } //初始化策略 protected void initStrategies(GPApplicationContext context) { //handlerMapping initHandlerMappings(context); //初始化参数适配器 initHandlerAdapters(context); //初始化视图转换器 initViewResolvers(context); } private void initViewResolvers(GPApplicationContext context) { String templateRoot = context.getConfig().getProperty("templateRoot"); String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile(); File templateRootDir = new File(templateRootPath); for (File file : templateRootDir.listFiles()) { this.viewResolvers.add(new GPViewResolver(templateRoot)); } } private void initHandlerAdapters(GPApplicationContext context) { for (GPHandlerMapping handlerMapping : handlerMappings) { this.handlerAdapters.put(handlerMapping,new GPHandlerAdapter()); } } private void initHandlerMappings(GPApplicationContext context) { if(this.applicationContext.getBeanDefinitionCount() == 0 ){ return; } for (String beanName : this.applicationContext.getBeanDefinitionNames()) { Object instance = applicationContext.getBean(beanName); Class<?> clazz = instance.getClass(); if(!clazz.isAnnotationPresent(GPController.class)){ continue; } String baseUrl = ""; if(clazz.isAnnotationPresent(GPRequestMapping.class)){ GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class); baseUrl = requestMapping.value(); } //只迭代public方法 for (Method method : clazz.getMethods()) { if(!method.isAnnotationPresent(GPRequestMapping.class)){ continue; } GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class); // //demo//query String regex = ("/" + baseUrl + "/" + requestMapping.value()) .replaceAll("\\*",".*") .replaceAll("/+","/"); Pattern pattern = Pattern.compile(regex); handlerMappings.add(new GPHandlerMapping(pattern,instance,method)); System.out.println("Mapped : " + regex + " --> " + method); } } } }
二、请求映射
public class GPHandlerMapping { private Object controller; //保存方法应对的实例 protected Method method; //保存映射的方法 protected Pattern pattern; //URL 的正则匹配 public GPHandlerMapping(Pattern pattern,Object controller,Method method) { this.controller = controller; this.method = method; this.pattern = pattern; } public Method getMethod() { return method; } public Pattern getPattern() { return pattern; } public Object getController() { return controller; } }
三、请求方法适配器
public class GPHandlerAdapter { public GPModelAndView handle(HttpServletRequest req, HttpServletResponse resp, GPHandlerMapping handler) throws Exception { Method method = handler.getMethod(); //1、先把形参的位置和参数名字建立映射关系,并且缓存下来 Map<String,Integer> paramIndexMapping = new HashMap<String, Integer>(); Annotation[][] pa = method.getParameterAnnotations(); for (int i = 0; i < pa.length; i ++) { for (Annotation a : pa[i]) { if(a instanceof GPRequestParam){ String paramName = ((GPRequestParam) a).value(); if(!"".equals(paramName.trim())){ paramIndexMapping.put(paramName,i); } } } } Class<?> [] paramTypes = method.getParameterTypes(); for (int i = 0; i < paramTypes.length; i++) { Class<?> type = paramTypes[i]; if(type == HttpServletRequest.class || type == HttpServletResponse.class){ paramIndexMapping.put(type.getName(),i); } } //2、根据参数位置匹配参数名字,从url中取到参数名字对应的值 Object[] paramValues = new Object[paramTypes.length]; //http://localhost/demo/query?name=Tom&name=Tomcat&name=Mic Map<String,String[]> params = req.getParameterMap(); for (Map.Entry<String, String[]> param : params.entrySet()) { String value = Arrays.toString(param.getValue()) .replaceAll("\\[|\\]","") .replaceAll("\\s",""); if(!paramIndexMapping.containsKey(param.getKey())){continue;} int index = paramIndexMapping.get(param.getKey()); //涉及到类型强制转换 paramValues[index] = caseStringValue(value,paramTypes[index]); } if(paramIndexMapping.containsKey(HttpServletRequest.class.getName())){ int index = paramIndexMapping.get(HttpServletRequest.class.getName()); paramValues[index] = req; } if(paramIndexMapping.containsKey(HttpServletResponse.class.getName())){ int index = paramIndexMapping.get(HttpServletResponse.class.getName()); paramValues[index] = resp; } Object result = method.invoke(handler.getController(),paramValues); if(result == null || result instanceof Void){ return null;} boolean isModelAndView = handler.getMethod().getReturnType() == GPModelAndView.class; if(isModelAndView){ return (GPModelAndView) result; } return null; } private Object caseStringValue(String value, Class<?> paramType) { if(String.class == paramType){ return value; } if(Integer.class == paramType){ return Integer.valueOf(value); }else if(Double.class == paramType){ return Double.valueOf(value); }else { if(value != null){ return value; } return null; } } }
四、页面数据封装
public class GPModelAndView { private String viewName; private Map<String,?> model; public GPModelAndView(String viewName) { this.viewName = viewName; } public GPModelAndView(String viewName, Map<String, ?> model) { this.viewName = viewName; this.model = model; } public String getViewName() { return viewName; } public Map<String, ?> getModel() { return model; } }
五、试图解析器
public class GPViewResolver { //.vm .ftl .jsp .gp .tom private final String DEFAULT_TEMPLATE_SUFFIX = ".html"; private File templateRootDir; public GPViewResolver(String templateRoot) { String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile(); templateRootDir = new File(templateRootPath); } public GPView resolveViewName(String viewName) { if(null == viewName || "".equals(viewName.trim())){return null;} viewName = viewName.endsWith(DEFAULT_TEMPLATE_SUFFIX) ? viewName : (viewName + DEFAULT_TEMPLATE_SUFFIX); File templateFile = new File((templateRootDir.getPath() + "/" + viewName) .replaceAll("/+","/")); return new GPView(templateFile); } }
六、自定义引擎模板
public class GPView { private File viewFile; public GPView(File templateFile) { this.viewFile = templateFile; } public void render(Map<String,?> model, HttpServletRequest req, HttpServletResponse resp) throws Exception { //无反射,不框架 //无正则,不架构 StringBuffer sb = new StringBuffer(); RandomAccessFile ra = new RandomAccessFile(this.viewFile,"r"); String line = null; while (null != (line = ra.readLine())){ line = new String(line.getBytes("iso-8859-1"),"utf-8"); Pattern pattern = Pattern.compile("¥\\{[^\\}]+\\}",Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(line); while (matcher.find()){ // ¥{teacher} String paramName = matcher.group(); paramName = paramName.replaceAll("¥\\{|\\}",""); Object paramValue = model.get(paramName); if(null == paramValue){continue;} line = matcher.replaceFirst(makeStringForRegExp(paramValue.toString())); matcher = pattern.matcher(line); } sb.append(line); } resp.setCharacterEncoding("utf-8"); resp.getWriter().write(sb.toString()); } //处理特殊字符 public static String makeStringForRegExp(String str) { return str.replace("\\", "\\\\").replace("*", "\\*") .replace("+", "\\+").replace("|", "\\|") .replace("{", "\\{").replace("}", "\\}") .replace("(", "\\(").replace(")", "\\)") .replace("^", "\\^").replace("$", "\\$") .replace("[", "\\[").replace("]", "\\]") .replace("?", "\\?").replace(",", "\\,") .replace(".", "\\.").replace("&", "\\&"); } }
柒徳咙咚呛 : Just Do It 资源地址:https://www.cnblogs.com/Andy-15811517571/