沉淀再出發:Spring的架構理解

沉淀再出發:Spring的架構理解

一、前言

??? 在Spring之前使用的EJB框架太龐大和重量級了,開發成本很高,由此spring應運而生。關于Spring,學過java的人基本上都會慢慢接觸到,并且在面試的時候也是經常遇到的,因為這個技術極大地方便了我們的開發和部署,并且由此衍生出來的框架和思想在很多地方都有使用,比如Spring mvc,Spring boot,Spring cloud等等框架以及IoC和AOP這兩個Spring最本質的思想。可以說一切的本質都是為了高內聚,松耦合,將不同的事物之間盡量的分割清楚,通過某一種額外的比較好修改的文檔來記錄這兩者的聯系,然后通過框架來將兩者在聯系起來,這樣做看似麻煩實則便于我們擴展、維護和變更,非常的方便,并且把冗余的代碼動態的裝飾接入點,更加方便了代碼的擴展、可維護性和可讀性,只需要一個代理即可。在這兩種思想之上,spring又構建了一些列的實現方式,通過IoC的Beans組件,以及Context這個讓Beans運行的上下文,以及Core這個Beans的常用工具,使得一切都以Beans為核心,搭建出了最本質的框架,然后由這三者支撐了更加頂層的、復雜的技術,極大地方便了框架的擴展和維護。這也是spring長盛不衰的原因了。

二、Spring的框架

??? Spring是一個開源的輕量級Java SE/Java EE開發應用框架,其目的是用于簡化企業級應用程序開發。在傳統應用程序開發中,一個完整的應用是由一組相互協作的對象組成。所以開發一個應用除了要開發業務邏輯之外,最多的是關注如何使這些對象協作來完成所需功能,而且要低耦合、高內聚。業務邏輯開發是不可避免的,那如果有個框架出來幫我們來創建對象及管理這些對象之間的依賴關系。雖然“抽象工廠、工廠方法設計模式”可以幫我們創建對象,“生成器模式”可以幫我們處理對象間的依賴關系,但是這些又需要我們創建另一些工廠類、生成器類,另外管理這些類,增加了我們的負擔,如果能通過配置方式來創建對象,管理對象之間依賴關系,我們不需要通過工廠和生成器來創建及管理對象之間的依賴關系,這樣我們就能減少許多工作,加速開發,能節省出很多時間來干其他事。Spring框架剛出來時主要就是來完成這個功能。
??? Spring框架除了幫我們管理對象及其依賴關系,還提供像通用日志記錄、性能統計、安全控制、異常處理等面向切面編程的能力,還能幫我們管理最頭疼的數據庫事務,它本身提供了一套簡單的JDBC訪問實現,還能與第三方數據庫訪問框架集成(如Hibernate、JPA),與各種Java EE技術整合(如Java Mail、任務調度等等),提供一套自己的web層框架Spring MVC、而且還能非常簡單的與第三方web框架集成。從這里我們可以認為Spring是一個超級粘合平臺,除了自己提供功能外,還提供粘合其他技術和框架的能力,從而使我們可以更自由的選擇到底使用什么技術進行開發。而且不管是JAVA SE(C/S架構)應用程序還是JAVA EE(B/S架構)應用程序都可以使用這個平臺進行開發。

?2.1、框架的本質

??? Spring 框架中的核心組件只有三個:Core、Context 和 Beans。它們構建起了整個 Spring 的骨骼架構。沒有它們就不可能有 AOP、Web 等上層的特性功能。如果在它們三個中選出核心的話,那就非 Beans 組件莫屬了,其實 Spring 就是面向 Bean 的編程(BOP,Bean Oriented Programming),Bean 在 Spring 中才是真正的主角。Bean 在 Spring 中作用就像 Object 對 OOP 的意義一樣,沒有對象的概念就像沒有面向對象編程,Spring 中沒有 Bean 也就沒有 Spring 存在的意義。就像一次演出舞臺都準備好了但是卻沒有演員一樣。為什么要 Bean 這種角色 Bean 或者為何在 Spring 如此重要,這由 Spring 框架的設計目標決定,Spring 為何如此流行,我們用 Spring 的原因是什么,想想會發現原來 Spring 解決了一個非常關鍵的問題,它可以讓你把對象之間的依賴關系轉而用配置文件來管理,也就是依賴注入機制。而這個注入關系在一個叫 IoC 容器中管理,那 IoC 容器就是被 Bean 包裹的對象。Spring 正是通過把對象包裝在 Bean 中而達到對這些對象的管理以及一些列額外操作的目的。它這種設計策略完全類似于 Java 實現 OOP 的設計理念,當然了 Java 本身的設計要比 Spring 復雜太多太多,但是都是構建一個數據結構,然后根據這個數據結構設計他的生存環境,并讓它在這個環境中按照一定的規律在不停的運動,在它們的不停運動中設計一系列與環境或者與其他個體完成信息交換。這樣想來我們用到的其他框架都是大慨類似的設計理念。

