Spring手写简化版MVC流程详解
lin000_0 人气:0spring是一个非常流行的技术框架,其中spring mvc组件在其中非常重要的地位,主要面要客户端提供服务,我们今天来手写一个简化版的mvc,且包括ioc部分,主要利用servlet机制来实现,类的关系如下:
准备注解类,类于spring的@Autowired、@Service、@Controller、@RequestMapping、@RequestParam
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CSAutowired { String value() default ""; } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CSController { String value() default ""; } @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CSRequestMapping { String value() default ""; } @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CSRequestParam { String value() default ""; } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CSService { String value() default ""; }
准备service interface
public interface IDemoService { public String get(String name); }
准备service实现类,利用@CSService
@CSService public class DemoService implements IDemoService { @Override public String get(String name) { return "My name is "+name; } }
准备对外服务的类,主要利用@CSController注解
@CSController @CSRequestMapping("/demo") public class DemoAction { @CSAutowired private IDemoService demoService; @CSRequestMapping("/query") public void query(HttpServletRequest req, HttpServletResponse resp, @CSRequestParam("name") String name){ String result=demoService.get(name); try { resp.getWriter().write(result); } catch (IOException exception) { exception.printStackTrace(); } } @CSRequestMapping("/add") public void add(HttpServletRequest req, HttpServletResponse resp, @CSRequestParam("aa") Integer a,@CSRequestParam("b") Integer b){ try { resp.getWriter().write(a+"+"+b+"="+(a+b)); } catch (IOException exception) { exception.printStackTrace(); } } @CSRequestMapping("/remove") public void remove(HttpServletRequest req, HttpServletResponse resp, @CSRequestParam("id") Integer id){ try { resp.getWriter().write("id="+id); } catch (IOException exception) { exception.printStackTrace(); } } }
准备servlet
主要实现了以下功能:
1).根据@CSController对外服务的url如何mapping到具体方法 doHandlerMap
2).service和controller bean的管理 iocBeans
3).如何实列化bean doInstance
4).如何获取url中参数值 doDispatch中
5).找到需要加载的class doScanner
6).如何自动autowired doAutoWried
public class CSDispatchServlet extends HttpServlet { public static String urlPattern="/custom"; private void doDispatch(HttpServletRequest request,HttpServletResponse response) throws Exception{ String url=request.getRequestURI(); String contextPath=request.getContextPath(); url=url.replace(urlPattern,""); if(!handlerMap.containsKey(url)){ response.getWriter().write("404 not found!"); return; } Method method=handlerMap.get(url); Annotation[][] methodParameterAnnotations= method.getParameterAnnotations(); Parameter[] methodParameters= method.getParameters(); Annotation[][] paramerterAnnotations=method.getParameterAnnotations(); ArrayList<Object> methodParameterValues=new ArrayList<Object>(); Map<String,String[]> requestParams= request.getParameterMap(); int parmeterCnt=0; for(Parameter parameter:methodParameters){ if(parameter.getType()==HttpServletRequest.class ){ methodParameterValues.add(request); }else if(parameter.getType()==HttpServletResponse.class){ methodParameterValues.add(response); }else { String methodParamName=""; if(paramerterAnnotations[parmeterCnt].length>0) { Annotation annotation= paramerterAnnotations[parmeterCnt][0]; if(annotation instanceof CSRequestParam) { methodParamName = ((CSRequestParam) annotation).value(); } } if("".equals(methodParamName.trim())){ methodParamName=parameter.getName(); } String value=""; //String value=Arrays.toString(requestParams.get(methodParamName)); if(requestParams.get(methodParamName).length>1) value=Arrays.toString(requestParams.get(methodParamName)); else if(requestParams.get(methodParamName).length==1) value= requestParams.get(methodParamName)[0]; else value="999999"; if(parameter.getType()==String.class) methodParameterValues.add(value); else if(parameter.getType()==Integer.class) { try { methodParameterValues.add(Integer.parseInt(value)); } catch (Exception e){ methodParameterValues.add(99999999); } }else { //可以扩展复杂类型转换 } } parmeterCnt++; } String beanName=this.genBeanName(method.getDeclaringClass().getSimpleName()); method.invoke(this.iocBeans.get(beanName), methodParameterValues.toArray()); } private String genBeanName(String beanName){ if(beanName.length()>1) beanName=beanName.substring(0,0).toLowerCase()+beanName.substring(1); else beanName=beanName.toLowerCase(); return beanName; } @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 { try { this.doDispatch(req,resp); } catch (Exception exception) { exception.printStackTrace(); } } private ArrayList<String> classs=new ArrayList<String>(); private ConcurrentHashMap<String,Object> iocBeans=new ConcurrentHashMap<String,Object>(); private ConcurrentHashMap<String,Method> handlerMap=new ConcurrentHashMap<String,Method>(); private void doInstance() { try { for (String className : classs) { if (!className.contains(".")) continue; Class<?> clazz = Class.forName(className); String beanName=""; if (clazz.isAnnotationPresent(CSController.class)) { CSController controller = clazz.getAnnotation(CSController.class); beanName=controller.value(); }else if(clazz.isAnnotationPresent(CSService.class)){ CSService service=clazz.getAnnotation(CSService.class); beanName=service.value(); }else { continue; } Object instance=clazz.newInstance(); if("".equals(beanName.trim())) beanName=clazz.getSimpleName(); beanName=genBeanName(beanName); iocBeans.put(beanName,instance); if(clazz.isAnnotationPresent(CSService.class)){ for(Class c: clazz.getInterfaces()){ if(iocBeans.containsKey(c.getName())) continue; iocBeans.put(c.getName(),instance); } } } }catch (Exception e){ e.printStackTrace(); } } private void doAutoWried(){ for(Object o:iocBeans.values()){ if(o==null) continue; Class clazz=o.getClass(); if(clazz.isAnnotationPresent(CSService.class) || clazz.isAnnotationPresent(CSController.class)){ Field[] fields=clazz.getDeclaredFields(); for(Field f:fields){ if(!f.isAnnotationPresent(CSAutowired.class)) continue; CSAutowired autowired=f.getAnnotation(CSAutowired.class); String beanName=autowired.value(); if("".equals(beanName)) beanName=f.getType().getName(); f.setAccessible(true); try{ Object o1=iocBeans.get(beanName); f.set(o,iocBeans.get(beanName)); }catch (IllegalAccessException e){ e.printStackTrace(); } } } } } private void doHandlerMap(ServletConfig config){ for(Object o:iocBeans.values()){ if(!o.getClass().isAnnotationPresent(CSController.class)) continue; String baseUrl=""; if(o.getClass().isAnnotationPresent(CSRequestMapping.class)){ CSRequestMapping requestMapping=o.getClass().getAnnotation(CSRequestMapping.class); baseUrl=requestMapping.value(); } for(Method method: o.getClass().getMethods()){ if(method.isAnnotationPresent(CSRequestMapping.class)) { CSRequestMapping requestMapping=method.getAnnotation(CSRequestMapping.class); String url=baseUrl+requestMapping.value().replaceAll("/+","/"); String contextPath=config.getServletContext().getContextPath(); this.handlerMap.put(url,method); } } } } @Override public void init(ServletConfig config) throws ServletException { InputStream is=null; try{ System.out.println("custom servlet init........"); /* Properties configContext=new Properties(); is=this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("contextConfigLocation")); configContext.load(is); String scanPackage=configContext.getProperty("scanPackage"); */ Enumeration<String> enumerations= config.getInitParameterNames(); while (enumerations.hasMoreElements()){ System.out.println(enumerations.nextElement()); } doScanner("com.mesui.spring.custom"); doInstance(); doAutoWried(); doHandlerMap( config); }catch (Exception exception){ exception.printStackTrace(); }finally { } } private void doScanner(String scanPackage){ URL url= this.getClass().getClassLoader().getResource("") ; String filePath=""; try { filePath= URLDecoder.decode( url.getPath(),"UTF-8")+"/"+scanPackage.replaceAll("\\.","/"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } File classDir=new File(filePath); for(File file:classDir.listFiles()){ if(file.isDirectory()){ doScanner(scanPackage+"."+file.getName()); }else if(!file.getName().endsWith(".class")) { continue; } if(!file.isDirectory()) { String clzzName = (scanPackage + "." + file.getName().replace(".class", "")); //map.put(clzzName,null); classs.add(clzzName); } } } }
在利用spring的configuration类初始化servlet
这边为了方便进行偷懒,这样/custom/下的服务按照自已逻辑对对外服务,不按照spring mvc的进行,另外自已可以tomcat的web.xml中标记servlet完全脱离spring
@Configuration public class MybatisPlusConfig { @Bean public ServletRegistrationBean CustomServlet(){ return new ServletRegistrationBean(new CSDispatchServlet(),CSDispatchServlet.urlPattern+"/*"); } }
测试
结论
从上面的例子中我们可以看到自已写一个mvc也很方便,不是什么难事,但是这个只是用于学习,毕竟spring是一个体系,我们自已不可能将所有内容重新写一遍,但是自已写着玩有助于对spring mvc和IOC的理解。
加载全部内容