自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試官:關(guān)于Spring就問這13個

開發(fā) 前端
一句話概括:Spring是一個輕量級、非入侵式的控制反轉(zhuǎn)(IoC)和面向切面(AOP)的框架。

[[373983]]

本文轉(zhuǎn)載自微信公眾號「sowhat1412」,作者sowhat1412 。轉(zhuǎn)載本文請聯(lián)系sowhat1412公眾號。

1 Spring核心組件

一句話概括:Spring是一個輕量級、非入侵式的控制反轉(zhuǎn)(IoC)和面向切面(AOP)的框架。

Spring 版本 JDK版本
1.x 1.3:引入了動態(tài)代理機制,AOP 底層就是動態(tài)代理。
2.x 1.4:正常升級
3.x 5:引入注解,Spring 3 最低版本是 Java 5 ,從此以后不叫1.x 直接叫x
4.x 6:劃時代意義的版本,開始支持 Spring Boot 1.X
5.x 8:lambda 表達式等功能

PS :目前Java 開發(fā)的標(biāo)配是 Spring5 + Spring Boot 2 + JDK 8

1.1 Spring 簡介

現(xiàn)如今的Java開發(fā)又簡稱為Spring開發(fā),Spring是Java目前第一大框架,它讓現(xiàn)有的技術(shù)更容易使用,促進良好的編程習(xí)慣,大大簡化應(yīng)用程序的開發(fā)。

因為你想啊,如果我們想實現(xiàn)某個功能,代碼量一般都是固定的,要么全自己寫,要么用已有的優(yōu)秀框架,而Spring不僅已經(jīng)給我們提供了各種優(yōu)秀組件,還提供了良好的代碼組織邏輯跟業(yè)務(wù)開發(fā)流程規(guī)范框架,它的主要優(yōu)點如下:

IOC跟DI的支持

Spring就是一個大工廠容器,可以將所有對象創(chuàng)建和依賴關(guān)系維護,交給Spring管理,Spring工廠是用于生成Bean,并且管理Bean的生命周期,實現(xiàn)高內(nèi)聚低耦合的設(shè)計理念。

AOP編程的支持

Spring提供面向切面編程,可以方便的實現(xiàn)對程序進行權(quán)限攔截、運行監(jiān)控等功能。

聲明式事務(wù)的支持

只需要通過配置就可以完成對事務(wù)的管理,而無需手動編程,以前重復(fù)的一些JDBC操作,統(tǒng)統(tǒng)不需我們再寫了。

方便程序的測試

Spring對Junit4提供支持,可以通過注解方便的測試Spring程序。

粘合劑功能

方便集成各種優(yōu)秀框架,Spring不排斥各種優(yōu)秀的開源框架,其內(nèi)部提供了對各種優(yōu)秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持。

降低 JavaEE API的使用難度

Spring 對 JavaEE 開發(fā)中非常難用的一些API(JDBC、JavaMail、遠程調(diào)用等)都提供了封裝,這些API的提供使得應(yīng)用難度大大降低。

1.2 Spring組件

Spring框架是分模塊存在,除了最核心的Spring Core Container是必要模塊之外,其他模塊都是可選,大約有20多個模塊。

Spring框架 有很多特性,這些特性由7個定義良好的模塊構(gòu)成。

  1. Spring Core:Spring核心,它是框架最基礎(chǔ)的部分,提供IOC和依賴注入DI特性。
  2. Spring Context:Spring上下文容器,它是 BeanFactory 功能加強的一個子接口。
  3. Spring Web:它提供Web應(yīng)用開發(fā)的支持。
  4. Spring MVC:它針對Web應(yīng)用中MVC思想的實現(xiàn)。
  5. Spring DAO:提供對JDBC抽象層,簡化了JDBC編碼,同時,編碼更具有健壯性。
  6. Spring ORM:它支持用于流行的ORM框架的整合,比如:Spring + Hibernate、Spring + iBatis、Spring + JDO的整合等。
  7. Spring AOP:即面向切面編程,它提供了與AOP聯(lián)盟兼容的編程實現(xiàn)。

2 IOC 跟 AOP

提到Spring永遠離不開的兩個話題就是 IOC跟AOP,這是Spring的兩大核心知識點,初學(xué)者不要被IOC、AOP、Aspect、Pointcut、Advisor這些術(shù)語嚇著了,這些術(shù)語都是無聊的人為了發(fā)論文硬造的。

2.1 IOC

Java是個面向?qū)ο蟮木幊陶Z言,一般一個應(yīng)用程序是由一組對象通過相互協(xié)作開發(fā)出的業(yè)務(wù)邏輯組成的,那么如何管理這些對象呢?抽象工廠、工廠方法設(shè)計模式可以幫我們創(chuàng)建對象,生成器模式幫我們處理對象間的依賴關(guān)系,可是這些又需要我們創(chuàng)建另一些工廠類、生成器類,我們又要而外管理這些類,增加了我們的負擔(dān)。如果程序在對象需要的時候,就能自動管理對象的聲明周期,不用我們自己再去管理Bean的聲明周期了,這樣不就實現(xiàn)解耦了么。

Spring提出了一種思想:由Spring來負責(zé)控制對象的生命周期和對象間的關(guān)系。所有的類都會在Spring容器中登記,告訴Spring這這個類是什么,需要什么,然后Spring會在系統(tǒng)運行到適當(dāng)?shù)臅r候,把你要的東西主動給你,同時也把你交給其他需要你的Bean。所有的類的創(chuàng)建、銷毀都由Spring來控制,也就是說控制對象生存周期的不再是引用它的對象,而是Spring。對于某個具體的對象而言,以前是它控制其他對象,現(xiàn)在是所有對象都被spring控制,所以這叫控制反轉(zhuǎn)(Inversion of Controller),也可以叫依賴注入 DI(Dependency Injection)。

知道大致思想后其實可以如果嘗試 自己實現(xiàn)IOC 的話就會發(fā)現(xiàn)核心就是 反射 + XML解析/注解解析 。

讀取 XML 獲取 bean 相關(guān)信息,類信息、屬性值信息。

通過反射機制獲取到目標(biāo)類的構(gòu)造函數(shù),調(diào)用構(gòu)造函數(shù),再給對象賦值。