?2.2、核心組件的協同工作

??? 把 Beans 比作一場演出中的演員的話,那 Context 就是這場演出的舞臺背景,而 Core 應該就是演出的道具了。只有它們在一起才能具備演出一場好戲的最基本條件。當然有最基本的條件還不能使這場演出脫穎而出,還要表演的節目足夠的精彩,這些節目就是 Spring 能提供的特色功能了。我們知道 Bean 包裝的是 Object,而 Object 必然有數據,如何給這些數據提供生存環境就是 Context 要解決的問題,對 Context 來說就是要發現每個 Bean 之間的關系,為它們建立這種關系并且要維護好這種關系。所以 Context 就是一個 Bean 關系的集合,這個關系集合又叫 Ioc 容器,一旦建立起這個 Ioc 容器后 Spring 就可以工作了。那 Core 組件又有什么用武之地呢?其實 Core 就是發現、建立和維護每個 Bean 之間的關系所需要的一些列的工具,從這個角度看來,Core 這個組件叫 Util 更能理解。

?

?2.3、Bean 組件

?? Bean 組件在 Spring 的 org.springframework.beans 包下。這個包下的所有類主要解決了三件事:Bean 的定義、Bean 的創建以及對 Bean 的解析。對 Spring 的使用者來說唯一需要關心的就是 Bean 的創建,其他兩個由 Spring 在內部幫你完成了,對你來說是透明的。Spring Bean 的創建時典型的工廠模式,它的頂級接口是 BeanFactory,下圖是這個工廠的繼承層次關系:

???? BeanFactory 有三個子類:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。但是我們可以發現最終的默認實現類是 DefaultListableBeanFactory,實現了所有的接口。那為何要定義這么多層次的接口呢?查閱這些接口的源碼和說明發現,每個接口都有使用的場合,它主要是為了區分在 Spring 內部對象的傳遞和轉化過程中,對對象的數據訪問所做的限制。例如 ListableBeanFactory 接口表示這些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的這些 Bean 是有繼承關系的,也就是每個 Bean 有可能有父 Bean。AutowireCapableBeanFactory 接口定義 Bean 的自動裝配規則。這四個接口共同定義了 Bean 的集合、Bean 之間的關系以及 Bean 行為。Bean 的定義主要有 BeanDefinition 描述。

Bean的作用域

? Spring定義了多種Bean作用域,可以基于這些作用域創建bean,包括:

    單例(Singleton):在整個應用中,只創建bean的一個實例。原型(Prototype):每次注入或者通過Spring應用上下文獲取的時候,都會創建一個新的bean實例。會話(Session):在Web應用中,為每個會話創建一個bean實例。請求(Rquest):在Web應用中,為每個請求創建一個bean實例。

?聲明Bean

@Component 組件,沒有明確的角色
@Service 在業務邏輯層使用
@Repository 在數據訪問層使用
@Controller 在展現層使用(MVC -> Spring MVC)使用
在這里,可以指定bean的id名:Component(“yourBeanName”)
同時,Spring支持將@Named作為@Component注解的替代方案。
兩者之間有一些細微的差異,但是在大多數場景中,它們是可以互相替換的。

?2.4、Context 組件

??? Context 作為 Spring 的 Ioc 容器,基本上整合了 Spring 的大部分功能,或者說是大部分功能的基礎。Context 在 Spring 的 org.springframework.context 包下, Context 組件實際上就是給 Spring 提供一個運行時的環境,用以保存各個對象的狀態。下面看一下這個環境是如何構建的。ApplicationContext 是 Context 的頂級父類,除了能標識一個應用環境的基本信息外,還繼承了五個接口,這五個接口主要是擴展了 Context 的功能。

