javascript
Spring中毒太深,离开Spring我居然连最基本的接口都不会写了
來源:cnblogs.com/lonely-wolf/p/14127957.html
前言
隨著?Spring?的崛起以及其功能的完善,現在可能絕大部分項目的開發都是使用?Spring(全家桶)?來進行開發,Spring也確實和其名字一樣,是開發者的春天,Spring?解放了程序員的雙手,而等到?SpringBoot出來之后配置文件大大減少,更是進一步解放了程序員的雙手,但是也正是因為Spring家族產品的強大,使得我們習慣了面向?Spring?開發。
那么假如有一天沒有了?Spring,是不是感覺心里一空,可能一下子連最基本的接口都不會寫了,尤其是沒有接觸過Servlet編程的朋友。因為加入沒有了?Spring?等框架,那么我們就需要利用最原生的?Servlet?來自己實現接口路徑的映射,對象也需要自己進行管理。
Spring 能幫我們做什么
Spring?是為解決企業級應用開發的復雜性而設計的一款框架,Spring?的設計理念就是:簡化開發。
在?Spring?框架中,一切對象都是?bean,所以其通過面向?bean?編程(BOP),結合其核心思想依賴注入(DI)和面向切面((AOP)編程,Spring?實現了其偉大的簡化開發的設計理念。
控制反轉(IOC)
IOC?全稱為:Inversion of Control。控制反轉的基本概念是:不用創建對象,但是需要描述創建對象的方式。
簡單的說我們本來在代碼中創建一個對象是通過?new?關鍵字,而使用了?Spring?之后,我們不在需要自己去?new?一個對象了,而是直接通過容器里面去取出來,再將其自動注入到我們需要的對象之中,即:依賴注入。
也就說創建對象的控制權不在我們程序員手上了,全部交由?Spring?進行管理,程序要只需要注入就可以了,所以才稱之為控制反轉。
依賴注入(DI)
依賴注入(Dependency Injection,DI)就是?Spring?為了實現控制反轉的一種實現方式,所有有時候我們也將控制反轉直接稱之為依賴注入。
面向切面編程(AOP)
AOP?全稱為:Aspect Oriented Programming。AOP是一種編程思想,其核心構造是方面(切面),即將那些影響多個類的公共行為封裝到可重用的模塊中,而使原本的模塊內只需關注自身的個性化行為。
AOP?編程的常用場景有:Authentication(權限認證)、Auto Caching(自動緩存處理)、Error Handling(統一錯誤處理)、Debugging(調試信息輸出)、Logging(日志記錄)、Transactions(事務處理)等。
利用 Spring 來完成 Hello World
最原生的?Spring?需要較多的配置文件,而?SpringBoot?省略了許多配置,相比較于原始的?Spring?又簡化了不少,在這里我們就以?SpringBoot?為例來完成一個簡單的接口開發。
1、新建一個?maven?項目,pom?文件中引入依賴(省略了少部分屬性):
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.0</version><relativePath/> </parent> <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency> </dependencies>2、新建一個?HelloController?類:
package?com.lonely.wolf.note.springboot.demo;import?org.springframework.web.bind.annotation.GetMapping; import?org.springframework.web.bind.annotation.RequestMapping; import?org.springframework.web.bind.annotation.RestController;@RestController @RequestMapping("/hello") public?class?HelloController?{@GetMapping("/demo")public?String?helloWorld(String?name){return?"Hello:"?+?name;} }3、最后新建一個?SpringBoot?啟動類:
package?com.lonely.wolf.note.springboot;import?org.springframework.boot.SpringApplication; import?org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication(scanBasePackages?=?"com.lonely.wolf.note.springboot") class?MySpringBootApplication?{public?static?void?main(String[]?args)?{SpringApplication.run(MySpringBootApplication.class,?args);} }4、現在就可以輸入測試路徑:http://localhost:8080/hello/demo?name=雙子孤狼?進行測試,正常輸出:Hello:雙子孤狼。
我們可以看到,利用?SpringBoot?來完成一個簡單的應用開發非常簡單,可以不需要任何配置完成一個簡單的應用,這是因為?SpringBoot?內部已經做好了約定(約定優于配置思想),包括容器?Tomcat?都被默認集成,所以我們不需要任何配置文件就可以完成一個簡單的?demo?應用。
假如沒有了 Spring
通過上面的例子我們可以發現,利用?Spring?來完成一個?Hello World?非常簡單,但是假如沒有了?Spring,我們又該如何完成這樣的一個?Hello World?接口呢?
基于 Servlet 開發
在還沒有框架之前,編程式基于原始的?Servlet?進行開發,下面我們就基于原生的?Servlet?來完成一個簡單的接口調用。
1、pom?文件引入依賴,需要注意的是,package?屬性要設置成?war?包,為了節省篇幅,這里沒有列出?pom?完整的信息:
<packaging>war</packaging>? <dependencies><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.4</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.7</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.72</version></dependency> </dependencies>2、在?src/main?下面新建文件夾?webapp/WEB-INF,然后在?WEB-INF?下面新建一個?web.xml?文件:
<?xml?version="1.0"?encoding="UTF-8"?> <web-app?xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/j2ee"?xmlns:javaee="http://java.sun.com/xml/ns/javaee"xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee?http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"version="2.4"><display-name>Lonely?Wolf?Web?Application</display-name><servlet><servlet-name>helloServlet</servlet-name><servlet-class>com.lonely.wolf.mini.spring.servlet.HelloServlet</servlet-class></servlet><servlet-mapping><servlet-name>helloServlet</servlet-name><url-pattern>/hello/*</url-pattern></servlet-mapping> </web-app>這里面定義了?selvlet?和?servlet-mapping?兩個標簽,這兩個標簽必須一一對應,上面的標簽定義了?servlet?的位置,而下面的?servlet-mapping?文件定義了路徑的映射,這兩個標簽通過?servlet-name?標簽對應。
3、新建一個?HelloServlet?類繼承?HttpServlet:
package?com.lonely.wolf.mini.spring.servlet;import?javax.servlet.ServletException; import?javax.servlet.http.HttpServlet; import?javax.servlet.http.HttpServletRequest; import?javax.servlet.http.HttpServletResponse; import?java.io.IOException;/***?原始Servlet接口編寫,一般需要實現GET和POST方法,其他方法可以視具體情況選擇性繼承*/ public?class?HelloServlet?extends?HttpServlet?{@Overrideprotected?void?doGet(HttpServletRequest?request,?HttpServletResponse?response)?throws?ServletException,?IOException?{this.doPost(request,response);}@Overrideprotected?void?doPost(HttpServletRequest?request,?HttpServletResponse?response)?throws?ServletException,?IOException?{response.setContentType("text/html;charset=utf-8");response.getWriter().write("Hello:"?+?request.getParameter("name"));} }4、執行?maven?打包命令,確認成功打包成?war?包:
圖片5、RUN-->Edit Configurations,然后點擊左上角的?+?號,新建一個?Tomcat Server,如果是第一次配置,默認沒有?Tomcat Server?選項,需要點擊底部的?xx more items...:
圖片6、點擊右邊的?Deployment,然后按照下圖依次點擊,最后在彈框內找到上面打包好的?war?包文件:
圖片7、選中之后,需要注意的是,下面?Application Context?默認會帶上?war?包名,為了方便,我們需要把它刪掉,即不用上下文路徑,只保留一個根路徑?/?(當然上下文也可以保留,但是每次請求都要帶上這一部分), 再選擇?Apply,點擊?OK,即可完成部署:
圖片8、最后我們在瀏覽器輸入請求路徑http://localhost:8080/hello?name=雙子孤狼,即可得到返回:Hello:雙子孤狼。
上面我們就完成了一個簡單的 基于Servlet?的接口開發,可以看到,配置非常麻煩,每增加一個?Servlet?都需要增加對應的配置,所以才會有許多框架的出現來幫我們簡化開發,比如原來很流行的?Struts2?框架,當然現在除了一些比較老的項目,一般我們都很少使用,而更多的是選擇?Spring?框架來進行開發。
模仿Spring
Spring?的源碼體系非常龐大,大部分人對其源碼都敬而遠之。確實,Spring?畢竟經過了這么多年的迭代,功能豐富,項目龐大,不是一下子就能看懂的。雖然?Spring?難以理解,但是其最核心的思想仍然是我們上面介紹的幾點,接下來就基于?Spring?最核心的部分來模擬,自己動手實現一個超級迷你版本的?Spring(此版本并不包含?AOP?功能)。
1、pom?依賴和上面保持不變,然后?web.xml?作如下改變,這里會攔截所有的接口?/*,然后多配置了一個參數,這個參數其實也是為了更形象的模擬?Spring:
<servlet><servlet-name>myDispatcherServlet</servlet-name><servlet-class>com.lonely.wolf.mini.spring.v1.MyDispatcherServlet</servlet-class><init-param><param-name>defaultConfig</param-name><param-value>application.properties</param-value></init-param> </servlet><servlet-mapping><servlet-name>myDispatcherServlet</servlet-name><url-pattern>/*</url-pattern> </servlet-mapping>2、在?respurces?下面新建一個配置文件?application.properties,用來定義掃描的基本路徑:
basePackages=com.lonely.wolf.mini.spring3、創建一些相關的注解類:
package?com.lonely.wolf.mini.spring.annotation;import?java.lang.annotation.*;@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public?@interface?WolfAutowired?{String?value()?default?""; } package?com.lonely.wolf.mini.spring.annotation;import?java.lang.annotation.*;@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public?@interface?WolfController?{String?value()?default?""; } package?com.lonely.wolf.mini.spring.annotation;import?java.lang.annotation.*;@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public?@interface?WolfGetMapping?{String?value()?default?""; } package?com.lonely.wolf.mini.spring.annotation;import?java.lang.annotation.*;@Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public?@interface?WolfRequestParam?{String?value()?default?""; } package?com.lonely.wolf.mini.spring.annotation;import?java.lang.annotation.*;@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public?@interface?WolfService?{String?value()?default?""; }4、這個時候最核心的邏輯就是?MyDispatcherServlet?類了:
package?com.lonely.wolf.mini.spring.v1;import?com.lonely.wolf.mini.spring.annotation.*; import?com.lonely.wolf.mini.spring.v1.config.MyConfig; import?org.apache.commons.lang3.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.io.InputStream; import?java.lang.annotation.Annotation; import?java.lang.reflect.Field; import?java.lang.reflect.Method; import?java.net.URL; import?java.util.*;public?class?MyDispatcherServlet?extends?HttpServlet?{private?MyConfig?myConfig?=?new?MyConfig();private?List<String>?classNameList?=?new?ArrayList<String>();private?Map<String,Object>?iocContainerMap?=?new?HashMap<>();private?Map<String,HandlerMapping>?handlerMappingMap?=?new?HashMap<>();@Overrideprotected?void?doGet(HttpServletRequest?request,?HttpServletResponse?response)?throws?ServletException,?IOException?{this.doPost(request,response);}@Overrideprotected?void?doPost(HttpServletRequest?request,?HttpServletResponse?response)?throws?ServletException,?IOException?{try?{this.doDispatch(request,?response);}?catch?(Exception?e)?{e.printStackTrace();}}private?void?doDispatch(HttpServletRequest?request,?HttpServletResponse?response)?throws?Exception{String?requestUrl?=?this.formatUrl(request.getRequestURI());HandlerMapping?handlerMapping?=?handlerMappingMap.get(requestUrl);if?(null?==?handlerMapping){response.getWriter().write("404?Not?Found");return;}//獲取方法中的參數類型Class<?>[]?paramTypeArr?=?handlerMapping.getMethod().getParameterTypes();Object[]?paramArr?=?new?Object[paramTypeArr.length];for?(int?i=0;i<paramTypeArr.length;i++){Class<?>?clazz?=?paramTypeArr[i];//參數只考慮三種類型,其他不考慮if?(clazz?==?HttpServletRequest.class){paramArr[i]?=?request;}else?if?(clazz?==?HttpServletResponse.class){paramArr[i]?=?response;}?else?if?(clazz?==?String.class){Map<Integer,String>?methodParam?=?handlerMapping.getMethodParams();paramArr[i]?=?request.getParameter(methodParam.get(i));}else{System.out.println("暫不支持的參數類型");}}//反射調用controller方法handlerMapping.getMethod().invoke(handlerMapping.getTarget(),?paramArr);}private?String?formatUrl(String?requestUrl)?{requestUrl?=?requestUrl.replaceAll("/+","/");if?(requestUrl.lastIndexOf("/")?==?requestUrl.length()?-1){requestUrl?=?requestUrl.substring(0,requestUrl.length()?-1);}return?requestUrl;}@Overridepublic?void?init(ServletConfig?config)?throws?ServletException?{//1.加載配置文件try?{doLoadConfig(config.getInitParameter("defaultConfig"));}?catch?(Exception?e)?{System.out.println("加載配置文件失敗");return;}//2.根據獲取到的掃描路徑進行掃描doScanPacakge(myConfig.getBasePackages());//3.將掃描到的類進行初始化,并存放到IOC容器doInitializedClass();//4.依賴注入doDependencyInjection();System.out.println("DispatchServlet?Init?End..."?);}private?void?doDependencyInjection()?{if?(iocContainerMap.size()?==?0){return;}//循環IOC容器中的類Iterator<Map.Entry<String,Object>>?iterator?=?iocContainerMap.entrySet().iterator();while?(iterator.hasNext()){Map.Entry<String,Object>?entry?=?iterator.next();Class<?>?clazz?=?entry.getValue().getClass();Field[]?fields?=?clazz.getDeclaredFields();//屬性注入for?(Field?field?:?fields){//如果屬性有WolfAutowired注解則注入值(暫時不考慮其他注解)if?(field.isAnnotationPresent(WolfAutowired.class)){String?value?=?toLowerFirstLetterCase(field.getType().getSimpleName());//默認bean的value為類名首字母小寫if?(field.getType().isAnnotationPresent(WolfService.class)){WolfService?wolfService?=?field.getType().getAnnotation(WolfService.class);value?=?wolfService.value();}field.setAccessible(true);try?{Object?target?=?iocContainerMap.get(beanName);if?(null?==?target){System.out.println(clazz.getName()?+?"required?bean:"?+?beanName?+?",but?we?not?found?it");}field.set(entry.getValue(),iocContainerMap.get(beanName));//初始化對象,后面注入}?catch?(IllegalAccessException?e)?{e.printStackTrace();}}}//初始化HanderMappingString?requestUrl?=?"";//獲取Controller類上的請求路徑if?(clazz.isAnnotationPresent(WolfController.class)){requestUrl?=?clazz.getAnnotation(WolfController.class).value();}//循環類中的方法,獲取方法上的路徑Method[]?methods?=?clazz.getMethods();for?(Method?method?:?methods){//假設只有WolfGetMapping這一種注解if(!method.isAnnotationPresent(WolfGetMapping.class)){continue;}WolfGetMapping?wolfGetMapping?=?method.getDeclaredAnnotation(WolfGetMapping.class);requestUrl?=?requestUrl?+?"/"?+?wolfGetMapping.value();//拼成完成的請求路徑//不考慮正則匹配路徑/xx/*?的情況,只考慮完全匹配的情況if?(handlerMappingMap.containsKey(requestUrl)){System.out.println("重復路徑");continue;}Annotation[][]?annotationArr?=?method.getParameterAnnotations();//獲取方法中參數的注解Map<Integer,String>?methodParam?=?new?HashMap<>();//存儲參數的順序和參數名retryParam:for?(int?i=0;i<annotationArr.length;i++){for?(Annotation?annotation?:?annotationArr[i]){if?(annotation?instanceof?WolfRequestParam){WolfRequestParam?wolfRequestParam?=?(WolfRequestParam)?annotation;methodParam.put(i,wolfRequestParam.value());//存儲參數的位置和注解中定義的參數名continue?retryParam;}}}requestUrl?=?this.formatUrl(requestUrl);//主要是防止路徑多了/導致路徑匹配不上HandlerMapping?handlerMapping?=?new?HandlerMapping();handlerMapping.setRequestUrl(requestUrl);//請求路徑handlerMapping.setMethod(method);//請求方法handlerMapping.setTarget(entry.getValue());//請求方法所在controller對象handlerMapping.setMethodParams(methodParam);//請求方法的參數信息handlerMappingMap.put(requestUrl,handlerMapping);//存入hashmap}}}/***?初始化類,并放入容器iocContainerMap內*/private?void?doInitializedClass()?{if?(classNameList.isEmpty()){return;}for?(String?className?:?classNameList){if?(StringUtils.isEmpty(className)){continue;}Class?clazz;try?{clazz?=?Class.forName(className);//反射獲取對象if?(clazz.isAnnotationPresent(WolfController.class)){String?value?=?((WolfController)clazz.getAnnotation(WolfController.class)).value();//如果直接指定了value則取value,否則取首字母小寫類名作為key值存儲類的實例對象iocContainerMap.put(StringUtils.isBlank(value)???toLowerFirstLetterCase(clazz.getSimpleName())?:?value,clazz.newInstance());}else?if(clazz.isAnnotationPresent(WolfService.class)){String?value?=?((WolfService)clazz.getAnnotation(WolfService.class)).value();iocContainerMap.put(StringUtils.isBlank(value)???toLowerFirstLetterCase(clazz.getSimpleName())?:?value,clazz.newInstance());}else{System.out.println("不考慮其他注解的情況");}}?catch?(Exception?e)?{e.printStackTrace();System.out.println("初始化類失敗,className為"?+?className);}}}/***?將首字母轉換為小寫*?@param?className*?@return*/private?String?toLowerFirstLetterCase(String?className)?{if?(StringUtils.isBlank(className)){return?"";}String?firstLetter?=?className.substring(0,1);return?firstLetter.toLowerCase()?+?className.substring(1);}/***?掃描包下所有文件獲取全限定類名*?@param?basePackages*/private?void?doScanPacakge(String?basePackages)?{if?(StringUtils.isBlank(basePackages)){return;}//把包名的.替換為/String?scanPath?=?"/"?+?basePackages.replaceAll("\\.","/");URL?url?=?this.getClass().getClassLoader().getResource(scanPath);//獲取到當前包所在磁盤的全路徑File?files?=?new?File(url.getFile());//獲取當前路徑下所有文件for?(File?file?:?files.listFiles()){//開始掃描路徑下的所有文件if?(file.isDirectory()){//如果是文件夾則遞歸doScanPacakge(basePackages?+?"."?+?file.getName());}else{//如果是文件則添加到集合。因為上面是通過類加載器獲取到的文件路徑,所以實際上是class文件所在路徑classNameList.add(basePackages?+?"."?+?file.getName().replace(".class",""));}}}/***?加載配置文件*?@param?configPath?-?配置文件所在路徑*/private?void?doLoadConfig(String?configPath)?{InputStream?inputStream?=?this.getClass().getClassLoader().getResourceAsStream(configPath);Properties?properties?=?new?Properties();try?{properties.load(inputStream);}?catch?(IOException?e)?{e.printStackTrace();System.out.println("加載配置文件失敗");}properties.forEach((k,?v)?->?{try?{Field?field?=?myConfig.getClass().getDeclaredField((String)k);field.setAccessible(true);field.set(myConfig,v);}?catch?(Exception?e)?{e.printStackTrace();System.out.println("初始化配置類失敗");return;}});} }5、這個?Servlet?相比較于上面的?HelloServlet?多了一個?init?方法,這個方法中主要做了以下幾件事情:
(1)初始化配置文件,拿到配置文件中配置的參數信息(對應方法:doLoadConfig)。
(2)拿到第?1?步加載出來的配置文件,獲取到需要掃描的包路徑,然后將包路徑進行轉換成實際的磁盤路徑,并開始遍歷磁盤路徑下的所有?class?文件,最終經過轉換之后得到掃描路徑下的所有類的全限定類型,存儲到全局變量?classNameList?中(對應方法:doScanPacakge)。
(3)根據第?2?步中得到的全局變量?classNameList?中的類通過反射進行初始化(需要注意的是只會初始化加了指定注解的類)并將得到的對應關系存儲到全局變量?iocContainerMap?中(即傳說中的?IOC?容器),其中?key?值為注解中的?value?屬性,如?value?屬性為空,則默認取首字母小寫的類名作為?key?值進行存儲(對應方法:doInitializedClass)。
(4)這一步比較關鍵,需要對?IOC?容器中的所有類的屬性進行賦值并且需要對?Controller?中的請求路徑進行映射存儲,為了確保最后能順利調用?Controller?中的方法,還需要將方法的參數進行存儲 。
對屬性進行映射時只會對加了注解的屬性進行映射,映射時會從?IOC?容器中取出第?3?步中已經初始化的實例對象進行賦值,最后將請求路徑和?Controller?中方法的映射關系存入變量?handlerMappingMap,key?值為請求路徑,value?為方法的相關信息 (對應方法:doDependencyInjection)。
6、存儲請求路徑和方法的映射關系時,需要用到?HandlerMapping?類來進行存儲:
package?com.lonely.wolf.mini.spring.v1;import?java.lang.reflect.Method; import?java.util.Map;//省略了getter/setter方法 public?class?HandlerMapping?{private?String?requestUrl;private?Object?target;//保存方法對應的實例private?Method?method;//保存映射的方法private?Map<Integer,String>?methodParams;//記錄方法參數 }7、初始化完成之后,因為攔截了?/*?,所以調用任意接口都會進入?MyDispatcherServlet?,而且最終都會執行方法?doDispatch,執行這個方法時會拿到請求的路徑,然后和全局變量?handlerMappingMap?進行匹配,匹配不上則返回?404,匹配的上則取出必要的參數進行賦值,最后通過反射調用到?Controller?中的相關方法。
8、新建一個?HelloController?和?HelloService?來進行測試:
package?com.lonely.wolf.mini.spring.controller;import?com.lonely.wolf.mini.spring.annotation.WolfAutowired; import?com.lonely.wolf.mini.spring.annotation.WolfController; import?com.lonely.wolf.mini.spring.annotation.WolfGetMapping; import?com.lonely.wolf.mini.spring.annotation.WolfRequestParam; import?com.lonely.wolf.mini.spring.service.HelloService;import?javax.servlet.http.HttpServletRequest; import?javax.servlet.http.HttpServletResponse; import?java.io.IOException;@WolfController public?class?HelloController?{@WolfAutowiredprivate?HelloService?helloService;@WolfGetMapping("/hello")public?void?query(HttpServletRequest?request,HttpServletResponse?response,?@WolfRequestParam("name")?String?name)?throws?IOException?{response.setContentType("text/html;charset=utf-8");response.getWriter().write("Hello:"?+?name);} } package?com.lonely.wolf.mini.spring.service;import?com.lonely.wolf.mini.spring.annotation.WolfService;@WolfService(value?=?"hello_service")//為了演示能否正常取value屬性 public?class?HelloService?{ }9、輸入測試路徑:http://localhost:8080hello?name=雙子孤狼, 進行測試發現可以正常輸出:Hello:雙子孤狼。
上面這個例子只是一個簡單的演示,通過這個例子只是希望在沒有任何框架的情況下,我們也能知道如何完成一個簡單的應用開發。例子中很多細節都沒有進行處理,僅僅只是為了體驗一下?Spring?的核心思想,并了解?Spring?到底幫助我們做了什么,實際上?Spring?能幫我們做的事情遠比這個例子中多得多,Spring?體系龐大,設計優雅,經過了多年的迭代優化,是一款非常值得研究的框架。
總結
本文從介紹?Spring?核心功能開始入手,從如何利用?Spring?完成一個應用開發,講述到假如沒有?Spring?我們該如何基于?Servlet?進行開發,最后再通過一個簡單的例子體驗了?Spring?的核心思想。
推薦文章面試官問:前后端分離項目,有什么優缺點?我說:沒
2020 年騰訊新增 20 億行代碼,鵝廠第一編程語言還是它
通俗講解分布式鎖,看完不懂算我輸
寫博客能月入10K?
一款基于 Spring Boot 的現代化社區(論壇/問答/社交網絡/博客)
這或許是最美的Vue+Element開源后臺管理UI
推薦一款高顏值的 Spring Boot 快速開發框架
一款基于 Spring Boot 的現代化社區(論壇/問答/社交網絡/博客)
13K點贊都基于 Vue+Spring 前后端分離管理系統ELAdmin,大愛
想接私活時薪再翻一倍,建議根據這幾個開源的SpringBoot項目
總結
以上是生活随笔為你收集整理的Spring中毒太深,离开Spring我居然连最基本的接口都不会写了的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 面试官:注解@Component,@Se
- 下一篇: 刚刚字节跳动发布了1200个java岗位