如果想自己跟下源碼你會發(fā)現(xiàn)IOC的源碼入口是refresh(),里面包含13個提供不同功能的函數(shù),具體流程較復(fù)雜,公眾號回復(fù) IOC 獲得原圖。

2.2 Context

IOC 容器只是提供一個管理對象的空間而已,如何向容器中放入我們需要容器代為管理的對象呢?這就涉及到Spring的應(yīng)用上下文Context。

應(yīng)用上下文Context :

基于 Core 和 Beans,提供了大量的擴展,包括國際化操作(基于 JDK )、資源加載(基于 JDK properties)、數(shù)據(jù)校驗(Spring 自己封裝的數(shù)據(jù)校驗機制)、數(shù)據(jù)綁定(Spring 特有,HTTP 請求中的參數(shù)直接映射稱 POJO)、類型轉(zhuǎn)換,ApplicationContext 接口是 Context 的核心,可以理解為Bean的上下文或背景信息。

可以簡單的理解 應(yīng)用上下文 是Spring容器的一種抽象化表述,而我們常見的 ApplicationContext 本質(zhì)上就是一個維護Bean定義以及對象之間協(xié)作關(guān)系的高級接口。Spring 框架本身就提供了很多個容器的實現(xiàn),大概分為兩種類型:

  1. 一種是不常用的BeanFactory,這是最簡單的容器,只能提供基本的DI功能。
  2. 另外一種就是繼承了BeanFactory后派生而來的應(yīng)用上下文,其抽象接口也就是我們上面提到的的ApplicationContext,它能提供更多企業(yè)級的服務(wù),例如解析配置文本信息等等,這也是應(yīng)用上下文實例對象最常見的應(yīng)用場景。有了上下文對象,我們就能向容器注冊需要Spring管理的對象了。對于上下文抽象接口,Spring也為我們提供了多種類型的容器實現(xiàn),供我們在不同的應(yīng)用場景選擇。

AnnotationConfigApplicationContext:從一個或多個基于java的配置類中加載上下文定義,適用于java注解的方式。

ClassPathXmlApplicationContext:從類路徑下的一個或多個xml配置文件中加載上下文定義,適用于xml配置的方式。

FileSystemXmlApplicationContext:從文件系統(tǒng)下的一個或多個xml配置文件中加載上下文定義,也就是說系統(tǒng)盤符中加載xml配置文件。

AnnotationConfigWebApplicationContext:專門為web應(yīng)用準備的,適用于注解方式。

XmlWebApplicationContext:從web應(yīng)用下的一個或多個xml配置文件加載上下文定義,適用于xml配置方式。

工作中通過XML配置或注解 將需要管理的Bean跟Bean之間的協(xié)作關(guān)系配置好,然后利用應(yīng)用上下文對象Context加載進Spring容器,容器就能為你的程序提供你想要的對象管理服務(wù)了。比如追蹤下 ClassPathXmlApplicationContext 的底層源碼:

可以看到一個XML文件的解析就可以上延8層,可見Spring容器為了實現(xiàn)IOC進行了全面性的考慮。

2.3 AOP

如果想編碼實現(xiàn)計算器功能,我們的目標(biāo)是實現(xiàn)加減乘除的運算,可是如何在每種運算前后進行打印日志跟數(shù)字合規(guī)的校驗?zāi)亍?/p>

把日志記錄和數(shù)據(jù)校驗可重用的功能模塊分離出來,然后在程序的執(zhí)行的合適的地方動態(tài)地植入這些代碼并執(zhí)行。這樣就簡化了代碼的書寫。

業(yè)務(wù)邏輯代碼中沒有參和通用邏輯的代碼,業(yè)務(wù)模塊更簡潔,只包含核心業(yè)務(wù)代碼。實現(xiàn)了業(yè)務(wù)邏輯和通用邏輯的代碼分離,便于維護和升級,降低了業(yè)務(wù)邏輯和通用邏輯的耦合性。

圖片思路:代碼最終是要加載到內(nèi)存中實現(xiàn)new出對象,那么如果我們把可重用的功能提取出來,然后將這些通用功能在內(nèi)存中通過入的方式實現(xiàn)構(gòu)造出一個新的目標(biāo)對象不就OK了么!

Spring AOP(Aspect Oriented Programming) 恰恰提供從另一個角度來考慮程序結(jié)構(gòu)以完善面向?qū)ο缶幊蹋绻f依賴注入的目的是讓相互協(xié)作的組件保持一種較為松散的耦合狀態(tài)的話,AOP則是將遍布應(yīng)用各處的功能分離出來形成可重用的組件。在編譯期間、裝載期間或運行期間實現(xiàn)在不修改源代碼的情況下給程序動態(tài)添加功能的一種技術(shù)。從而實現(xiàn)對業(yè)務(wù)邏輯的隔離,提高代碼的模塊化能力。

AOP 的核心其實就是動態(tài)代理,如果是實現(xiàn)了接口的話就會使用 JDK 動態(tài)代理,否則使用 CGLIB 代理,主要應(yīng)用于處理一些具有橫切性質(zhì)的系統(tǒng)級服務(wù),如日志收集、事務(wù)管理、安全檢查、緩存、對象池管理等。

Spring主要提供了 Aspect 切面、JoinPoint 連接點、PointCut 切入點、Advice 增強等實現(xiàn)方式,AOP一般有5種環(huán)繞方式:

  1. 前置通知 (@Before)
  2. 返回通知 (@AfterReturning)
  3. 異常通知 (@AfterThrowing)
  4. 后置通知 (@After)
  5. 環(huán)繞通知 (@Around) :(優(yōu)先級最高)

PS :多個切面的情況下,可以通過@Order指定先后順序,數(shù)字越小,優(yōu)先級越高。

3 JDK 動態(tài)代理和 CGLIB 代理區(qū)別

JDK 動態(tài)代理 與 CGLib動態(tài)代理均是實現(xiàn)Spring AOP的基礎(chǔ),它們的實現(xiàn)方式有所不同。

3.1 JDK動態(tài)代理

特點

Interface:對于JDK動態(tài)代理,業(yè)務(wù)類需要一個Interface。

Proxy:Proxy類是動態(tài)產(chǎn)生的,這個類在調(diào)用 Proxy.newProxyInstance() 方法之后,產(chǎn)生一個Proxy類的實例。實際上,這個Proxy類也是存在的,不僅僅是類的實例,這個Proxy類可以保存在硬盤上。