?

??? ApplicationContext 繼承了 BeanFactory,這也說明了 Spring 容器中運行的主體對象是 Beans,另外 ApplicationContext 繼承了 ResourceLoader 接口,使得 ApplicationContext 可以訪問到任何外部資源。
??? ApplicationContext 的子類主要包含兩個方面:

1  ConfigurableApplicationContext 表示該 Context 是可修改的,也就是在構建 Context 中用戶可以動態添加或修改已有的配置信息,它下面又有多個子類,其中經常使用的是可更新的 Context,即 AbstractRefreshableApplicationContext 類。
2  WebApplicationContext 顧名思義,就是為 web 準備的 Context ,可以直接訪問到 ServletContext,通常情況下,這個接口使用的少。

?? 再往下分就是按照構建 Context 的文件類型,接著就是訪問 Context 的方式。這樣一級一級構成了完整的 Context 等級層次。
?? 總體來說 ApplicationContext 必須要完成以下幾件事:

1   標識一個應用環境
2   利用 BeanFactory 創建 Bean 對象
3   保存對象關系表
4   能夠捕獲各種事件

?2.5、Core 組件

??? Core 組件作為 Spring 的核心組件,其中包含了很多的關鍵類,其中一個重要組成部分就是定義了資源的訪問方式這種把所有資源都抽象成一個接口的方式很值得在以后的設計中拿來學習。下面就重要看一下這個部分在 Spring 的作用。


??? Resource 接口封裝了各種可能的資源類型,也就是對使用者來說屏蔽了文件類型的不同。對資源的提供者來說,如何把資源包裝起來交給其他人用這也是一個問題,我們看到 Resource 接口繼承了 InputStreamSource 接口,這個接口中有個 getInputStream 方法,返回的是 InputStream 類。這樣所有的資源都被可以通過 InputStream 這個類來獲取,所以也屏蔽了資源的提供者。另外還有一個問題就是加載資源的問題,也就是源的加載者要統一,這個任務是由 ResourceLoader 接口完成,它屏蔽了所有的資源加載者的差異,只需要實現這個接口就可以加載所有的資源,默認實現是 DefaultResourceLoader。

?? Context 和 Resource 建立關系:Context 是把資源的加載、解析和描述工作委托給了 ResourcePatternResolver 類來完成,相當于一個接頭人,把資源的加載、解析和資源的定義整合在一起便于其他組件使用。Core 組件中還有很多類似的方式。

?2.6、IoC 容器如何工作

? 前面介紹了 Core 組件、Bean 組件和 Context 組件的結構與相互關系,下面這里從使用者角度看一下它們是如何運行的,以及我們如何讓 Spring 完成各種功能。

?如何創建 BeanFactory 工廠

?? IoC容器實際上就是Context組件結合其他兩個組件共同構建了一個 Bean 關系網,構建的入口就在 AbstractApplicationContext 類的 refresh 方法中。這個方法的代碼如下:

 1 public void refresh() throws BeansException, IllegalStateException {
 2     synchronized (this.startupShutdownMonitor) {
 3         // Prepare this context for refreshing.
 4         prepareRefresh();
 5         // Tell the subclass to refresh the internal bean factory.
 6         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 7         // Prepare the bean factory for use in this context.
 8         prepareBeanFactory(beanFactory);
 9         try {
10             // Allows post-processing of the bean factory in context subclasses.
11             postProcessBeanFactory(beanFactory);
12             // Invoke factory processors registered as beans in the context.
13             invokeBeanFactoryPostProcessors(beanFactory);
14             // Register bean processors that intercept bean creation.
15             registerBeanPostProcessors(beanFactory);
16             // Initialize message source for this context.
17             initMessageSource();
18             // Initialize event multicaster for this context.
19             initApplicationEventMulticaster();
20             // Initialize other special beans in specific context subclasses.
21             onRefresh();
22             // Check for listener beans and register them.
23             registerListeners();
24             // Instantiate all remaining (non-lazy-init) singletons.
25             finishBeanFactoryInitialization(beanFactory);
26             // Last step: publish corresponding event.
27             finishRefresh();
28         }
29         catch (BeansException ex) {
30             // Destroy already created singletons to avoid dangling resources.
31             destroyBeans();
32             // Reset 'active' flag.
33             cancelRefresh(ex);
34             // Propagate exception to caller.
35             throw ex;
36         }
37     }
38 }

