在之前的一篇博客中已经简单的实现了spring的IOC和DI功能,本文将在之前的基础上实现mvc功能。
一 什么是MVC
MVC简单的说就是一种软件实现的设计模式,将整个系统进行分层,M(model 数据模型,业务逻辑层) 、V(view 视图层)、C(controller 控制器调度),实现应用程序的分层开发。实现原理如下图:
主要执行步骤:
1 用户在发起request请求给前端控制器;
2 控制器接收到请求后,经过一系统的过滤器,找到对应的请求处理映射;
3 根据请求映射获得请求处理适配器;
4 适配器将对请求进行处理并将处理结果(ModelAndView)返回给前端控制器;
5 前端处理器将处理结果交给视图解析器解析;
6 视图解析器将解析的结果返回给控制器;
7 控制器将结果返回给用户。
二 简单模拟实现
创建一个核心控制器(DispatcherServlet)继承HttpServlet,配置在web.xml中,并指定要初始化的参数
1 <!-- 核心servlet --> 2 <servlet> 3 <servlet-name>dispatcherServlet</servlet-name> 4 <servlet-class>org.wl.test.spring.mvc.DispatcherServlet</servlet-class> 5 <!-- 初始化参数 --> 6 <init-param> 7 <param-name>contextConfigLocation</param-name> 8 <param-value>classpath:application.properties</param-value> 9 </init-param> 10 <!-- 启动时加载 --> 11 <load-on-startup>0</load-on-startup> 12 </servlet> 13 <servlet-mapping> 14 <servlet-name>dispatcherServlet</servlet-name> 15 <url-pattern>/</url-pattern> 16 </servlet-mapping>
核心控制器,在web容器启动时执行init方法进行文件的初始化
1 public class DispatcherServlet extends HttpServlet { 2 3 private List<HandlerMapping> handlerMappingList = new ArrayList<HandlerMapping>(); 4 5 private Map<HandlerMapping, HandlerAdapter> adapterMap = new HashMap<>(); 6 7 @Override 8 public void init(ServletConfig config) throws ServletException { 9 // web.xml 配置核心servlet 获取配置的信息 10 String configFile = config.getInitParameter("contextConfigLocation"); 11 //定义一个当前上下文对象,实现基础包的扫描、IOC、DI 12 ApplicationContext context = new ApplicationContext(configFile.replace("classpath:", "")); 13 //获取扫描到的有Controller注解的类 14 List<Object> controllerList = context.getControllerList(); 15 //初始化HandlerMapping 16 initHandlerMapping(controllerList); 17 //初始化HandlerAdapter 18 initHandlerAdapter(); 19 } 20 21 22 private void initHandlerAdapter() { 23 if (handlerMappingList.size() == 0) { 24 return; 25 } 26 27 handlerMappingList.forEach(handlerMapping -> { 28 Method method = handlerMapping.getMethod(); 29 //方法的参数 <参数索引,参数名字> 30 Map<Integer, String> paramMap = new HashMap<>(); 31 32 //使用了注解参数 33 Annotation[][] annos = method.getParameterAnnotations(); 34 if(annos.length > 0){ 35 for(int i=0; i<annos.length; i++){ 36 for(Annotation anno : annos[i]){ 37 if(anno instanceof RequestParam){ 38 RequestParam requestParam = (RequestParam) anno; 39 String paramName = requestParam.value(); 40 41 paramMap.put(i, paramName); 42 } 43 } 44 } 45 } 46 //直接用的servlet参数,如HttpServletRequest 47 Class<?>[] paramTypes = method.getParameterTypes(); 48 if(paramTypes.length > 0){ 49 for(int i=0; i<paramTypes.length; i++){ 50 Class<?> typeClass = paramTypes[i]; 51 if (typeClass == HttpServletRequest.class || typeClass == HttpServletResponse.class) { 52 String paramName = typeClass.getName(); 53 54 paramMap.put(i, paramName); 55 } 56 } 57 } 58 59 HandlerAdapter handlerAdapter = new HandlerAdapter(paramMap); 60 adapterMap.put(handlerMapping, handlerAdapter); 61 }); 62 } 63 64 /** 65 * 完成请求方法与请求处理实例的映射关系 66 * @param controllerList 67 */ 68 private void initHandlerMapping(List<Object> controllerList) { 69 if(controllerList.size() == 0){ 70 return; 71 } 72 73 controllerList.forEach(controllerObj -> { 74 //类上的请求路径 75 String classRequestUrl = ""; 76 if (controllerObj.getClass().isAnnotationPresent(RequestMapping.class)) { 77 RequestMapping classRequestMapping = controllerObj.getClass().getAnnotation(RequestMapping.class); 78 if(classRequestMapping != null){ 79 classRequestUrl += urlHandler(classRequestMapping.value()); 80 } 81 } 82 //方法上的请求路径 83 Method[] methods = controllerObj.getClass().getDeclaredMethods(); 84 if(methods.length > 0){ 85 for(int i=0; i<methods.length; i++){ 86 String methodRequestUrl = ""; 87 Method method = methods[i]; 88 //必须是public修饰的方法 89 if(method.getModifiers() == Modifier.PUBLIC){ 90 if (method.isAnnotationPresent(RequestMapping.class)) { 91 RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class); 92 if(methodRequestMapping != null){ 93 methodRequestUrl += urlHandler(methodRequestMapping.value()); 94 } 95 96 String requestUrl = classRequestUrl + methodRequestUrl; 97 98 HandlerMapping handlerMapping = new HandlerMapping(); 99 handlerMapping.setMethod(method); 100 handlerMapping.setUrl(requestUrl); 101 102 handlerMapping.setControllerInstance(controllerObj); 103 handlerMappingList.add(handlerMapping); 104 } 105 } 106 107 } 108 } 109 110 }); 111 112 } 113 114 /** 115 * url处理 116 * @param url 117 * @return 118 */ 119 public String urlHandler( String url){ 120 if(!url.startsWith("/")){ 121 url = "/" + url; 122 } 123 if(url.endsWith("/")){ 124 url = url.substring(0, url.length() - 1); 125 } 126 return url; 127 } 128 129 @Override 130 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 131 this.doPost(req, resp); 132 } 133 134 @Override 135 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 136 doDispatcher(req, resp); 137 } 138 139 /** 140 * 请求处理 141 * @param req 142 * @param resp 143 */ 144 private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 145 req.setCharacterEncoding("utf-8"); 146 resp.setContentType("text/html;charset=UTF-8"); 147 148 String contextUrl = req.getContextPath(); 149 String requestUrl = req.getRequestURI(); 150 151 String url = requestUrl.replace(contextUrl, ""); 152 HandlerMapping handlerMapping = null; 153 for(int i=0; i<handlerMappingList.size(); i++){ 154 if(url.equals(handlerMappingList.get(i).getUrl())){ 155 handlerMapping = handlerMappingList.get(i); 156 break; 157 } 158 } 159 if(handlerMapping == null){ 160 resp.getWriter().write("404, 未知的请求!"); 161 }else{ 162 HandlerAdapter adapter = adapterMap.get(handlerMapping); 163 try { 164 Object result = adapter.handler(req, resp, handlerMapping); 165 166 viewResolve(req, resp, result); 167 } catch (Exception e) { 168 e.printStackTrace(); 169 resp.getWriter().write("500, 服务器发生异常!"); 170 } 171 } 172 173 } 174 175 /** 176 * 视图解析 返回 177 * @param result 178 */ 179 private void viewResolve(HttpServletRequest request, HttpServletResponse response, Object result) throws Exception{ 180 if (result.getClass() == ModelAndView.class) { 181 ModelAndView mv = (ModelAndView) result; 182 String view = mv.getViewName(); 183 Map<String, Object> dataMap = mv.getData(); 184 if(dataMap.size() > 0){ 185 for(String key : dataMap.keySet()){ 186 request.setAttribute(key, dataMap.get(key)); 187 } 188 } 189 request.getRequestDispatcher(view).forward(request, response); 190 } 191 } 192 193 }
ApplicationContext的具体实现如下,主要实现对执行资源文件的扫描,并完成IOC和DI
public class ApplicationContext { /** * 配置文件 */ private static String PROPERTIES_FILE = ""; /** * 初始化一个集合,存放扫描到的class对象 */ private List<Class<?>> classList = Collections.synchronizedList(new ArrayList<>()); /** * 初始化map 存放别名与对象实例 */ private Map<String, Object> aliasInstanceMap = new HashMap<>(); public ApplicationContext(String fileName) { PROPERTIES_FILE = fileName; try { String basePackage = getBasePackage(PROPERTIES_FILE); buildAliasInstanceMap(basePackage); doAutowired(); } catch (Exception e) { e.printStackTrace(); } } /** * 完成别名与实例的映射 */ public void buildAliasInstanceMap(String basePackage) throws Exception { scanClasses(basePackage); if(classList.size() == 0){return;} for(Class<?> clazz : classList){ if (clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class) || clazz.isAnnotationPresent(Autowired.class)) { String alias = getAlias(clazz); Object obj = aliasInstanceMap.get(alias); //如果别名实例映射关系已经存在,则给出提示 if(obj != null){ throw new Exception("alias is exist!"); }else{ aliasInstanceMap.put(alias, clazz.newInstance()); } } } System.out.println(aliasInstanceMap); } /** * 属性对象的注入 */ public void doAutowired(){ if (aliasInstanceMap.size() == 0) { return; } aliasInstanceMap.forEach((k, v)->{ Field[] fields = v.getClass().getDeclaredFields(); for(Field field : fields){ if (field.isAnnotationPresent(Autowired.class)) { String alias = ""; Autowired autowired = field.getAnnotation(Autowired.class); if(autowired != null){ //注入的对象是接口时,由于不知道接口有几个实现类,所以就必须在Autowired或者Qualifier上指定要注解的具体的实现类 if(!"".equals(autowired.value())){ alias = autowired.value(); }else{ Qualifier qualifier = field.getAnnotation(Qualifier.class); if(qualifier != null){ alias = qualifier.value(); } } } if ("".equals(alias)) { alias = getAlias(field.getType()); } Object instance = null; if(!"".equals(alias)){ instance = aliasInstanceMap.get(alias); } field.setAccessible(true); try { field.set(v, instance); } catch (IllegalAccessException e) { e.printStackTrace(); } } } }); } /** * 获取对象的别名,如果注解中配置了别名,别使用配置的别名,否则默认使用类名首字母小写 * @param clazz * @return */ public String getAlias(Class<?> clazz){ String alias = ""; Controller controller = clazz.getAnnotation(Controller.class); if(controller != null){ alias = controller.value(); } Service service = clazz.getAnnotation(Service.class); if (service != null) { alias = service.value(); } Autowired autowired = clazz.getAnnotation(Autowired.class); if(autowired != null){ alias = autowired.value(); } //注解中没有配置别名 if("".equals(alias)){ String simpleName = clazz.getSimpleName(); alias = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1); } return alias; } /** * 跟据基础包名读取包及子包中的类对象 * @param basePackage */ public void scanClasses(String basePackage){ if(basePackage == null || "".equals(basePackage)){return;} doScan(basePackage); System.out.println(classList); } private void doScan(String basePackage) { String path = basePackage.replaceAll("\\.","/"); URL url = this.getClass().getClassLoader().getResource(path); File file = new File(url.getFile()); file.listFiles(new FileFilter() { @Override public boolean accept(File childFile) { String fileName = childFile.getName(); if(childFile.isDirectory()){ //当前文件是目录,递归 扫描下级子目录下的class文件 doScan(basePackage + "." + fileName); }else{ if(fileName.endsWith(".class")){ String className = basePackage + "." + fileName.replace(".class", ""); try { Class<?> clazz = this.getClass().getClassLoader().loadClass(className); classList.add(clazz); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } return false; } }); } /** * 从配置的属性文件中读取要扫描的包 * @return */ public String getBasePackage(String fileName) throws IOException { String basePackage; Properties prop = new Properties(); InputStream in = this.getClass().getClassLoader().getResourceAsStream(fileName); prop.load(in); basePackage = prop.getProperty("basePackage"); return basePackage; } /** * 根据beanName 获取 * @param beanName * @return */ public Object getBean(String beanName){ return aliasInstanceMap.get(beanName); } /** * 获取所有标注了controller的注解 * @return */ public List<Object> getControllerList(){ List<Object> controllerList = new ArrayList<>(); if(aliasInstanceMap.size() > 0) { aliasInstanceMap.values().forEach(obj -> { if(obj.getClass().isAnnotationPresent(Controller.class)){ controllerList.add(obj); } }); } return controllerList; } public static void main(String[] args) throws Exception { String fileName = "application.properties"; ApplicationContext context = new ApplicationContext(fileName); String basePackage = context.getBasePackage(PROPERTIES_FILE); context.buildAliasInstanceMap(basePackage); context.doAutowired(); //测试 UserController controller = (UserController) context.getBean("userController"); controller.save(); } }
请求映射HandlerMapping,用来存放请求url和要执行的方法和方法所在对象实例,构建请求与请求处理的映射关系
1 public class HandlerMapping { 2 3 private String url; 4 private Method method; 5 private Object controllerInstance; 6 7 //此处省去getter和setter 8 }
处理适配器HandlerAdapter,每一个请求映射都有一个请求处理适配器来完成请求的处理(handler)
1 public class HandlerAdapter { 2 3 private Map<Integer, String> paramMap; 4 5 public HandlerAdapter(Map<Integer, String> paramMap){ 6 this.paramMap = paramMap; 7 } 8 9 public Object handler(HttpServletRequest request, HttpServletResponse response, HandlerMapping handlerMapping) throws Exception { 10 Method method = handlerMapping.getMethod(); 11 Object classInstance = handlerMapping.getControllerInstance(); 12 13 int paramNum = method.getParameterCount(); 14 Object[] paramObj = new Object[paramNum]; 15 for(int i=0; i<paramNum; i++){ 16 String paramName = paramMap.get(i); 17 if(paramName.equals(HttpServletRequest.class.getName())){ 18 paramObj[i] = request; 19 }else if(paramName.equals(HttpServletResponse.class.getName())){ 20 paramObj[i] = response; 21 } else { 22 paramObj[i] = request.getParameter(paramName); 23 } 24 } 25 Object result = method.invoke(classInstance, paramObj); 26 return result; 27 } 28 29 30 public Map<Integer, String> getParamMap() { 31 return paramMap; 32 } 33 34 public void setParamMap(Map<Integer, String> paramMap) { 35 this.paramMap = paramMap; 36 } 37 }
处理结果ModelAndView,用来存放当前请求要返回的视图和数据
1 public class ModelAndView { 2 3 private String viewName; 4 private Map<String, Object> data = new HashMap<>(); 5 6 public ModelAndView(String viewName) { 7 this.viewName = viewName; 8 } 9 10 public void addAttribute(String name, Object value){ 11 data.put(name, value); 12 } 13 14 //此处省略getter和setter 15 }
视图展示返回的结果
<html> <head> <title>Title</title> </head> <body> <h3>hello mvc...</h3> <hr/> 用 户 信 息:<%=request.getAttribute("user") %> </body> </html>
浏览器端显示信息
以上是模拟实现springmvc的主要代码,实现的源代码我已经上传在github,感兴趣的朋友可以访问
https://github.com/wlzq/spring
原文地址:https://www.cnblogs.com/love-wzy/p/10203655.html