Method:對于業(yè)務(wù)委托類的每個方法,現(xiàn)在Proxy類里面都不用靜態(tài)顯示出來。

InvocationHandler:這個類在業(yè)務(wù)委托類執(zhí)行時,會先調(diào)用invoke方法。invoke方法在執(zhí)行想要的代理操作,可以實現(xiàn)對業(yè)務(wù)方法的再包裝。

總結(jié):

JDK動態(tài)代理類實現(xiàn)了InvocationHandler接口,重寫的invoke方法。

JDK動態(tài)代理的基礎(chǔ)是反射機制(method.invoke(對象,參數(shù)))Proxy.newProxyInstance()

3.2 CGLib動態(tài)代理

特點:

使用字節(jié)碼處理框架ASM,其原理是通過字節(jié)碼技術(shù)為一個類創(chuàng)建子類,并在子類中采用方法攔截的技術(shù)攔截所有父類方法的調(diào)用,順勢織入橫切邏輯。

CGLib創(chuàng)建的動態(tài)代理對象性能比JDK創(chuàng)建的動態(tài)代理對象的性能高不少,但是CGLib在創(chuàng)建代理對象時所花費的時間卻比JDK多得多,所以對于單例的對象,因為無需頻繁創(chuàng)建對象,用CGLib合適,反之,使用JDK方式要更為合適一些。同時,由于CGLib由于是采用動態(tài)創(chuàng)建子類的方法,對于final方法,無法進行代理。

注意:

JDK的動態(tài)代理只可以為接口去完成操作,而 CGlib 它既可以為沒有實現(xiàn)接口的類去做代理,也可以為實現(xiàn)接口的類去做代理。

3.3 代碼實現(xiàn)部分

公共代碼 :

  1. //接口類 
  2. public interface FoodService { 
  3.     public void makeNoodle(); 
  4.     public void makeChicken(); 
  1. //實現(xiàn)接口 
  2. public class FoodServiceImpl implements FoodService { 
  3.     @Override 
  4.     public void makeNoodle() { 
  5.         System.out.println("make noodle"); 
  6.     } 
  7.  
  8.     @Override 
  9.     public void makeChicken() { 
  10.         System.out.println("make Chicken"); 
  11.     } 

jdk動態(tài)代理代碼:

  1. import java.lang.reflect.InvocationHandler; 
  2. import java.lang.reflect.Method; 
  3. import java.lang.reflect.Proxy; 
  4.  
  5. public class JDKProxyFactory implements InvocationHandler 
  6.  private Object target; 
  7.  
  8.  public JDKProxyFactory(Object target) 
  9.  { 
  10.   super(); 
  11.   this.target = target; 
  12.  } 
  13.  
  14.  // 創(chuàng)建代理對象 
  15.  public Object createProxy() 
  16.  { 
  17.   // 1.得到目標(biāo)對象的類加載器 
  18.   ClassLoader classLoader = target.getClass().getClassLoader(); 
  19.   // 2.得到目標(biāo)對象的實現(xiàn)接口 
  20.   Class<?>[] interfaces = target.getClass().getInterfaces(); 
  21.   // 3.第三個參數(shù)需要一個實現(xiàn)invocationHandler接口的對象 
  22.   Object newProxyInstance = Proxy.newProxyInstance(classLoader, interfaces, this); 
  23.   return newProxyInstance; 
  24.  } 
  25.  
  26.  // 第一個參數(shù):代理對象.一般不使用;第二個參數(shù):需要增強的方法;第三個參數(shù):方法中的參數(shù) 
  27.  @Override 
  28.  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
  29.  { 
  30.   System.out.println("這是增強方法前......"); 
  31.   Object invoke = method.invoke(target, args); 
  32.   System.out.println("這是增強方法后......"); 
  33.   return invoke; 
  34.  } 
  35.  
  36.  public static void main(String[] args) 
  37.  { 
  38.   // 1.創(chuàng)建對象 
  39.   FoodServiceImpl foodService = new FoodServiceImpl(); 
  40.   // 2.創(chuàng)建代理對象 
  41.   JDKProxyFactory proxy = new JDKProxyFactory(foodService); 
  42.   // 3.調(diào)用代理對象的增強方法,得到增強后的對象 
  43.   FoodService createProxy = (FoodService) proxy.createProxy(); 
  44.   createProxy.makeChicken(); 
  45.  } 

Cglib動態(tài)代理代碼:

  1. import net.sf.cglib.proxy.Enhancer; 
  2. import net.sf.cglib.proxy.MethodInterceptor; 
  3. import net.sf.cglib.proxy.MethodProxy; 
  4. import java.lang.reflect.Method; 
  5.  
  6. public class CglibProxyFactory implements MethodInterceptor 
  7.     //得到目標(biāo)對象 
  8.     private Object target; 
  9.     //使用構(gòu)造方法傳遞目標(biāo)對象 
  10.     public CglibProxyFactory(Object target) { 
  11.         super(); 
  12.         this.target = target; 
  13.     } 
  14.     //創(chuàng)建代理對象 
  15.     public Object createProxy(){ 
  16.         //1.創(chuàng)建Enhancer 
  17.         Enhancer enhancer = new Enhancer(); 
  18.         //2.傳遞目標(biāo)對象的class 
  19.         enhancer.setSuperclass(target.getClass()); 
  20.         //3.設(shè)置回調(diào)操作 
  21.         enhancer.setCallback(this); 
  22.         return enhancer.create(); 
  23.     } 
  24.  
  25.     //參數(shù)一:代理對象;參數(shù)二:需要增強的方法;參數(shù)三:需要增強方法的參數(shù);參數(shù)四:需要增強的方法的代理 
  26.  @Override 
  27.     public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 
  28.         System.out.println("這是增強方法前......"); 
  29.         Object invoke = methodProxy.invoke(target, args); 
  30.         System.out.println("這是增強方法后......"); 
  31.         return invoke; 
  32.     } 
  33.  
  34.     public static void main(String[] args) { 
  35.         // 1.創(chuàng)建對象 
  36.         FoodServiceImpl foodService = new FoodServiceImpl(); 
  37.         // 2.創(chuàng)建代理對象 
  38.         CglibProxyFactory proxy = new CglibProxyFactory(foodService); 
  39.         // 3.調(diào)用代理對象的增強方法,得到增強后的對象 
  40.         FoodService createProxy = (FoodService) proxy.createProxy(); 
  41.         createProxy.makeChicken(); 
  42.     } 

4. Spring AOP 和 AspectJ AOP區(qū)別

4.1 Spring AOP

Spring AOP 屬于運行時增強,主要具有如下特點:

  • 基于動態(tài)代理來實現(xiàn),默認如果使用接口的,用JDK提供的動態(tài)代理實現(xiàn),如果是方法則使用CGLIB實現(xiàn)
  • Spring AOP 需要依賴 IOC 容器來管理,并且只能作用于Spring容器,使用純Java代碼實現(xiàn)
  • 在性能上,由于Spring AOP是基于動態(tài)代理來實現(xiàn)的,在容器啟動時需要生成代理實例,在方法調(diào)用上也會增加棧的深度,使得Spring AOP的性能不如AspectJ的那么好。
  • Spring AOP致力于解決企業(yè)級開發(fā)中最普遍的AOP(方法織入)。

4.2 AspectJ

AspectJ 是一個易用的功能強大的AOP框架,屬于編譯時增強, 可以單獨使用,也可以整合到其它框架中,是 AOP 編程的完全解決方案。AspectJ需要用到單獨的編譯器ajc。

AspectJ屬于靜態(tài)織入,通過修改代碼來實現(xiàn),在實際運行之前就完成了織入,所以說它生成的類是沒有額外運行時開銷的,一般有如下幾個織入的時機:

  • 編譯期織入(Compile-time weaving):如類 A 使用 AspectJ 添加了一個屬性,類 B 引用了它,這個場景就需要編譯期的時候就進行織入,否則沒法編譯類 B。
  • 編譯后織入(Post-compile weaving):也就是已經(jīng)生成了 .class 文件,或已經(jīng)打成 jar 包了,這種情況我們需要增強處理的話,就要用到編譯后織入。
  • 類加載后織入(Load-time weaving):指的是在加載類的時候進行織入,要實現(xiàn)這個時期的織入,有幾種常見的方法

4.3 對比

Spring AOP AspectJ
在純Java中實現(xiàn) 用Java編程語言擴展實現(xiàn)
編譯器javac 一般需要ajc
只可運行時織入 支持編譯時、編譯后、加載時織入
僅支持方法級編織 可編織字段、方法、構(gòu)造函數(shù)、靜態(tài)初始值等
只可在spring管理的Bean上實現(xiàn) 可在所有域?qū)ο髮崿F(xiàn)
僅支持方法執(zhí)行切入點 支持所有切入點
比AspectJ 慢很多 速度比AOP快很多
易學(xué)習(xí)使用 比AOP更復(fù)雜
代理由目標(biāo)對象創(chuàng)建,切面應(yīng)用在代理上 執(zhí)行程序前,各方面直接織入代碼中

