Spring手寫簡(jiǎn)化版MVC流程詳解
spring是一個(gè)非常流行的技術(shù)框架,其中spring mvc組件在其中非常重要的地位,主要面要客戶端提供服務(wù),我們今天來(lái)手寫一個(gè)簡(jiǎn)化版的mvc,且包括ioc部分,主要利用servlet機(jī)制來(lái)實(shí)現(xiàn),類的關(guān)系如下:
準(zhǔn)備注解類,類于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 ""; }
準(zhǔn)備service interface
public interface IDemoService { public String get(String name); }
準(zhǔn)備service實(shí)現(xiàn)類,利用@CSService
@CSService public class DemoService implements IDemoService { @Override public String get(String name) { return "My name is "+name; } }
準(zhǔn)備對(duì)外服務(wù)的類,主要利用@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(); } } }
準(zhǔn)備servlet
主要實(shí)現(xiàn)了以下功能:
1).根據(jù)@CSController對(duì)外服務(wù)的url如何mapping到具體方法 doHandlerMap
2).service和controller bean的管理 iocBeans
3).如何實(shí)列化bean doInstance
4).如何獲取url中參數(shù)值 doDispatch中
5).找到需要加載的class doScanner
6).如何自動(dòng)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 { //可以擴(kuò)展復(fù)雜類型轉(zhuǎn)換 } } 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
這邊為了方便進(jìn)行偷懶,這樣/custom/下的服務(wù)按照自已邏輯對(duì)對(duì)外服務(wù),不按照spring mvc的進(jìn)行,另外自已可以tomcat的web.xml中標(biāo)記servlet完全脫離spring
@Configuration public class MybatisPlusConfig { @Bean public ServletRegistrationBean CustomServlet(){ return new ServletRegistrationBean(new CSDispatchServlet(),CSDispatchServlet.urlPattern+"/*"); } }
測(cè)試
結(jié)論
從上面的例子中我們可以看到自已寫一個(gè)mvc也很方便,不是什么難事,但是這個(gè)只是用于學(xué)習(xí),畢竟spring是一個(gè)體系,我們自已不可能將所有內(nèi)容重新寫一遍,但是自已寫著玩有助于對(duì)spring mvc和IOC的理解。
到此這篇關(guān)于Spring手寫簡(jiǎn)化版mvc流程詳解的文章就介紹到這了,更多相關(guān)Spring mvc內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java基于jdbc連接mysql數(shù)據(jù)庫(kù)功能實(shí)例詳解
這篇文章主要介紹了java基于jdbc連接mysql數(shù)據(jù)庫(kù)功能,結(jié)合實(shí)例形式詳細(xì)分析了jdbc連接mysql數(shù)據(jù)庫(kù)的原理、步驟、實(shí)現(xiàn)方法及相關(guān)操作技巧,需要的朋友可以參考下2017-10-10SpringBoot服務(wù)端數(shù)據(jù)校驗(yàn)過(guò)程詳解
這篇文章主要介紹了SpringBoot服務(wù)端數(shù)據(jù)校驗(yàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02解決@PathVariable參數(shù)接收不完整的問題
這篇文章主要介紹了解決@PathVariable參數(shù)接收不完整的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08Java使用雙異步實(shí)現(xiàn)將Excel的數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(kù)
在開發(fā)中,我們經(jīng)常會(huì)遇到這樣的需求,將Excel的數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(kù)中,這篇文章主要來(lái)和大家講講Java如何使用雙異步實(shí)現(xiàn)將Excel的數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(kù),感興趣的可以了解下2024-01-01springboot項(xiàng)目啟動(dòng)自動(dòng)跳轉(zhuǎn)到瀏覽器的操作代碼
這篇文章主要介紹了springboot項(xiàng)目啟動(dòng)自動(dòng)跳轉(zhuǎn)到瀏覽器的操作代碼,本文圖文實(shí)例代碼相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2024-03-03java開發(fā)分布式服務(wù)框架Dubbo調(diào)用過(guò)程
這篇文章主要為大家介紹了java開發(fā)分布式服務(wù)框架Dubbo調(diào)用過(guò)程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2021-11-11