Browse Source

手写MVC优化

青涩知夏 4 years ago
parent
commit
ef8b681082
23 changed files with 745 additions and 94 deletions
  1. 0 56
      src/main/java/cn/nosum/demo/mvc/action/DemoAction.java
  2. 65 0
      src/main/java/cn/nosum/demo/mvc/action/MyAction.java
  3. 30 0
      src/main/java/cn/nosum/demo/mvc/action/PageAction.java
  4. 0 23
      src/main/java/cn/nosum/demo/mvc/action/TwoAction.java
  5. 21 0
      src/main/java/cn/nosum/demo/service/IModifyService.java
  6. 10 0
      src/main/java/cn/nosum/demo/service/IQueryService.java
  7. 36 0
      src/main/java/cn/nosum/demo/service/impl/ModifyService.java
  8. 25 0
      src/main/java/cn/nosum/demo/service/impl/QueryService.java
  9. 3 2
      src/main/java/cn/nosum/framework/beans/support/BeanDefinitionReader.java
  10. 5 6
      src/main/java/cn/nosum/framework/context/ApplicationContext.java
  11. 1 4
      src/main/java/cn/nosum/framework/mvc/v3/servlet/GPDispatcherServlet.java
  12. 153 0
      src/main/java/cn/nosum/framework/mvc/v4/servlet/DispatcherServlet.java
  13. 91 0
      src/main/java/cn/nosum/framework/mvc/v4/servlet/HandlerAdapter.java
  14. 42 0
      src/main/java/cn/nosum/framework/mvc/v4/servlet/HandlerMapping.java
  15. 28 0
      src/main/java/cn/nosum/framework/mvc/v4/servlet/ModelAndView.java
  16. 56 0
      src/main/java/cn/nosum/framework/mvc/v4/servlet/View.java
  17. 21 0
      src/main/java/cn/nosum/framework/mvc/v4/servlet/ViewResolver.java
  18. 118 0
      src/main/java/cn/nosum/util/StringUtils.java
  19. 2 1
      src/main/resources/application.properties
  20. 10 0
      src/main/resources/layouts/404.html
  21. 13 0
      src/main/resources/layouts/500.html
  22. 13 0
      src/main/resources/layouts/first.html
  23. 2 2
      src/main/webapp/WEB-INF/web.xml

+ 0 - 56
src/main/java/cn/nosum/demo/mvc/action/DemoAction.java