5. BeanFactory 和 FactoryBean

5.1 BeanFactory

  • BeanFactory 以 Factory 結(jié)尾,表示它是一個工廠類(接口),BeanFacotry 是 Spring 中比較原始的Factory。
  • BeanFactory 無法支持 Spring 的許多插件,如AOP功能、Web應(yīng)用等。ApplicationContext 接口由BeanFactory接口派生而來,提供了國際化訪問、事件傳播等多個功能。
  • BeanFactory 是 IOC 容器的核心,負責(zé)生產(chǎn)和管理 Bean 對象。

5.2 FactoryBean

  • FactoryBean 以 Bean 結(jié)尾,表示它是一個Bean。
  • FactoryBean 是工廠類接口,用戶可以通過實現(xiàn)該接口定制實例化 Bean 的邏輯。FactoryBean 接口對于 Spring 框架來說占用重要的地位,Spring自身就提供了70多個FactoryBean的實現(xiàn)。
  • 當(dāng)在IOC容器中的Bean實現(xiàn)了 FactoryBean 后,通過getBean(String BeanName)獲取到的 Bean 對象并不是 FactoryBean 的實現(xiàn)類對象,而是這個實現(xiàn)類中的 getObject() 方法返回的對象。要想獲取FactoryBean的實現(xiàn)類,就要 getBean(String &BeanName),在BeanName之前加上 &。

6. Spring生命周期

Spring IOC 初始化跟銷毀 Bean 的過程大致分為Bean定義、Bean初始化、Bean的生存期 跟 Bean的銷毀4個部分。

如果僅僅是實例化跟依賴注入當(dāng)然簡單,問題是如果我們要完成自定義的要求,Spring提供了一系列接口跟配置來完成 Bean 的初始化過程,看下整個IOC容器初始化Bean的流程。

一般情況下我們自定義Bean的初始化跟銷毀方法下面三種:

1.通過 XML 或者 @Bean配置

通過xml或者@Bean(initMethod="init", destroyMethod="destory")來實現(xiàn)。

2.使用 JSR250 規(guī)則定義的(java規(guī)范)兩個注解來實現(xiàn)

@PostConstruct: 在Bean創(chuàng)建完成,且屬于賦值完成后進行初始化,屬于JDK規(guī)范的注解。

@PreDestroy: 在bean將被移除之前進行通知,在容器銷毀之前進行清理工作。

提示:JSR是由JDK提供的一組規(guī)范。

3.通過繼承實現(xiàn)類方法

實現(xiàn)InitializingBean接口的afterPropertiesSet()方法,當(dāng)beanFactory創(chuàng)建好對象,且把bean所有屬性設(shè)置好之后會調(diào)這個方法,相當(dāng)于初始化方法。

實現(xiàn)DisposableBean的destory()方法,當(dāng)bean銷毀時會把單實例bean進行銷毀

對于單實例的bean,可以正常調(diào)用初始化和銷毀方法。對于多實例的bean,容器只負責(zé)調(diào)用時候初始化,但不會管理bean, 容器關(guān)閉時不會調(diào)用銷毀方法。

7. Spring中的設(shè)計模式