??? 這個方法就是構建整個 Ioc 容器過程的完整的代碼,了解里面的每一行代碼基本上就了解大部分 Spring 的原理和功能了。主要包含這樣幾個步驟:

1  構建 BeanFactory,以便于產生所需的“演員”
2  注冊可能感興趣的事件
3  創建 Bean 實例對象
4  觸發被監聽的事件

???? 創建和配置 BeanFactory,這里 refresh 也就是刷新配置,前面介紹了 Context 有可更新的子類,這里正是實現這個功能,當 BeanFactory 已存在是就更新,如果沒有就新創建。下面是更新 BeanFactory 的方法代碼:

 1 protected final void refreshBeanFactory() throws BeansException {
 2     if (hasBeanFactory()) {
 3         destroyBeans();
 4         closeBeanFactory();
 5     }
 6     try {
 7         DefaultListableBeanFactory beanFactory = createBeanFactory();
 8         beanFactory.setSerializationId(getId());
 9         customizeBeanFactory(beanFactory);
10         loadBeanDefinitions(beanFactory);
11         synchronized (this.beanFactoryMonitor) {
12             this.beanFactory = beanFactory;
13         }
14     }
15     catch (IOException ex) {
16         throw new ApplicationContextException(
17             "I/O error parsing bean definition source for "
18             + getDisplayName(), ex);
19     }
20 }

???? 這個方法實現了 AbstractApplicationContext 的抽象方法 refreshBeanFactory,這段代碼清楚的說明了 BeanFactory 的創建過程。注意 BeanFactory 對象的類型的變化,前面介紹了它有很多子類,在什么情況下使用不同的子類這非常關鍵。BeanFactory 的原始對象是 DefaultListableBeanFactory,設計到后面對這個對象的多種操作。除了 BeanFactory 相關的類外,還發現了與 Bean 的 register 相關。這在 refreshBeanFactory 方法中有一行 loadBeanDefinitions(beanFactory) 將找到答案,這個方法將開始加載、解析 Bean 的定義,也就是把用戶定義的數據結構轉化為 IoC 容器中的特定數據結構。

創建 BeanFactory 時序圖:

Bean 的解析和登記流程時序圖:

??? 創建好 BeanFactory 后,接下去添加一些 Spring 本身需要的一些工具類,這個操作在 AbstractApplicationContext 的 prepareBeanFactory 方法完成。AbstractApplicationContext 中接下來的三行代碼對 Spring 的功能擴展性起了至關重要的作用。前兩行主要是讓我們可以對已經構建的 BeanFactory 的配置做修改,后面一行就是讓我們對以后再創建 Bean 的實例對象時添加一些自定義的操作。所以它們都是擴展了 Spring 的功能,所以我們要學習使用 Spring 必須對這一部分搞清楚。

?如何創建 Bean 實例并構建 Bean 的關系網

?? ?下面是 Bean 的實例化代碼,是從 finishBeanFactoryInitialization 方法開始的。

 1 protected void finishBeanFactoryInitialization(
 2     ConfigurableListableBeanFactory beanFactory) {
 3  
 4     // Stop using the temporary ClassLoader for type matching.
 5     beanFactory.setTempClassLoader(null);
 6  
 7     // Allow for caching all bean definition metadata, not expecting further changes.
 8     beanFactory.freezeConfiguration();
 9  
10     // Instantiate all remaining (non-lazy-init) singletons.
11     beanFactory.preInstantiateSingletons();
12 }

??? 從上面代碼中可以發現 Bean 的實例化是在 BeanFactory 中發生的。preInstantiateSingletons 方法的代碼如下:

 1 public void preInstantiateSingletons() throws BeansException {
 2     if (this.logger.isInfoEnabled()) {
 3         this.logger.info("Pre-instantiating singletons in " + this);
 4     }
 5     synchronized (this.beanDefinitionMap) {
 6         for (String beanName : this.beanDefinitionNames) {
 7             RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
 8             if (!bd.isAbstract() && bd.isSingleton()
 9                 && !bd.isLazyInit()) {
10                 if (isFactoryBean(beanName)) {
11                     final FactoryBean factory =
12                         (FactoryBean) getBean(FACTORY_BEAN_PREFIX+ beanName);
13                     boolean isEagerInit;
14                     if (System.getSecurityManager() != null
15                         && factory instanceof SmartFactoryBean) {
16                         isEagerInit = AccessController.doPrivileged(
17                             new PrivilegedAction<Boolean>() {
18                             public Boolean run() {
19                                 return ((SmartFactoryBean) factory).isEagerInit();
20                             }
21                         }, getAccessControlContext());
22                     }
23                     else {
24                         isEagerInit = factory instanceof SmartFactoryBean
25                             && ((SmartFactoryBean) factory).isEagerInit();
26                     }
27                     if (isEagerInit) {
28                         getBean(beanName);
29                     }
30                 }
31                 else {
32                     getBean(beanName);
33                 }
34             }
35         }
36     }
37 }

