目錄
1. IoC的提出
2. Spring容器
2.1. Spring容器實現原理
2.2. Spring組件
2.2.1 XML標簽方式
2.2.2. 類注解方式
2.2.3. 方法注解方式
2.3. Spring容器分類
2.3.1. BeanFactory容器
2.3.2.?ApplicationContext容器
2.3.3.?WebApplicationContext容器
3. Spring中的常用注解
1. IoC的提出
? ? ? ?在采用面向對象方法設計的軟件系統中,底層實現是由一組對象組成的,所有的對象通過彼此的合作,最終實現系統的業務邏輯。對象之間的耦合關系就像手表齒輪的嚙合,彼此之間存在復雜依賴關系,往往牽一發而動全身,如圖1所示。因此,如何降低系統之間、模塊之間和對象之間的耦合度,是軟件工程追求的目標之一。1996年,軟件專家Michael mattson首次提出了控制反轉(Inversion of Control, IoC)概念。控制反轉的核心思想是:借助于“第三方(IoC容器)” 實現具有依賴關系的對象之間的解耦,如圖2所示。
圖1. 傳統面向對象系統 | 圖2. 使用IoC容器的面向對象系統 |
? ? ? ? 在使用IoC容器的面向對象系統中,全部對象的控制權都交給IoC容器來管理,所以,IoC容器成為整個系統的關鍵核心,它起到了一種類似“粘合劑”的作用,把系統中的所有對象粘合在一起發揮作用。
? ? ? ?2004年,Martin Fowler探討了同一個問題,既然IoC是控制反轉,那么到底是“哪些方面的控制被反轉了呢”?最后,他得出結論是“獲得依賴對象的過程被反轉了”。控制被反轉之后,獲得依賴對象的過程由自身管理變為由IoC容器主動注入。于是,他給“控制反轉”取了一個更合適的名字叫做“依賴注入(Dependency Injection, DI)”。所以,依賴注入(DI)和控制反轉(IoC)是從不同的角度來描述同一件事情,就是指通過引入IoC容器,利用依賴關系注入的方式,實現對象之間的解耦。
2. Spring容器
? ? ? ?Spring容器是Spring框架的核心,它是一種IoC容器,負責管理應用程序中對象的生命周期和依賴關系。Spring容器通過讀取Spring組件的元數據((XML文件、注解或Java配置))來創建、配置、裝配并管理應用程序中的對象。
2.1. Spring容器實現原理
? ? ? ? 實現Spring容器的最主要技術是Java反射編程。Java的反射機制是指在程序的運行狀態中,可以構造任意一個類的對象,可以了解任意一個對象所屬的類,可以了解任意一個類的成員變量和方法,可以調用任意一個對象的屬性和方法。簡單來說,只要給定類的名字,就可以通過反射機制來獲得類的所有信息。Spring容器的工作模式可以看作是工廠模式的升華。可以把Spring容器看作是一個對象工廠,這個工廠里要生產的對象都在元數據中給出定義,然后利用反射編程,根據元數據(XML文件、注解或Java配置)中給出的類名生成相應的對象。從實現來看,Spring是把工廠模式里寫死的對象生成代碼,改變為由元數據來定義,也就是把工廠和對象生成這兩者獨立離開來,目的是提高靈活性和可維護性。Spring容器的實現原理如圖3所示。
? 圖3. Spring容器實現原理
2.2. Spring組件
? ? ? 納入Spring容器管理的Java類稱為Spring組件(簡稱組件),Spring容器對組件進行實例化所創建的對象稱為Bean。一個Java類可以標識為多個Spring組件,每個Spring組件可以創建一個或多個Bean。定義Spring組件主要有三種方式:XML標簽、類注解和方法注解。
2.2.1 XML標簽方式
? ? ? ?該方式通過在XML配置文件中使用<bean>標簽來定義Spring組件。下面通過一個例子來說明如何通過<bean>標簽來定義組件和創建Bean。
(1)類的定義
? ? ? ? ? 在包com.my.examples.example1中定義Java類Login和User,類的聲明如下:
package com.my.examples.example1;public class Login {String username;String password;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}
}
package com.my.examples.example1;public class User {Login login;public void print(){System.out.println("login user: "+login.getUsername());}public Login getLogin() {return login;}public void setLogin(Login login) {this.login = login;}
}
?(2)Spring組件定義
? ? ? ? 在項目的resources目錄下創建spring-config.xml文件,文件的內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 定義Spring組件 --><bean id="login" class="com.my.examples.example1.Login"><!--為屬性code和name注入初始值 --><property name="username" value="admin"></property><property name="password" value="123456"></property></bean><bean id="currentUser" class="com.my.examples.example1.User"><!-- 為屬性login注入引用值(Spring組件login) --><property name="login" ref="login"></property></bean>
</beans>
(3)配置Spring應用上下文
? ? ? ?根據XML配置文件創建Spring容器,利用容器所提供的getBean()方法獲取相應的Bean,從容中獲取Bean后就可以調用其方法來執行。
package com.my.examples.example1;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {//根據XML配置文件創建Spring容器ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");//獲取Spring容器中的BeanLogin login= context.getBean("login", Login.class);User user=context.getBean("currentUser", User.class);user.print();}
}
運行類Test,在控制臺中輸出如下信息:
login user: admin |
2.2.2. 類注解方式
? ? ? ?該方式通過@Component、@Respository、@Service、@Controller注解來定義Spring組件,利用Java配置類(用@Configuration注解定義的類)代替XML配置文件。@Component注解用于標識一個類為Spring組件,當Spring框架掃描到該注解時,會創建該類的實例并納入Spring容器中管理。@Respository、@Service、@Controller都是@Component注解的特殊形式,分別用于標識數據訪問層、服務層和控制層的Spring組件。
? ? ? 下面將2.1.1節中的例子改為類注解方式。
(1)Spring組件的定義
? ? ? 在包com.my.examples.example2中定義Java類Login和User,在類上加入@Component注解使其成為Spring組件。
package com.my.examples.example2;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;@Component //定義Spring組件
@PropertySource("classpath:my-config.properties") //引入外部屬性文件
public class Login {@Value("${my.username}") //注入屬性文件中的account值String username;@Value("${my.password}") //注入屬性文件中的password值String password;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}
}
package com.my.examples.example2;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component //定義Spring組件
public class User {@Autowired //注入注入依賴的Spring組件的BeanLogin login;public void print(){System.out.println("login user: "+login.getUsername());}
}
? ? ? ?@PropertySource注解的功能是引入外部屬性文件:my-config.properties,該文件位于項目的resources目錄下,文件的內容如下:
my.username=admin my.password=123456 |
(2)Java配置類的定義
? ? ? 在包com.my.examples.example2中定義類Config,在該類上加入@Configuraton注解使其成為Java配置類。在該配置類上加入@ComponentScan注解,其功能是讓Spring容器掃描com.my.examples.example2包及其子包下面所有的Spring組件,并將這些組件進行例化為Bean。
package com.my.examples.example2;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration //定義Java配置類
//掃描包com.my.examples.example2及其子包下面的所有Spring組件
@ComponentScan("com.my.examples.example2")
public class Config {
}
(3)配置Spring應用上下文
? ? ? 根據Java配置類創建Spring容器,利用容器提供的getBean()方法獲取相應的Bean,調用Bean提供的方法來執行任務。
package com.my.examples.example2;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Test {public static void main(String[] args) {//根據Java配置類創建Spring容器AnnotationConfigApplicationContext context = newAnnotationConfigApplicationContext(Config.class);//獲取容器中的BeanLogin login=context.getBean(Login.class);User user=context.getBean(User.class);user.print();}
}
運行類Test,在控制臺中輸出如下內容:
login user: admin |
2.2.3. 方法注解方式
? ? ? ?該方式不需要在類上加入@Component等注解來定義Spring組件,而是通過在Java配置類中的方法上加入@Bean注解,該方法的返回值是一個Spring組件的Bean。
? ? ? 下面把第2.1.2節中的應用改為采用Java配置的方式來實現。
(1)類的定義
? ? ? ?在包com.my.examples.example3中定義Java類Login和User。
package com.my.examples.example3;public class Login {String username;String password;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}
}
package com.my.examples.example3;public class User {Login login;public void print(){System.out.println("login user: "+login.getUsername());}public Login getLogin() {return login;}public void setLogin(Login login) {this.login = login;}
}
(2)定義Java配置類
package com.my.examples.example3;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;@Configuration //定義Java配置類
//掃描包com.my.examples.example3及其子包下面的所有Spring組件
@ComponentScan("com.my.examples.example3")
@PropertySource("classpath:my-config.properties") //引入外部屬性文件
public class Config {@Value("${my.username}")String username;@Value("${my.password}")String password;@Bean //定義Spring組件并返回Beanpublic Login login(){Login login = new Login();//注入屬性值login.setUsername(username);login.setPassword(password);return login;}@Bean //定義Spring組件并返回Beanpublic User user(){User user = new User();//注入組件Login的Bean時,直接調用方法login()user.setLogin(login());return user;}
}
(3)配置Spring應用上下文
? ? ? ?根據Java配置類創建Spring容器,根據容器提供的getBean()方法獲取相應的Bean,調用Bean的方法執行任務。
package com.my.examples.example3;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Test {public static void main(String[] args) {//根據Java配置類創建Spring容器AnnotationConfigApplicationContext context = newAnnotationConfigApplicationContext(Config.class);//獲取容器中的BeanLogin login = context.getBean(Login.class);User user = context.getBean(User.class);user.print();}
}
運行類Test,在控制臺中輸出如下內容:
login user: admin |
2.3. Spring容器分類
? ? ?Spring框架主要提供三種類型的容器:BeanFactory容器、ApplicationContext容器和WebApplicationContext。
2.3.1. BeanFactory容器
? ? ? ?BeanFactory是Spring框架中用于管理 Bean的最基本接口,它提供了一系列用于獲取Bean和管理 Bean依賴的方法。BeanFactory使用懶加載方式,即只有在需要時才創建和配置bean。
? ? ? ?BeanFactory接口包含的的主要方法:
- Object getBean(String name):根據bean的名稱在Spring容器中獲取對應的Bean實例。
- Object getBean(String name, Object... args):根據 Bean 的名稱和給定的構造函數參數獲取一個 Bean 實例。
- boolean containsBean(String name):判斷Spring容器中是否包含指定名稱的bean。
- boolean?isSingleton(String name):判斷指定名稱的bean是否是單例。
- boolean isPrototype(String name):判斷指定名稱的bean是否是原型。
? ? ?BeanFactory接口的主要實現類:
- XmlBeanFactory:這是一個簡單的實現,它從 XML 文件中加載 Bean 定義,從 Spring 3.1 開始,該類已被標記為過時。
- DefaultListableBeanFactory:它提供了一套完整的服務來管理不同的 Bean,包括注冊 Bean、解析依賴、管理 Bean 生命周期、以及提供不同的 Bean 作用域等。
2.3.2.?ApplicationContext容器
? ? ? ?ApplicationContext是BeanFactory的子接口,它在BeanFactory的基礎上增加了更多的功能。除了提供BeanFactory的所有功能外,ApplicationContext還提供了更多企業特定的功能,如國際化支持、事件發布、應用層特定的上下文等。ApplicationContext在容器啟動時就創建和配置所有的單例bean,提供了更多的高級特性。
? ? ? ApplicationContext接口的主要實現類:
- ClassPathXmlApplicationContext:從類路徑下的XML文件加載Spring應用上下文。這是在傳統的 Spring 應用中常見的一種方式,適合那些配置存儲在項目內部的XML文件中的應用。
- FileSystemXmlApplicationContext:從文件系統指定的位置的XML文件加載Spring應用上下文。這種方式適用于需要從外部文件系統(而非類路徑)加載配置的場景。
- AnnotationConfigApplicationContext:從一個或多個基于Java的配置類中加載Spring應用上下文。這種方式專門為基于Java注解的配置提供支持。
2.3.3.?WebApplicationContext容器
? ? ? ?WebApplicationContext是專門為 Web 應用程序設計的,它擴展了 ApplicationContext 接口,添加了一些針對Web環境的特定功能。WebApplicationContext通常在基于Spring的Web應用程序中使用,特別是在使用Spring MVC框架時,它通常在web.xml文件中配置,或者通過Spring 的WebApplicationInitializer接口在Java配置中啟動。
? ? ? ?WebApplicationContext接口的主要實現類:
- XmlWebApplicationContext:從XML文件加載配置的Web應用上下文,這是一個傳統的方法,適合那些已經使用XML配置文件的項目。
- AnnotationConfigWebApplicationContext:支持基于Java注解的配置,允許通過Java代碼直接配置Spring,使得配置更加直觀和類型安全。
3. Spring中的常用注解
? ? ? 在Spring框架中,與Spring容器相關的注解如下表。
注解 | 功能 |
@Component | 通用注解,用于標識一個類為Spring組件,當Spring框架掃描到該注解時,會創建該類的實例并納入Spring容器管理。 @Respository、@Service、@Controller都是這個注解的特殊形式,分別用于標識數據訪問層、服務層和控制層的Spring組件。 |
@Respository | 該注解用于標注數據訪問層組件,數據訪問層組件通常用于數據操作。 |
@Service | 該注解用于標注服務層組件,服務層組件通常用于業務邏輯處理。 |
@Controller | 該注解用于標注控制層組件,控制層組件通常用于處理HTTP請求。 |
@Autowired | 自動裝配注解,它可以放在類的成員變量、方法以及構造方法上,用于自動注入依賴,@Autowired是按照類型注入依賴,如果想要按照名稱來注入依賴,則需要結合@Qualifier注解一起使用。 |
@Resource | 與@Autowired功能一樣,區別在于該注解默認是按照名稱來注入依賴,只有當找不到與名稱匹配的Bean時才會按照類型來注入依賴 |
@Qualifier | 與@Autowired一起使用,用于指定要注入的Bean的名稱 |
@Configuration | 用于定義配置類,可替代XML文件,類中的方法可以使用@Bean注解,以聲明Spring容器管理的Bean。 |
@Bean | 用于標識一個方法,該方法返回一個Bean實例,并將其注冊到Spring容器中。 |
@Scope | 用于指定Bean的作用域,分為如下幾種: 1)singleton(單例):只創建一個Bean,這是默認的作用域,適用于無狀態Bean,比如服務層Bean、數據訪問層Bean; 2)prototype(原型):每次請求(通過getBean方法)都會創建一個新的Bean,適用于有狀態的Bean; 3)request(請求):在Web應用中,每一次HTTP請求都會產生一個新的Bean,該Bean僅在當前HTTP請求內有效; 4)session(會話):在Web應用中,每個HTTP會話都會創建一個新的Bean,該請求僅在當前HTTP會話內有效; 5)global-session(全局會話):僅在Portlet應用中使用,每個全局HTTP會話會創建一個Bean,適用于需要在全局HTTP會話期間保持狀態的Bean。 6)application(應用):在整個ServletContext范圍內,Bean都是單例的,適用于需要在整個Web用用范圍內共享的Bean。 |
@Value | 用于注入簡單類型的屬性值,如字符串、數字等。 |
@Async | 用于標識一個方法或類,表示其中的操作應該異步執行。 |
@PostConstruct | 用于標識一個方法,該方法在Bean實例化后自動執行,用于初始化操作。 |
@PreDestroy | 用于標識一個方法,該方法在Bean銷毀前自動執行,用于清理操作。 |