Spring手写简化版MVC流程详解
作者:lin000_0
spring是一个非常流行的技术框架,其中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的理解。
到此这篇关于Spring手写简化版mvc流程详解的文章就介绍到这了,更多相关Spring mvc内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!