?? 這里出現了一個非常重要的 Bean—— FactoryBean,可以說 Spring 一大半的擴展的功能都與這個 Bean 有關,這是個特殊的 Bean 是一個工廠 Bean,可以產生 Bean 的 Bean,這里的產生 Bean 是指 Bean 的實例,如果一個類繼承 FactoryBean 用戶只要實現它的 getObject 方法,就可以自己定義產生實例對象的方法。然而在 Spring 內部這個 Bean 的實例對象是 FactoryBean,通過調用這個對象的 getObject 方法就能獲取用戶自定義產生的對象,從而為 Spring 提供了很好的擴展性。Spring 獲取 FactoryBean 本身的對象是在前面加上 & 來完成的。如何創建 Bean 的實例對象以及如何構建 Bean 實例對象之間的關聯關系式 Spring 中的一個核心關鍵,下面是這個過程的流程圖。

?? 如果是普通的 Bean 就直接創建它的實例,是通過調用 getBean 方法。下面是創建 Bean 實例的時序圖:

? 還有一個非常重要的部分就是建立 Bean 對象實例之間的關系,這也是 Spring 框架的核心競爭力,何時、如何建立它們之間的關系:

?

?IoC 容器的擴展點

??? 現在還有一個問題就是如何讓這些 Bean 對象有一定的擴展性,就是可以加入用戶的一些操作。那么有哪些擴展點呢? Spring 又是如何調用到這些擴展點的?對 Spring 的 Ioc 容器來說,主要有這么幾個。BeanFactoryPostProcessor, BeanPostProcessor。它們分別是在構建 BeanFactory 和構建 Bean 對象時調用。還有就是 InitializingBean 和 DisposableBean,他們分別是在 Bean 實例創建和銷毀時被調用。用戶可以實現這些接口中定義的方法,Spring 就會在適當的時候調用他們。還有一個是 FactoryBean 他是個特殊的 Bean,這個 Bean 可以被用戶更多的控制。這些擴展點通常也是我們使用 Spring 來完成我們特定任務的地方,如何精通 Spring 就看你有沒有掌握好 Spring 有哪些擴展點,并且如何使用他們,要知道如何使用他們就必須了解他們內在的機理。可以用下面一個比喻來解釋。
??? 我們把 Ioc 容器比作一個箱子,這個箱子里有若干個球的模子,可以用這些模子來造很多種不同的球,還有一個造這些球模的機器,這個機器可以產生球模。那么他們的對應關系就是:BeanFactory 是那個造球模的機器,球模就是 Bean,而球模造出來的球就是 Bean 的實例。那前面所說的幾個擴展點又在什么地方呢? BeanFactoryPostProcessor 對應到當造球模被造出來時,你將有機會可以對其做出適當的修正,也就是他可以幫你修改球模。而 InitializingBean 和 DisposableBean 是在球模造球的開始和結束階段,你可以完成一些預備和掃尾工作。BeanPostProcessor 就可以讓你對球模造出來的球做出適當的修正。最后還有一個 FactoryBean,它可是一個神奇的球模。這個球模不是預先就定型了,而是由你來給他確定它的形狀,既然你可以確定這個球模型的形狀,當然他造出來的球肯定就是你想要的球了,這樣在這個箱子里你可以發現所有你想要的球。

Ioc 容器如何使用