Spring 框架中廣泛使用了不同類型的設(shè)計模式,下面我們來看看到底有哪些設(shè)計模式?

  • 工廠設(shè)計模式 : Spring 使用工廠模式通過 BeanFactory、ApplicationContext 創(chuàng)建 bean 對象。
  • 代理設(shè)計模式 : Spring AOP 功能的實現(xiàn)。
  • 單例設(shè)計模式 : Spring 中的 Bean 默認都是單例的。
  • 模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 結(jié)尾的對數(shù)據(jù)庫操作的類,它們就使用到了模板模式。
  • 包裝器設(shè)計模式 : 我們的項目需要連接多個數(shù)據(jù)庫,而且不同的客戶在每次訪問中根據(jù)需要會去訪問不同的數(shù)據(jù)庫。這種模式讓我們可以根據(jù)客戶的需求能夠動態(tài)切換不同的數(shù)據(jù)源。
  • 觀察者模式: Spring 事件驅(qū)動模型就是觀察者模式很經(jīng)典的一個應(yīng)用。
  • 適配器模式 :Spring AOP 的增強或通知(Advice)使用到了適配器模式、spring MVC 中也是用到了適配器模式適配Controller。

8. Spring循環(huán)依賴

8.1 簡說循環(huán)依賴

Spring循環(huán)依賴:說白了就是一個或多個對象實例之間存在直接或間接的依賴關(guān)系,這種依賴關(guān)系構(gòu)成了構(gòu)成一個環(huán)形調(diào)用。發(fā)生循環(huán)依賴的兩個前提條件是:

出現(xiàn)循環(huán)依賴的Bean必須要是單例(singleton),如果依賴prototype則完全不會有此需求。

依賴注入的方式不能全是構(gòu)造器注入的方式,只能解決setter方法的循環(huán)依賴,這是錯誤的。

假設(shè)AB之間相互依賴,通過嘗試不同的注入方式注入后可的如下結(jié)論:

依賴情況 依賴注入方式 問題解決
AB循環(huán)依賴 均采用setter方法注入
AB循環(huán)依賴 均采用屬性自動注入
AB循環(huán)依賴 均采用構(gòu)造器注入
AB循環(huán)依賴 A中注入B的方式為setter方法,B中注入A的方式為構(gòu)造器
AB循環(huán)依賴 B中注入A的方式為setter方法,A中注入B的方式為構(gòu)造器
 

PS:第四種可以而第五種不可以的原因是 Spring在創(chuàng)建Bean時默認會根據(jù)自然排序進行創(chuàng)建,所以A會先于B進行創(chuàng)建。

8.2 循環(huán)依賴通俗說

Spring通過三級緩存解決了循環(huán)依賴。

一級緩存 : Map<String,Object> singletonObjects,單例池,用于保存實例化、注入、初始化完成的bean實例

二級緩存 : Map<String,Object> earlySingletonObjects,早期曝光對象,用于保存實例化完成的bean實例

三級緩存 : Map<String,ObjectFactory<?>> singletonFactories,早期曝光對象工廠,用于保存bean創(chuàng)建工廠,以便于后面擴展有機會創(chuàng)建代理對象。

當(dāng)A、B兩個類發(fā)生循環(huán)引用時,在A完成實例化后,就使用實例化后的對象去創(chuàng)建一個對象工廠,并添加到三級緩存中,如果A被AOP代理,那么通過這個工廠獲取到的就是A代理后的對象,如果A沒有被AOP代理,那么這個工廠獲取到的就是A實例化的對象。當(dāng)A進行屬性注入時,會去創(chuàng)建B,同時B又依賴了A,所以創(chuàng)建B的同時又會去調(diào)用getBean(a)來獲取需要的依賴,此時的getBean(a)會從緩存中獲?。?/p>

  • 第一步,先獲取到三級緩存中的工廠。
  • 第二步,調(diào)用對象工工廠的getObject方法來獲取到對應(yīng)的對象,得到這個對象后將其注入到B中。緊接著B會走完它的生命周期流程,包括初始化、后置處理器等。

當(dāng)B創(chuàng)建完后,會將B再注入到A中,此時A再完成它的整個生命周期。至此循環(huán)依賴結(jié)束!

8.2 三級緩存意義何在?

先跟蹤下源碼(如上圖),跟蹤過程中注意區(qū)別下有AOP的依賴跟沒有AOP的依賴兩種情況,跟蹤后你會發(fā)現(xiàn)三級緩存的功能是只有真正發(fā)生循環(huán)依賴的時候,才去提前生成代理對象,否則只會創(chuàng)建一個工廠并將其放入到三級緩存中,但是不會去通過這個工廠去真正創(chuàng)建對象。至于提速這一說法,還是拉閘吧。

如上圖所示,如果使用二級緩存解決循環(huán)依賴,意味著所有Bean在實例化后就要完成AOP代理,這樣違背了Spring設(shè)計的原則,Spring在設(shè)計之初就是通過AnnotationAwareAspectJAutoProxyCreator這個后置處理器來在Bean生命周期的最后一步來完成AOP代理,而不是在實例化后就立馬進行AOP代理。

9. Spring事務(wù)

Spring 事務(wù)的本質(zhì)其實就是數(shù)據(jù)庫對事務(wù)的支持,沒有數(shù)據(jù)庫的事務(wù)支持,spring是無法提供事務(wù)功能的。Spring只提供統(tǒng)一事務(wù)管理接口,具體實現(xiàn)都是由各數(shù)據(jù)庫自己實現(xiàn),數(shù)據(jù)庫事務(wù)的提交和回滾是通過binlog或者undolog實現(xiàn)的,具體流程在MySQL中講過了。Spring會在事務(wù)開始時,根據(jù)當(dāng)前環(huán)境中設(shè)置的隔離級別,調(diào)整數(shù)據(jù)庫隔離級別,由此保持一致。

9.1 Spring事務(wù)的種類

Spring 支持編程式事務(wù)管理和聲明式事務(wù)管理兩種方式:

1.編程式事務(wù)

編程式事務(wù)管理使用TransactionTemplate。

2.聲明式事務(wù)

聲明式事務(wù)管理建立在AOP之上的。其本質(zhì)是通過AOP功能,對方法前后進行攔截,將事務(wù)處理的功能編織到攔截的方法中,也就是在目標(biāo)方法開始之前啟動一個事務(wù),在執(zhí)行完目標(biāo)方法之后根據(jù)執(zhí)行情況提交或者回滾事務(wù)。

優(yōu)點是不需要在業(yè)務(wù)邏輯代碼中摻雜事務(wù)管理的代碼,只需在配置文件中做相關(guān)的事務(wù)規(guī)則聲明或通過@Transactional注解的方式,便可以將事務(wù)規(guī)則應(yīng)用到業(yè)務(wù)邏輯中,減少業(yè)務(wù)代碼的污染。唯一不足地方是,最細粒度只能作用到方法級別,無法做到像編程式事務(wù)那樣可以作用到代碼塊級別。