@@ -1,56 +0,0 @@
-package cn.nosum.demo.mvc.action;
-
-import java.io.IOException;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import cn.nosum.demo.service.IDemoService;
-import cn.nosum.framework.annotation.Autowired;
-import cn.nosum.framework.annotation.Controller;
-import cn.nosum.framework.annotation.RequestMapping;
-import cn.nosum.framework.annotation.RequestParam;
-
-
-//虽然,用法一样,但是没有功能
-@Controller
-@RequestMapping("/demo")
-public class DemoAction {
-
-  	@Autowired
-	private IDemoService demoService;
-
-	@RequestMapping("/query")
-	public void query(HttpServletRequest req, HttpServletResponse resp, @RequestParam("name") String name){
-		String result = demoService.get(name);
-		try {
-			resp.getWriter().write(result);
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-	}
-
-	@RequestMapping("/add")
-	public void add(HttpServletRequest req, HttpServletResponse resp, @RequestParam("a") Integer a, @RequestParam("b") Integer b){
-		try {
-			resp.getWriter().write(a + "+" + b + "=" + (a + b));
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-	}
-
-	@RequestMapping("/sub")
-	public void add(HttpServletRequest req, HttpServletResponse resp, @RequestParam("a") Double a, @RequestParam("b") Double b){
-		try {
-			resp.getWriter().write(a + "-" + b + "=" + (a - b));
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-	}
-
-	@RequestMapping("/remove")
-	public String  remove(@RequestParam("id") Integer id){
-		return "" + id;
-	}
-
-}

+ 65 - 0
src/main/java/cn/nosum/demo/mvc/action/MyAction.java

@@ -0,0 +1,65 @@
+package cn.nosum.demo.mvc.action;
+
+import cn.nosum.demo.service.IModifyService;
+import cn.nosum.demo.service.IQueryService;
+import cn.nosum.framework.annotation.Autowired;
+import cn.nosum.framework.annotation.Controller;
+import cn.nosum.framework.annotation.RequestMapping;
+import cn.nosum.framework.annotation.RequestParam;
+import cn.nosum.framework.mvc.v4.servlet.ModelAndView;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Controller
+@RequestMapping("/web")
+public class MyAction {
+
+	@Autowired
+	private IQueryService queryService;
+	@Autowired
+	private IModifyService modifyService;
+
+	@RequestMapping("/query.json")
+	public ModelAndView query(HttpServletRequest request, HttpServletResponse response,
+							  @RequestParam("name") String name){
+		String result = queryService.query(name);
+		return out(response,result);
+	}
+	
+	@RequestMapping("/add*.json")
+	public ModelAndView add(HttpServletRequest request,HttpServletResponse response,
+							@RequestParam("name") String name,@RequestParam("addr") String addr){
+		String result = modifyService.add(name,addr);
+		Map<String,String> model=new HashMap<String, String>();
+		model.put("result",result);
+		return new ModelAndView("",model);
+	}
+	
+	@RequestMapping("/remove.json")
+	public ModelAndView remove(HttpServletRequest request, HttpServletResponse response,
+							   @RequestParam("id") Integer id){
+		String result = modifyService.remove(id);
+		return out(response,result);
+	}
+	
+	@RequestMapping("/edit.json")
+	public ModelAndView edit(HttpServletRequest request,HttpServletResponse response,
+							 @RequestParam("id") Integer id, @RequestParam("name") String name){
+		String result = modifyService.edit(id,name);
+		return out(response,result);
+	}
+
+	private ModelAndView out(HttpServletResponse resp,String str){
+		try {
+			resp.getWriter().write(str);
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+}

+ 30 - 0
src/main/java/cn/nosum/demo/mvc/action/PageAction.java

@@ -0,0 +1,30 @@
+package cn.nosum.demo.mvc.action;
+
+import cn.nosum.demo.service.IQueryService;
+import cn.nosum.framework.annotation.Autowired;
+import cn.nosum.framework.annotation.Controller;
+import cn.nosum.framework.annotation.RequestMapping;
+import cn.nosum.framework.annotation.RequestParam;
+import cn.nosum.framework.mvc.v4.servlet.ModelAndView;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Controller
+@RequestMapping("/")
+public class PageAction {
+
+    @Autowired
+    IQueryService queryService;
+
+    @RequestMapping("/first.html")
+    public ModelAndView query(@RequestParam("teacher") String teacher){
+        String result = queryService.query(teacher);
+        Map<String,Object> model = new HashMap<String,Object>();
+        model.put("teacher", teacher);
+        model.put("data", result);
+        model.put("token", "123456");
+        return new ModelAndView("first.html",model);
+    }
+
+}

+ 0 - 23
src/main/java/cn/nosum/demo/mvc/action/TwoAction.java

@@ -1,23 +0,0 @@
-package cn.nosum.demo.mvc.action;
-
-import java.io.IOException;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import cn.nosum.demo.service.IDemoService;
-
-public class TwoAction {
-	
-	private IDemoService demoService;
-
-	public void edit(HttpServletRequest req,HttpServletResponse resp, String name){
-		String result = demoService.get(name);
-		try {
-			resp.getWriter().write(result);
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-	}
-	
-}

+ 21 - 0
src/main/java/cn/nosum/demo/service/IModifyService.java

@@ -0,0 +1,21 @@
+package cn.nosum.demo.service;
+
+
+public interface IModifyService {
+
+	/**
+	 * 增加
+	 */
+	public String add(String name, String addr);
+	
+	/**
+	 * 修改
+	 */
+	public String edit(Integer id, String name);
+	
+	/**
+	 * 删除
+	 */
+	public String remove(Integer id);
+	
+}

+ 10 - 0
src/main/java/cn/nosum/demo/service/IQueryService.java

@@ -0,0 +1,10 @@
+package cn.nosum.demo.service;
+
+
+public interface IQueryService {
+	
+	/**
+	 * 查询
+	 */
+	public String query(String name);
+}

+ 36 - 0
src/main/java/cn/nosum/demo/service/impl/ModifyService.java

@@ -0,0 +1,36 @@
+package cn.nosum.demo.service.impl;
+
+
+import cn.nosum.demo.service.IModifyService;
+import cn.nosum.framework.annotation.Service;
+
+/**
+ * 增删改业务
+ * @author Tom
+ *
+ */
+@Service
+public class ModifyService implements IModifyService {
+
+	/**
+	 * 增加
+	 */
+	public String add(String name,String addr) {
+		return "modifyService add,name=" + name + ",addr=" + addr;
+	}
+
+	/**
+	 * 修改
+	 */
+	public String edit(Integer id,String name) {
+		return "modifyService edit,id=" + id + ",name=" + name;
+	}
+
+	/**
+	 * 删除
+	 */
+	public String remove(Integer id) {
+		return "modifyService id=" + id;
+	}
+	
+}

+ 25 - 0
src/main/java/cn/nosum/demo/service/impl/QueryService.java

@@ -0,0 +1,25 @@
+package cn.nosum.demo.service.impl;
+
+import cn.nosum.demo.service.IQueryService;
+import cn.nosum.framework.annotation.Service;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+
+
+@Service
+public class QueryService implements IQueryService {
+
+	/**
+	 * 查询
+	 */
+	public String query(String name) {
+		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+		String time = sdf.format(new Date());
+		String json = "{\"name\":\"" + name + "\",\"time\":\"" + time + "\"}";
+		System.err.println("这是在业务方法中打印的:" + json);
+		return json;
+	}
+
+}

+ 3 - 2
src/main/java/cn/nosum/framework/beans/support/BeanDefinitionReader.java

@@ -60,10 +60,11 @@ public class BeanDefinitionReader {
                 // 如果是一个接口,则使用实现类
                 if (beanClass.isInterface()){continue;}
                 result.add(doCreateBeanDefinition(toLowerFirstCase(beanClass.getSimpleName()),beanClass.getName()));
-                // 如果有多个实现类
+                // 如果实现了多个接口,那么当前的实例就是这些接口的实现,直接注入即可
                 Class<?>[] interfaces=beanClass.getInterfaces();
                 for (Class<?> i : interfaces) {
-                    result.add(doCreateBeanDefinition(toLowerFirstCase(i.getName()),beanClass.getName()));
+                    // 这里的 name 是接口的包.类
+                    result.add(doCreateBeanDefinition(i.getName(),beanClass.getName()));
                 }
             }
         }catch (Exception e){e.printStackTrace();}

+ 5 - 6
src/main/java/cn/nosum/framework/context/ApplicationContext.java

@@ -58,7 +58,7 @@ public class ApplicationContext {
         return getBean(beanClass.getName());
     }
 
-    private Object getBean(String beanName) {
+    public Object getBean(String beanName) {
         // 读取配置信息
         BeanDefinition beanDefinition=this.beanDefinitionMap.get(beanName);
         // 将对象封装到 BeanDefinitionWrapper 中
@@ -79,21 +79,20 @@ public class ApplicationContext {
         //2、等第一次循环之后,第二次循环再检查第一次的缓存,再进行赋值
         Object instance = beanWrapper.getWrapperInstance();
         Class<?> clazz = beanWrapper.getWrappedClass();
-        //在Spring中@Component
+        // 在Spring中@Component
         if(!(clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class))){
             return;
         }
-        //把所有的包括private/protected/default/public 修饰字段都取出来
+        // 把所有的包括private/protected/default/public 修饰字段都取出来
         for (Field field : clazz.getDeclaredFields()) {
             if(!field.isAnnotationPresent(Autowired.class)){ continue; }
             Autowired autowired = field.getAnnotation(Autowired.class);
-            //如果用户没有自定义的beanName,就默认根据类型注入
+            // 如果用户没有自定义的beanName,就默认根据类型注入
             String autowiredBeanName = autowired.value().trim();
             if("".equals(autowiredBeanName)){
-                //field.getType().getName() 获取字段的类型
                 autowiredBeanName = field.getType().getName();
             }
-            //暴力访问
+            // 暴力访问
             field.setAccessible(true);
             try {
                 if(this.factoryBeanInstanceCache.get(autowiredBeanName) == null){

+ 1 - 4
src/main/java/cn/nosum/framework/mvc/v3/servlet/GPDispatcherServlet.java

@@ -19,7 +19,7 @@ import java.lang.reflect.Method;
 import java.net.URL;
 import java.util.*;
 
-public class GPDispatcherServlet extends HttpServlet {
+public class DispatcherServlet extends HttpServlet {
 
     //保存application.properties配置文件中的内容
     private Properties contextConfig = new Properties();
@@ -40,7 +40,6 @@ public class GPDispatcherServlet extends HttpServlet {
 
     @Override
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-
         //6、调用,运行阶段
         try {
             doDispatch(req, resp);
@@ -48,8 +47,6 @@ public class GPDispatcherServlet extends HttpServlet {
             e.printStackTrace();
             resp.getWriter().write("500 Exection,Detail : " + Arrays.toString(e.getStackTrace()));
         }
-
-
     }
 
     private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {

+ 153 - 0
src/main/java/cn/nosum/framework/mvc/v4/servlet/DispatcherServlet.java

@@ -0,0 +1,153 @@
+package cn.nosum.framework.mvc.v4.servlet;
+
+import cn.nosum.framework.annotation.Controller;
+import cn.nosum.framework.annotation.RequestMapping;
+import cn.nosum.framework.context.ApplicationContext;
+import cn.nosum.util.StringUtils;
+
+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.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DispatcherServlet extends HttpServlet {
+
+    private ApplicationContext applicationContext;
+    private List<HandlerMapping> handlerMappings = new ArrayList<HandlerMapping>();
+    private Map<HandlerMapping,HandlerAdapter> handlerAdapterMap=new HashMap<HandlerMapping, HandlerAdapter>();
+    private List<ViewResolver> viewResolvers=new ArrayList<ViewResolver>();
+
+
+    @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、调用,运行阶段
+        try {
+            doDispatch(req, resp);
+        } catch (Exception e) {
+            try{
+                processDispatchResult(req,resp,new ModelAndView("500"));
+            }catch (Exception e1){
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, ModelAndView modelAndView) throws Exception {
+        if(null == modelAndView){return;}
+        if(this.viewResolvers.isEmpty()){return;}
+        for (ViewResolver viewResolver : this.viewResolvers) {
+            View view = viewResolver.resolveViewName(modelAndView.getViewName());
+            // 直接往浏览器输出
+            view.render(modelAndView.getModel(),req,resp);
+            return;
+        }
+    }
+
+    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
+        // 通过 request 取得 URL,匹配 handlerMapping
+        HandlerMapping handlerMapping=getHandler(req);
+        if(handlerMapping == null){
+            processDispatchResult(req,resp,new ModelAndView("404"));
+            return;
+        }
+        HandlerAdapter handlerAdapter = getHandlerAdapter(handlerMapping);
+        ModelAndView mv = handlerAdapter.handler(req,resp,handlerMapping);
+        processDispatchResult(req,resp,mv);
+    }
+
+    private HandlerAdapter getHandlerAdapter(HandlerMapping handlerMapping) {
+        if(this.handlerAdapterMap.isEmpty()){return null;}
+        return this.handlerAdapterMap.get(handlerMapping);
+    }
+
+    private HandlerMapping getHandler(HttpServletRequest req) {
+        if(this.handlerMappings.isEmpty()){return  null;}
+        String url = req.getRequestURI();
+        String contextPath = req.getContextPath();
+        url = url.replaceAll(contextPath,"").replaceAll("/+","/");
+
+        for (HandlerMapping mapping : handlerMappings) {
+            Matcher matcher = mapping.getPattern().matcher(url);
+            if(!matcher.matches()){continue;}
+            return mapping;
+        }
+        return null;
+    }
+
+    @Override
+    public void init(ServletConfig config) throws ServletException {
+        //初始化Spring核心IoC容器
+        applicationContext = new ApplicationContext(config.getInitParameter("contextConfigLocation"));
+        //完成了IoC、DI和MVC部分对接
+        //初始化九大组件
+        initStrategies(applicationContext);
+        System.out.println("Spring framework is init.");
+    }
+
+    private void initStrategies(ApplicationContext context) {
+        //handlerMapping
+        initHandlerMappings(context);
+        //初始化参数适配器
+        initHandlerAdapters(context);
+        //初始化视图转换器
+        initViewResolvers(context);
+    }
+
+    private void initViewResolvers(ApplicationContext 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 ViewResolver(templateRoot));
+        }
+    }
+
+    private void initHandlerAdapters(ApplicationContext context) {
+        for (HandlerMapping handlerMapping : handlerMappings) {
+            this.handlerAdapterMap.put(handlerMapping,new HandlerAdapter());
+        }
+    }
+
+    private void initHandlerMappings(ApplicationContext context) {
+        if (context.getBeanDefinitionCount()==0){return;}
+        for (String beanName : this.applicationContext.getBeanDefinitionNames()) {
+            Object instance = applicationContext.getBean(beanName);
+            Class<?> clazz = instance.getClass();
+            if(!clazz.isAnnotationPresent(Controller.class)){ continue; }
+            // 提取 class 上配置的url
+            String baseUrl = "";
+            if(clazz.isAnnotationPresent(RequestMapping.class)){
+                RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
+                baseUrl = requestMapping.value();
+            }
+            //只获取public的方法
+            for (Method method : clazz.getMethods()) {
+                if(!method.isAnnotationPresent(RequestMapping.class)){continue;}
+                //提取每个方法上面配置的url
+                RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
+                String regex = ("/" + baseUrl + "/" + requestMapping.value()
+                        .replaceAll("\\*",".*")) // 将 * 替换成 .*
+                        .replaceAll("/+","/"); // 将多个 / 替换为一个
+                Pattern pattern = Pattern.compile(regex);
+                handlerMappings.add(new HandlerMapping(pattern,instance,method));
+                System.out.println("Mapped : " + regex + "," + method);
+            }
+        }
+    }
+}
+
+
+

+ 91 - 0
src/main/java/cn/nosum/framework/mvc/v4/servlet/HandlerAdapter.java

@@ -0,0 +1,91 @@
+package cn.nosum.framework.mvc.v4.servlet;
+
+
+import cn.nosum.framework.annotation.RequestParam;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class HandlerAdapter {
+
+    public ModelAndView handler(HttpServletRequest req, HttpServletResponse resp, HandlerMapping handler) throws Exception{
+
+        // 保存参数名称和参数的位置
+        Map<String,Integer> paramIndexMapping = new HashMap<String, Integer>();
+
+        // 通过运行时的状态取得方法参数的 annotation
+        Annotation[] [] pa = handler.getMethod().getParameterAnnotations();
+        for (int i = 0; i < pa.length ; i ++) {
+            for(Annotation a : pa[i]){
+                if(a instanceof RequestParam){
+                    String paramName = ((RequestParam) a).value();
+                    if(!"".equals(paramName.trim())){
+                        paramIndexMapping.put(paramName,i);
+                    }
+                }
+            }
+        }
+
+        // 初始化
+        Class<?> [] paramTypes = handler.getMethod().getParameterTypes();
+        for (int i = 0; i < paramTypes.length; i++) {
+            Class<?> paramterType = paramTypes[i];
+            if(paramterType == HttpServletRequest.class || paramterType == HttpServletResponse.class){
+                paramIndexMapping.put(paramterType.getName(),i);
+            }
+        }
+
+
+        // 拼接实参列表
+        Map<String,String[]> params = req.getParameterMap();
+        Object [] paramValues = new Object[paramTypes.length];
+        for (Map.Entry<String,String[]> param : params.entrySet()) {
+            String value = Arrays.toString(params.get(param.getKey()))
+                    .replaceAll("\\[|\\]","")
+                    .replaceAll("\\s+",",");
+            if(!paramIndexMapping.containsKey(param.getKey())){continue;}
+            int index = paramIndexMapping.get(param.getKey());
+            // 允许自定义的类型转换器Converter
+            paramValues[index] = castStringValue(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 = handler.getMethod().invoke(handler.getController(),paramValues);
+        if(result == null || result instanceof Void){return null;}
+
+        boolean isModelAndView = handler.getMethod().getReturnType() == ModelAndView.class;
+        if(isModelAndView){
+            return (ModelAndView)result;
+        }
+        return null;
+    }
+
+    private Object castStringValue(String value, Class<?> paramType) {
+        if(String.class == paramType){
+            return value;
+        }else 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;
+        }
+
+    }
+}

+ 42 - 0
src/main/java/cn/nosum/framework/mvc/v4/servlet/HandlerMapping.java

@@ -0,0 +1,42 @@
+package cn.nosum.framework.mvc.v4.servlet;
+
+import java.util.regex.Pattern;
+
+import java.lang.reflect.Method;
+
+
+public class HandlerMapping {
+    private Pattern pattern;
+    private Method method;
+    private Object controller;
+
+    public HandlerMapping(Pattern pattern, Object controller, Method method) {
+        this.pattern = pattern;
+        this.method = method;
+        this.controller = controller;
+    }
+
+    public Pattern getPattern() {
+        return pattern;
+    }
+
+    public void setPattern(Pattern pattern) {
+        this.pattern = pattern;
+    }
+
+    public Method getMethod() {
+        return method;
+    }
+
+    public void setMethod(Method method) {
+        this.method = method;
+    }
+
+    public Object getController() {
+        return controller;
+    }
+
+    public void setController(Object controller) {
+        this.controller = controller;
+    }
+}

+ 28 - 0
src/main/java/cn/nosum/framework/mvc/v4/servlet/ModelAndView.java

@@ -0,0 +1,28 @@
+package cn.nosum.framework.mvc.v4.servlet;
+
+import java.util.Map;
+
+
+public class ModelAndView {
+    private String viewName;
+    private Map<String,?> model;
+
+    public ModelAndView(String viewName, Map<String, ?> model) {
+        this.viewName = viewName;
+        this.model = model;
+    }
+
+    public ModelAndView(String viewName) {
+        this.viewName = viewName;
+    }
+
+    public String getViewName() {
+        return viewName;
+    }
+
+    public Map<String, ?> getModel() {
+        return model;
+    }
+}
+
+

+ 56 - 0
src/main/java/cn/nosum/framework/mvc/v4/servlet/View.java

@@ -0,0 +1,56 @@
+package cn.nosum.framework.mvc.v4.servlet;
+
+import cn.nosum.util.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Created by Tom.
+ */
+public class View {
+
+    private File viewFile;
+    public View(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()){
+                String paramName = matcher.group();
+                paramName = paramName.replaceAll("¥\\{|\\}","");
+                String paramValue = StringUtils.getString(model.get(paramName),"");
+                line = matcher.replaceFirst(makeStringForRegExp(paramValue));
+                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("&", "\\&");
+    }
+}

+ 21 - 0
src/main/java/cn/nosum/framework/mvc/v4/servlet/ViewResolver.java

@@ -0,0 +1,21 @@
+package cn.nosum.framework.mvc.v4.servlet;
+
+import java.io.File;
+
+
+public class ViewResolver {
+    private File templateRootDir;
+    public ViewResolver(String templateRoot) {
+        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
+        templateRootDir = new File(templateRootPath);
+    }
+
+    public View resolveViewName(String viewName){
+        if(null == viewName || "".equals(viewName.trim())){return null;}
+        String DEFAULT_TEMPLATE_SUFFIX = ".html";
+        viewName = viewName.endsWith(DEFAULT_TEMPLATE_SUFFIX)? viewName : (viewName + DEFAULT_TEMPLATE_SUFFIX);
+        File templateFile = new File((templateRootDir.getPath() + "/" + viewName).replaceAll("/+","/"));
+        return new View(templateFile);
+    }
+
+}

+ 118 - 0
src/main/java/cn/nosum/util/StringUtils.java

@@ -0,0 +1,118 @@
+package cn.nosum.util;
+
+public class StringUtils {
+    /**
+     * 首字母小写
+     */
+    public static String firstCharToLowerCase(String str) {
+        char firstChar = str.charAt(0);
+        if (firstChar >= 'A' && firstChar <= 'Z') {
+            char[] arr = str.toCharArray();
+            arr[0] += ('a' - 'A');
+            return new String(arr);
+        }
+        return str;
+    }
+    /**
+     * 首字母大写
+     */
+    public static String firstCharToUpperCase(String str) {
+        char firstChar = str.charAt(0);
+        if (firstChar >= 'a' && firstChar <= 'z') {
+            char[] arr = str.toCharArray();
+            arr[0] -= ('a' - 'A');
+            return new String(arr);
+        }
+        return str;
+    }
+
+    /**
+     * 判断是否为空
+     */
+    public static boolean isEmpty(Object str) {
+        return (str == null) || (str.toString().length() == 0);
+    }
+
+    /**
+     * 判断是否不为空
+     */
+    public static boolean isNotEmpty(final String str) {
+        return !isEmpty(str);
+    }
+
+    /**
+     * 判断是否空白
+     */
+    public static boolean isBlank(final String str) {
+        int strLen;
+        if ((str == null) || ((strLen = str.length()) == 0))
+            return true;
+        for (int i = 0; i < strLen; i++) {
+            if (!Character.isWhitespace(str.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 判断是否不是空白
+     */
+    public static boolean isNotBlank(final String str) {
+        return !isBlank(str);
+    }
+
+    /**
+     * checkValue为 null 或者为 "" 时返回 defaultValue
+     */
+    public static String getString(Object checkValue, String defaultValue) {
+        return isEmpty(checkValue) ? defaultValue : checkValue.toString();
+    }
+
+    /**
+     * 消除转义字符
+     */
+    public static String escapeXML(String str) {
+        if (str == null)
+            return "";
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < str.length(); ++i) {
+            char c = str.charAt(i);
+            switch (c) {
+                case '\u00FF':
+                case '\u0024':
+                    break;
+                case '&':
+                    sb.append("&");
+                    break;
+                case '<':
+                    sb.append("<");
+                    break;
+                case '>':
+                    sb.append(">");
+                    break;
+                case '\"':
+                    sb.append("");
+                    break;
+                case '\'':
+                    sb.append("'");
+                    break;
+                default:
+                    if (c >= '\u0000' && c <= '\u001F')
+                        break;
+                    if (c >= '\uE000' && c <= '\uF8FF')
+                        break;
+                    if (c >= '\uFFF0' && c <= '\uFFFF')
+                        break;
+                    sb.append(c);
+                    break;
+            }
+        }
+        return sb.toString();
+    }
+
+
+
+
+
+}

+ 2 - 1
src/main/resources/application.properties

@@ -1 +1,2 @@
-scanPackage=cn.nosum.demo
+scanPackage=cn.nosum.demo
+templateRoot=layouts

+ 10 - 0
src/main/resources/layouts/404.html

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="zh-cn">
+<head>
+    <meta charset="utf-8">
+    <title>页面去火星了</title>
+</head>
+<body>
+    <font size='25' color='red'>404 Not Found</font><br/><font color='green'><i>Copyright@GupaoEDU</i></font>
+</body>
+</html>

+ 13 - 0
src/main/resources/layouts/500.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="zh-cn">
+<head>
+    <meta charset="utf-8">
+    <title>服务器好像累了</title>
+</head>
+<body>
+    <font size='25' color='blue'>500 服务器好像有点累了,需要休息一下</font><br/>
+    <b>Message:¥{detail}</b><br/>
+    <b>StackTrace:¥{stackTrace}</b><br/>
+    <font color='green'><i>Copyright@GupaoEDU</i></font>
+</body>
+</html>

+ 13 - 0
src/main/resources/layouts/first.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="zh-cn">
+<head>
+	<meta charset="utf-8">
+	<title>咕泡学院SpringMVC模板引擎演示</title>
+</head>
+<center>
+	<h1>大家好,我是¥{teacher}老师<br/>欢迎大家一起来探索Spring的世界</h1>
+	<h3>Hello,My name is ¥{teacher}</h3>
+	<div>¥{data}</div>
+	Token值:¥{token}
+</center>
+</html>

+ 2 - 2
src/main/webapp/WEB-INF/web.xml

@@ -7,10 +7,10 @@
 	<display-name>Gupao Web Application</display-name>
 	<servlet>
 		<servlet-name>mvc</servlet-name>
-		<servlet-class>cn.nosum.framework.mvc.v2.servlet.DispatcherServlet</servlet-class>
+		<servlet-class>cn.nosum.framework.mvc.v4.servlet.DispatcherServlet</servlet-class>
 		<init-param>
 			<param-name>contextConfigLocation</param-name>
-			<param-value>application.properties</param-value>
+			<param-value>classpath:application.properties</param-value>
 		</init-param>
 
 		<load-on-startup>1</load-on-startup>