??? 我們使用 Spring 必須要首先構建 Ioc 容器,沒有它 Spring 無法工作,ApplicatonContext.xml 就是 IoC 容器的默認配置文件,Spring 的所有特性功能都是基于這個 Ioc 容器工作的,比如AOP。IoC 它實際上就是為你構建了一個魔方,Spring 為你搭好了骨骼架構,這個魔方到底能變出什么好的東西出來,這必須要有你的參與。那我們怎么參與?這就是前面說的要了解 Spring 中有哪些擴展點,我們通過實現那些擴展點來改變 Spring 的通用行為。至于如何實現擴展點來得到我們想要的個性結果,Spring 中有很多例子,其中 AOP 的實現就是 Spring 本身實現了其擴展點來達到了它想要的特性功能。

??? 通過從 Spring 的幾個核心組件入手,試圖找出構建 Spring 框架的骨骼架構,進而分析 Spring 在設計時的一些設計理念,是否從中找出一些好的設計思想,對我們以后程序設計能提供一些思路。接著再詳細分析了 Spring 中是如何實現這些理念的,以及在設計模式上是如何使用的。通過分析 Spring 給我一個很大的啟示就是這套設計理念其實對我們有很強的借鑒意義,它通過抽象復雜多變的對象,進一步做規范,然后根據它定義的這套規范設計出一個容器,容器中構建它們的復雜關系,其實現在有很多情況都可以用這種類似的處理方法。

三、Spring的特性

?3.1、一些概念

1   應用程序:是能完成我們所需要功能的成品,比如購物網站、OA系統。
2   框架:是能完成一定功能的半成品,比如我們可以使用框架進行購物網站開發;框架做一部分功能,我們自己做一部分功能,這樣應用程序就創建出來了。而且框架規定了你在開發應用程序時的整體架構,提供了一些基礎功能,還規定了類和對象的如何創建、如何協作等,從而簡化我們開發,讓我們專注于業務邏輯開發。
3   非侵入式設計:從框架角度可以這樣理解,無需繼承框架提供的類,這種設計就可以看作是非侵入式設計,如果繼承了這些框架類,就是侵入設計,如果以后想更換框架,之前寫過的代碼幾乎無法重用,如果非侵入式設計則之前寫過的代碼仍然可以繼續使用。
4   輕量級及重量級:輕量級是相對于重量級而言的,輕量級一般就是非入侵性的、所依賴的東西非常少、資源占用非常少、部署簡單等等,其實就是比較容易使用,而重量級正好相反。
5   POJO:POJO(Plain Old Java Objects)簡單的Java對象,它可以包含業務邏輯或持久化邏輯,但不擔當任何特殊角色且不繼承或不實現任何其它Java框架的類或接口。
6   容器:在日常生活中容器就是一種盛放東西的器具,從程序設計角度看就是裝對象的的對象,因為存在放入、拿出等操作,所以容器還要管理對象的生命周期。
7   控制反轉:即Inversion of Control,縮寫為IoC,控制反轉還有一個名字叫做依賴注入(Dependency Injection),就是由容器控制程序之間的關系,而非傳統實現中,由程序代碼直接操控。
8   Bean:一般指容器管理的對象,在Spring中指Spring IoC容器管理的對象。

?3.2、Spring的優點

非常輕量級的容器:以集中的、自動化的方式進行應用程序對象創建和裝配,負責對象創建和裝配,管理對象生命周期,能組合成復雜的應用程序。Spring容器是非侵入式的(不需要依賴任何Spring特定類),而且完全采用POJOs進行開發,使應用程序更容易測試、更容易管理。而且核心JAR包非常小,Spring3.0.5不到1M,而且不需要依賴任何應用服務器,可以部署在任何環境(Java SE或Java EE)。
AOP:面向切面編程提供從另一個角度來考慮程序結構以完善面向對象編程(OOP),即可以通過在編譯期間、裝載期間或運行期間實現在不修改源代碼的情況下給程序動態添加功能的一種技術。通俗點說就是把可重用的功能提取出來,然后將這些通用功能在合適的時候織入到應用程序中;比如安全,日志記錄,這些都是通用的功能,我們可以把它們提取出來,然后在程序執行的合適地方織入這些代碼并執行它們,從而完成需要的功能并復用了這些功能。
簡單的數據庫事務管理:在使用數據庫的應用程序當中,自己管理數據庫事務是一項很讓人頭疼的事,而且很容易出現錯誤,Spring支持可插入的事務管理支持,而且無需JavaEE環境支持,通過Spring管理事務可以把我們從事務管理中解放出來來專注業務邏輯。
JDBC抽象及ORM框架支持:Spring使JDBC更加容易使用;提供DAO(數據訪問對象)支持,非常方便集成第三方ORM框架,比如Hibernate,Mybatis等;并且完全支持Spring事務和使用Spring提供的一致的異常體系。
靈活的Web層支持:Spring本身提供一套非常強大的MVC框架,而且可以非常容易的與第三方MVC框架集成,比如Struts等。
簡化各種技術集成:提供對Java Mail、任務調度、JMX、JMS、JNDI、EJB、動態語言、遠程訪問、Web Service等的集成。