9.2 Spring的事務(wù)傳播機制

spring事務(wù)的傳播機制說的是,當(dāng)多個事務(wù)同時存在的時候,spring如何處理這些事務(wù)的行為。事務(wù)傳播機制實際上是使用簡單的ThreadLocal實現(xiàn)的,所以,如果調(diào)用的方法是在新線程調(diào)用的,事務(wù)傳播實際上是會失效的。

  • propagation_requierd:如果當(dāng)前沒有事務(wù),就新建一個事務(wù),如果已存在一個事務(wù)中,加入到這個事務(wù)中,這是最常見的選擇,也是默認模式,它適合于絕大多數(shù)情況。
  • propagation_supports:支持當(dāng)前事務(wù),如果沒有當(dāng)前事務(wù),就以非事務(wù)方法執(zhí)行。
  • propagation_mandatory:使用當(dāng)前事務(wù),如果沒有當(dāng)前事務(wù),就拋出異常。
  • propagation_required_new:新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。
  • propagation_not_supported:以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。
  • propagation_never:以非事務(wù)方式執(zhí)行操作,如果當(dāng)前事務(wù)存在則拋出異常。
  • propagation_nested:如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則執(zhí)行與propagation_required類似的操作

9.2 Spring的事務(wù)隔離級別

TransactionDefinition接口中定義了五個表示隔離級別的常量,這里其實關(guān)鍵還是要看MySQL的隔離級別:

  • ISOLATION_DEFAULT:使用后端數(shù)據(jù)庫默認的隔離界別,MySQL默認可重復(fù)讀,Oracle默認讀已提交。
  • ISOLATION_READ_UNCOMMITTED:讀未提交。
  • ISOLATION_READ_COMMITTED:讀已提交。
  • ISOLATION_REPEATABLE_READ:可重復(fù)讀。
  • ISOLATION_SERIALIZABLE:串行化。

10. Spring MVC

10.1 什么是 MVC ?

圖片MVC模式中M是指業(yè)務(wù)模型,V是指用戶界面,C則是控制器,使用MVC的目的是將 M 和 V 的實現(xiàn)代碼分離,開發(fā)中一般將應(yīng)用程序分為 Controller、Model、View 三層,Controller 接收客戶端請求,調(diào)用 Model 生成業(yè)務(wù)數(shù)據(jù),傳遞給 View,View最終展示前端結(jié)果。

Spring MVC 就是對上述這套流程的封裝,屏蔽了很多底層代碼,開放出接口,讓開發(fā)者可以更加輕松、便捷地完成基于 MVC 模式的 Web 開發(fā)。

10.2 Spring 跟 Spring MVC關(guān)系

最開始只有Spring,只提供IOC跟AOP核心功能,后來出了亂七八糟的比如MVC、Security、Boot 等。原來的Spring 就變成了現(xiàn)在的Spring Core,MVC指的是Web的MVC框架。

Spring MVC 就是一個MVC框架,其實大范圍上來說屬于Spring,Spring MVC是一個類似于Struts的MVC模式的WEB開發(fā)框架,Spring MVC是基于 Spring 功能之上添加的 Web 框架,Spring 跟 SpringMVC可以理解為父子容器的關(guān)系,想用 Spring MVC 必須先依賴Spring。

Spring MVC 是控制層,用來接收前臺傳值,調(diào)用service層和持久層,返回數(shù)據(jù)再通過 Spring MVC把數(shù)據(jù)返回前臺

10.3 Spring MVC 的核心組件

DispatcherServlet:前置控制器,是整個流程控制的核心,控制其他組件的執(zhí)行,進行統(tǒng)一調(diào)度,降低組件之間的耦合性,相當(dāng)于總指揮。

Handler:處理器,完成具體的業(yè)務(wù)邏輯,相當(dāng)于 Servlet 或 Action。

HandlerMapping:DispatcherServlet 接收到請求之后,通過 HandlerMapping 將不同的請求映射到不同的 Handler。

HandlerInterceptor:處理器攔截器,是一個接口,如果需要完成一些攔截處理,可以實現(xiàn)該接口。

HandlerExecutionChain:處理器執(zhí)行鏈,包括兩部分內(nèi)容:Handler 和 HandlerInterceptor(系統(tǒng)會有一個默認的 HandlerInterceptor,如果需要額外設(shè)置攔截,可以添加攔截器)。

HandlerAdapter:處理器適配器,Handler 執(zhí)行業(yè)務(wù)方法之前,需要進行一系列的操作,包括表單數(shù)據(jù)的驗證、數(shù)據(jù)類型的轉(zhuǎn)換、將表單數(shù)據(jù)封裝到 JavaBean 等,這些操作都是由 HandlerApater 來完成,開發(fā)者只需將注意力集中業(yè)務(wù)邏輯的處理上,DispatcherServlet 通過 HandlerAdapter 執(zhí)行不同的 Handler。

ModelAndView:裝載了模型數(shù)據(jù)和視圖信息,作為 Handler 的處理結(jié)果,返回給 DispatcherServlet。

ViewResolver:視圖解析器,DispatcheServlet 通過它將邏輯視圖解析為物理視圖,最終將渲染結(jié)果響應(yīng)給客戶端。

10.4 Spring MVC 的工作流程

  • DispatcherServlet 表示前置控制器,是整個SpringMVC的控制中心。用戶發(fā)出請求,接收請求并攔截請求。
  • HandlerMapping 為處理器映射。DispatcherServlet調(diào)用 HandlerMapping,HandlerMapping根據(jù)請求url查找Handler。
  • HandlerExecution 表示具體的Handler,其主要作用是根據(jù)url查找控制器,如上url被查找控制器為:hello。
  • HandlerExecution 將解析后的信息傳遞給 DispatcherServlet,如解析控制器映射等。
  • HandlerAdapter 表示處理器適配器,其按照特定的規(guī)則去執(zhí)行Handler。
  • Handler 讓具體的 Controller 執(zhí)行。
  • Controller 將具體的執(zhí)行信息返回給 HandlerAdapter,如ModelAndView。
  • HandlerAdapte r將視圖邏輯名或模型傳遞給 DispatcherServlet。
  • DispatcherServlet 調(diào)用視圖解析器(ViewResolver)來解析 HandlerAdapter 傳遞的邏輯視圖名。
  • 視圖解析器將解析的邏輯視圖名傳給 DispatcherServlet。
  • DispatcherServlet 根據(jù)視圖解析器解析的視圖結(jié)果,調(diào)用具體的視圖。
  • 最終視圖呈現(xiàn)給用戶。

Spring MVC 雖然整體流程復(fù)雜,但是實際開發(fā)中很簡單,大部分的組件不需要開發(fā)者創(chuàng)建跟管理,只需要通過配置文件的方式完成配置即可,真正需要開發(fā)者進行處理的只有 Handler 、View 、Modle。

但是隨著前后端分離跟微服務(wù)的發(fā)展,一包走天下的開發(fā)模式其實用的不是很多了,大部分情況下是 SpringBoot + Vue。

11. Spring Boot

11.1 Spring Boot簡介

Spring Boot 基于 Spring 開發(fā),Spirng Boot 本身并不提供 Spring 框架的核心特性以及擴展功能,只是用于快速、敏捷地開發(fā)新一代基于 Spring 框架的應(yīng)用程序。它并不是用來替代 Spring 的解決方案,而是和 Spring 框架緊密結(jié)合用于提升 Spring 開發(fā)者體驗的工具。Spring Boot 以約定大于配置核心思想開展工作,相比Spring具有如下優(yōu)勢:

Spring Boot 可以建立獨立的Spring應(yīng)用程序。

Spring Boot 內(nèi)嵌了如Tomcat,Jetty和Undertow這樣的容器,也就是說可以直接跑起來,用不著再做部署工作了。

Spring Boot 無需再像Spring那樣搞一堆繁瑣的xml文件的配置。

Spring Boot 可以自動配置(核心)Spring。SpringBoot將原有的XML配置改為Java配置,將bean注入改為使用注解注入的方式(@Autowire),并將多個xml、properties配置濃縮在一個appliaction.yml配置文件中。

Spring Boot 提供了一些現(xiàn)有的功能,如量度工具,表單數(shù)據(jù)驗證以及一些外部配置這樣的一些第三方功能。

Spring Boot 整合常用依賴(開發(fā)庫,例如spring-webmvc、jackson-json、validation-api和tomcat等),提供的POM可以簡化Maven的配置。當(dāng)我們引入核心依賴時,SpringBoot會自引入其他依賴。

11.2 SpringBoot 注意點

1.SpringBoot 抽離

  • 將所有的功能場景都抽取出來,做成一個個的starter,spring-boot-starter-xxx 就是spring-boot的場景啟動器。只需要在項目中引入這些starter即可,所有相關(guān)的依賴都會導(dǎo)入進來 。

2.自動配置原理

  1. SpringBoot在啟動的時候從類路徑下的 META-INF/spring.factories 中獲取 EnableAutoConfiguration 指定的值
  2. 我們看我們需要的功能有沒有在SpringBoot默認寫好的自動配置類 xxxxAutoConfigurartion 當(dāng)中。
  3. 我們再來看這個自動配置類中到底配置了哪些組件;(只要我們要用的組件存在在其中,我們就不需要再手動配置了)。
  4. 給容器中自動配置類添加組件的時候,會從 xxxxProperties 類中獲取某些屬性。我們只需要在配置文件中指定這些屬性的值即可。

3.各種組件的整合

  • 比如整合MyBatis、Redis、Swagger、Security、Shrio、Druid等,百度教程即可。

11.3 Springboot啟動原理的底層

SpringApplication這個類主要做了以下四件事情:

  • 推斷應(yīng)用的類型是普通的項目還是Web項目
  • 查找并加載所有可用初始化器 , 設(shè)置到initializers屬性中
  • 找出所有的應(yīng)用程序監(jiān)聽器,設(shè)置到listeners屬性中
  • 推斷并設(shè)置main方法的定義類,找到運行的主類

SpringBoot啟動大致流程如下(源網(wǎng)侵刪):

11.3 架構(gòu)演進

單體應(yīng)用

傳統(tǒng)項目把所有的業(yè)務(wù)功能在一個項目中,這種單體架構(gòu)結(jié)構(gòu)邏輯比較簡單,對于小型項目來說很實用。但隨著業(yè)務(wù)量的增多,邏輯越來越復(fù)雜,項目會逐漸變得非常龐大,邏輯也會變得混亂,給維護和開發(fā)造成比較大的困難。

垂直架構(gòu)

把原來比較大的單體項目根據(jù)業(yè)務(wù)邏輯拆分成多個小的單體項目,比如把物流系統(tǒng)、客戶關(guān)系系統(tǒng)從原來的電子商城系統(tǒng)中抽離出來,構(gòu)建成兩個小的項目。這種架構(gòu)雖然解決了原來單體項目過大的問題,但也帶來了數(shù)據(jù)冗余、耦合性大的問題。

SOA架構(gòu)

面向服務(wù)架構(gòu)(Service Oriented Architecture),它在垂直架構(gòu)的基礎(chǔ)上,把原來項目中的公共組件抽離出來做成形成服務(wù),為各個系統(tǒng)提供服務(wù)。服務(wù)層即抽取出的公共組件。系統(tǒng)層的多個小型系統(tǒng)通過ESB企業(yè)服務(wù)總線(它是項目與服務(wù)之間通信的橋梁)以及Webservice調(diào)用服務(wù),完成各自的業(yè)務(wù)邏輯。但是SOA架構(gòu)抽取的粒度比較粗糙,系統(tǒng)與服務(wù)之間的耦合性很大,系統(tǒng)與服務(wù)界限不夠清晰,給開發(fā)和維護造成一定的困難。

微服務(wù)架構(gòu)

微服務(wù)架構(gòu)對服務(wù)的抽取粒度更小,把系統(tǒng)中的服務(wù)層完全隔離出來。遵循單一原則,系統(tǒng)層和服務(wù)層的界限清晰,各個系統(tǒng)通過服務(wù)網(wǎng)關(guān)調(diào)用所需微服務(wù)。微服務(wù)之間通過RESTful等輕量級協(xié)議傳輸,相比ESB更輕量。但這種架構(gòu)的開發(fā)成本比較高(因為新增了容錯、分布式事務(wù)等要求),對團隊的要求比較大,所以不適合小項目、小團隊。