四、總結

?? 在Spring中,我們知道了其中的本質——‘道’,然后在‘術’上進行實現,并且發現spring中提供了很多的擴展,從此對spring有了更深的理解,同時隨著版本的遞增,其中的各種功能也進行了很多的優化和提升,我們最好多閱讀一下spring的源代碼,從中明白很多程序背后的機理。

?參考文獻:https://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/

?????????????????? https://www.cnblogs.com/wxisme/p/4751397.html

轉載于:https://www.cnblogs.com/zyrblog/p/9944290.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/450649.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/450649.shtml
英文地址,請注明出處:http://en.pswp.cn/news/450649.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

用Python進行機器學習所需環境的配置(轉)

源&#xff1a;用Python進行機器學習所需環境的配置

成功創業者所需的能力

1. 富有遠見&#xff0c;樂在其中。 如果你能很好地預見自己的公司所在領域在很多年后的樣子&#xff0c;這能保證你在該領域取得長久的發展。很多人能在商業領域取得成功并不是因為他們徹底的廢舊立新&#xff0c;而是因為他們乘風破浪&#xff0c;能在現有的基礎上有所改進和…

記錄:non-compatible bean definition of same name and class [com.XXX.XXX]

啟動 springBoot 工程時報錯&#xff1a; Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name userLogAspect for bean class [com.foreveross.security.config.UserLogAspect] conflicts with existing,…

「日常訓練」 Genghis Khan the Conqueror(HDU-4126)

題意 給定\(n\)個點和\(m\)條無向邊&#xff08;\(n\le 3000\)&#xff09;&#xff0c;需要將這\(n\)個點連通。但是有\(Q\)次&#xff08;\(Q\le 10^4\)&#xff09;等概率的破壞&#xff0c;每次破壞會把\(m\)條邊中的某條邊的權值增大某個值&#xff0c;求\(Q\)次破壞每次將…

數學家吳文俊批判“中國式奧數”:害人害數學

奧數震動了兩位最高科技獎得主 一談起“奧數”&#xff0c;國內當今數學界的泰斗級人物吳文俊院士就急了。 他在沙發上挺直了腰&#xff0c;瞪大眼睛&#xff0c;伸出手掌指指點點&#xff1a;“是害人的&#xff0c;害數學&#xff01;” “什么奧林匹克&#xff1f;沒這回事&…

CentOS 7 搭建CA認證中心實現https取證

CA認證中心簡述CA &#xff1a;CertificateAuthority的縮寫&#xff0c;通常翻譯成認證權威或者認證中心&#xff0c;主要用途是為用戶發放數字證書功能&#xff1a;證書發放、證書更新、證書撤銷和證書驗證。作用&#xff1a;身份認證&#xff0c;數據的不可否認性端口&#x…

簡單明了 - Git 使用超詳細教程

見&#xff1a;http://www.admin10000.com/document/5374.html 一&#xff1a;Git是什么&#xff1f; Git是目前世界上最先進的分布式版本控制系統。 二&#xff1a;SVN與Git的最主要的區別&#xff1f; SVN是集中式版本控制系統&#xff0c;版本庫是集中放在中央服務器的&…

FileStream功能被禁用

今天還原數據庫&#xff0c;遇到如下問題&#xff1a; 網上的解決方法大概是三種&#xff1a; 1、講數據庫備份文件權限設置為“EventOne” 2、打開SQLServer配置管理器&#xff0c;選中服務然后右擊“屬性”將FileStream相關勾選并重啟當前實例服務 3、設置數據庫訪問級別 USE…

btree索引和hash索引的區別(待更新)

btreehash用于使用 , >, >, <, < 或者 BETWEEN 運算符的列比較。如果 LIKE 的參數是一個沒有以通配符起始的常量字符串的話也可以使用這種索引僅僅能滿足"","IN"和"<>"查詢