框架演變

從一個復(fù)雜應(yīng)用場景衍生一種規(guī)范框架,用戶只需要進行各種配置而不需要自己去實現(xiàn)它,這時候強大的配置功能成了優(yōu)點。發(fā)展到一定程度之后,人們根據(jù)實際生產(chǎn)應(yīng)用情況,選取其中實用功能和設(shè)計精華,重構(gòu)出一些輕量級的框架。之后為了提高開發(fā)效率,嫌棄原先的各類配置過于麻煩,于是開始提倡約定大于配置的方案,進而衍生出一些一站式的解決方案。

12. Spring Cloud

微服務(wù)的定義 :

2014 年 Martin Fowler 提出的一種新的架構(gòu)形式。微服務(wù)架構(gòu)是一種架構(gòu)模式,提倡將單一應(yīng)用程序劃分成一組小的服務(wù),服務(wù)之間相互協(xié)調(diào),互相配合,為用戶提供最終價值。每個服務(wù)運行在其獨立的進程中,服務(wù)與服務(wù)之間采用輕量級的通信機制(如HTTP或Dubbo)互相協(xié)作,每個服務(wù)都圍繞著具體的業(yè)務(wù)進行構(gòu)建,并且能夠被獨立的部署到生產(chǎn)環(huán)境中,另外,應(yīng)盡量避免統(tǒng)一的,集中式的服務(wù)管理機制,對具體的一個服務(wù)而言,應(yīng)根據(jù)業(yè)務(wù)上下文,選擇合適的語言、工具(如Maven)對其進行構(gòu)建。

微服務(wù)化的核心就是將傳統(tǒng)的一站式應(yīng)用,根據(jù)業(yè)務(wù)拆分成一個一個的服務(wù),徹底地去耦合,每一個微服務(wù)提供單個業(yè)務(wù)功能的服務(wù),一個服務(wù)做一件事情,從技術(shù)角度看就是一種小而獨立的處理過程,類似進程的概念,能夠自行單獨啟動或銷毀,擁有自己獨立的數(shù)據(jù)庫。

微服務(wù)時代核心問題(問題根本:網(wǎng)絡(luò)不可靠):

  • 服務(wù)很多,客戶端怎么訪問,如何提供對外網(wǎng)關(guān)?
  • 這么多服務(wù),服務(wù)之間如何通信? HTTP還是RPC?
  • 這么多服務(wù),如何治理? 服務(wù)的注冊跟發(fā)現(xiàn)。
  • 服務(wù)掛了怎么辦?熔斷機制。

主流微服務(wù)框架:

  • Spring Cloud Netflix
  • Spring Cloud Alibaba
  • Spring + Dubbo + ZooKeeper

關(guān)于 SpringCloud Netflix 前面詳細寫過,在此不再重復(fù)。

13. 常用注解

感覺Spring這塊沒啥特別好寫的不知道為啥,可能跟自己用的少也有關(guān)吧,最后來幾個簡單注解收尾,一般有Spring核心注解、SpringBoot注解、SpringCloud注解、任務(wù)執(zhí)行跟調(diào)度注解、測試注解、JPA注解、SpringMVC跟REST注解等等,這里只羅列下幾個核心注解(全部注解公眾號回復(fù)注解):

  • @Component : 可以配置CommandLineRunner使用,當(dāng)一個組件不好歸屬到下面類的時候會用該注解標(biāo)注,@Controller 、@Service、 @Repository 屬于 Component的細化。
  • @Autowired :自動導(dǎo)入依賴的Bean,默認byType,完成屬性,方法的組裝,可以對類成員變量,方法,構(gòu)造函數(shù)進行標(biāo)注,加上(required=false)時找不到也不報錯
  • @Import : 跟@Bean類似,更靈活的導(dǎo)入其他配置類。ImportSelector、ImportBeanDefinitionRegistrar等
  • @Bean : 等價xml中配置的bean, 用在方法上哦,來生產(chǎn)出一個bean,然后交給Spring管理
  • @Value : 可用在字段,構(gòu)造器參數(shù)跟方法參數(shù),指定一個默認值,支持 #{} 跟 ${} 兩個方式。一般將SpringbBoot中的application.properties 配置的屬性值賦值給變量。
  • @Configuration : 等價于Spring的XML配置文件,使用Java代碼可以檢查類型安全。如果有些時候必須用到xml配置文件,可通過@Configuration 類作為項目的配置主類,使用@ImportResource注解加載xml 文件
  • @Qualifier : 該注解通常跟@Autowired一起使用,當(dāng)想對注入的過程做更多的控制,@Qualifier可幫助配置,比如兩個以上相同類型的Bean時 Spring無法抉擇,用到此注解

14. 參考

MVC 常見面試題:https://blog.csdn.net/a745233700/article/details/80963758

Spring 常見面試題:https://thinkwon.blog.csdn.net/article/details/104397516

 

責(zé)任編輯:武曉燕 來源: sowhat1412
相關(guān)推薦

2020-04-20 08:35:48

HTTP HTTPS網(wǎng)絡(luò)協(xié)議

2021-03-25 08:45:15

MySQL

2021-04-06 06:07:37

ZAB 協(xié)議原子廣播協(xié)議網(wǎng)絡(luò)協(xié)議

2024-03-28 10:37:44

IoC依賴注入依賴查找

2023-11-27 08:17:05

SpringJava

2021-07-09 10:11:34

Redis云數(shù)據(jù)技術(shù)

2022-05-23 08:43:02

BigIntJavaScript內(nèi)置對象

2015-08-13 10:29:12

面試面試官

2023-09-26 07:49:11

AOP代理spring

2023-02-16 08:10:40

死鎖線程

2021-03-22 17:20:48

MYSQL開發(fā)數(shù)據(jù)庫

2021-04-30 20:25:20

Spring MVCJava代碼

2023-07-11 08:50:34

2024-09-24 10:28:22

2020-04-03 14:05:10

面試RedisJava

2019-08-23 09:20:35

Spring 5編程Java

2019-05-29 17:20:07

Spring設(shè)計模式Java

2024-11-14 14:53:04

2010-11-12 10:44:16

面試

2024-03-18 14:06:00

停機Spring服務(wù)器
點贊
收藏

51CTO技術(shù)棧公眾號