window.parent,top,window.self,parent,opener

2019獨角獸企業重金招聘Python工程師標準>>> 在應用有frameset或者iframe的頁面時&#xff0c;parent是父窗口&#xff0c;top是最頂級父窗口&#xff08;有的窗口中套了好幾層frameset或者iframe&#xff09;&#xff0c;self是當前窗口&#xff0c; opener是用ope…

ALM 中查看某個 test 的更改 history 歷史

ALM 中要查看某個 test 更改歷史&#xff0c; 需要下面兩個表&#xff1a;AUDIT_LOG and AUDIT_PROPERTIES------- Get Test modification history -------- ---- In ALM, 857, if filter out test case named 26169502, check its History. In the history, for the node of d…

編譯器vs.代碼 誰之過

摘要&#xff1a;編譯器是將程序語言編譯成機器語言的一種高級程序。如今許多編譯器越發智能&#xff0c;在編譯不通過的情況&#xff0c;你的代碼甚至都無法運行&#xff0c;那么到底是編譯的問題還是您的代碼問題呢&#xff1f; 許多程序員喜歡抱怨編譯器報出的各做錯誤&…

Android 在 Google 開發者大會上發布了哪些更新? | Google 開發者大會 2018

有哪些新的 Android 系統特性 Google Play 上的 targetVersion 要求 2018年8月 新應用發布必須為26或者更高2018年11月 升級現有應用必須為26或者更高2019年之后 新發布或者升級應用必須為一年內發布的 Android 版本工信部已經出臺相應的政策&#xff0c;中國主流的應用市場也已…

兩個不同的數據庫如何跨庫事務

首先我們要明白同一實例&#xff0c;簡單來說就是一個ip&#xff0c;如果兩個數據庫位于同一個ip&#xff0c;就是同一實例。其實實例并不相當于ip&#xff0c; 他其實相當于服務&#xff0c;也就是serve。 這樣的兩個或多個就可以跨庫事務&#xff0c;比如 begin; insert in…

鏈表排序(冒泡、選擇、插入、快排、歸并、希爾、堆排序)

參考http://www.cnblogs.com/TenosDoIt/p/3666585.html 插入排序&#xff08;算法中是直接交換節點&#xff0c;時間復雜度O&#xff08;n^2&#xff09;,空間復雜度O&#xff08;1&#xff09;&#xff09; 1 class Solution {2 public:3 ListNode *insertionSortList(Lis…

zookeeper使用和原理探究

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 zookeeper介紹 zookeeper是一個為分布式應用提供一致性服務的軟件&#xff0c;它是開源的Hadoop項目中的一個子項目&#xff0c;并且根據…

thinkphp如何部署到寶塔面板nginx服務器

原理&#xff1a;一般本地都會使用apache服務器&#xff0c;這個對pathinfo&#xff08;兩個&#xff0c;一個是環境變量$_SERVER[PATH_INFO]&#xff0c;另一個是pathinfo函數&#xff09;路由解析非常支持的&#xff0c;不需要部署什么&#xff0c; 但是nginx是對pathinfo函…

Android獲取所有應用的資源id和對應的uri

背景在某些應用中&#xff0c;為了實現應用apk資源放入重復利用&#xff0c;或者使用反射得到本應用的資源&#xff0c;需要使用反射方式獲得&#xff0c;但Resources類中也自帶了這種獲取方式&#xff0c;并且功能更加強大你可以獲取string,color,drawable,raw,xml等文件&…

nginx的腳本引擎(一)

nginx的腳本的語法和shell是很像的&#xff0c;我大致看了一下覺得挺有意思的&#xff0c;就想寫寫記錄一下。我沒看過shell腳本的引擎&#xff0c;不知道nginx腳本引擎和shell腳本引擎像不像&#xff0c;但是我覺得nginx的腳本引擎有點像C和匯編。 ngx_http_script_engine_t這…

一個待辦事列表todolist

最近有位老師讓我做的&#xff0c;圖片在下面&#xff0c;做了4個多小時&#xff0c;ui有的簡陋&#xff0c;可以再美化一下&#xff0c;這個會更好看&#xff0c;畢竟我也不是專業前端&#xff0c;測試網站http://todolist.sshouxin.top/使用的是thinkphp5.1的框架&#xff0c…