慢慢來比較快,虛心學技術
前言:創建應用對象之間協作關係的行爲通常稱爲裝配( wiring ),這也是依賴注入( DI )的本質
Spring提供三種Bean裝配機制:
- 在 XML 中進行顯式配置。
- 在 Java 中進行顯式配置
- 隱式的 bean 發現機制和自動裝配
一、什麼是自動化裝配?
很顯然,通過前兩篇文章的描述,我們可以發現,Spring的配置方式可以很簡潔,也可以也很複雜,JavaConfig配置方式使用大量的註解替代了XML中的配置,那麼,基於JavaConfig的基礎之上,是否可以再次封裝或簡化配置呢?
Spring實現了純註解配置的自動化隱式裝配,所謂的隱式裝配就是不需要像XML和JavaConfig一樣去爲每一個Bean創建<bean>節點或再配置類中爲每個Bean做註解,而是通過一些特殊的註解實現控制
其中包含兩個概念:
- 組件掃描( component scanning ): Spring 會自動發現應用上下文中所創建的 bean 。
- 自動裝配( autowiring ): Spring 自動滿足 bean 之間的依賴。【DI】
二、如何實現自動化裝配
ⅰ.四個基本註解:
@Autowired :標記於屬性,方法等,自動裝配的關鍵註解,依賴注入的表現,該註解可以自動尋找並從Spring容器中提取使用該註解的bean並注入到對應的屬性中去
@Component :標記於類,標明當前類是一個可被掃描的組件
@ComponentScan :標記於配置類,開啓組件註解掃描
@Configuration :標記於配置類,標明當前類是一個配置類
ⅱ.基本實現:
①定義基本接口CDPlayer
public interface CDPlayer {
/**
* 定義方法播放CD
* @param
*
* @return void
*
* @author lai.guanfu 2019/2/27
* @version 1.0
**/
void playCD();
}
②定義基本類CDBean
@Data//Data是lobok的註解,自動添加setter和getter方法等
@Component//將當前類定義爲可掃描的組件
public class CDBean {
/**
* 定義CD名
*/
private String title="The World!";
/**
* 定義CD作者
*/
private String author="Mr.D";
}
③定義實現類CDPlayerImpl,並將CDBean作爲屬性注入
@Component//將當前類定義爲可掃描的組件
public class CDPlayerImpl implements CDPlayer {
/**
* 從Spring容器注入CDBean-----依賴注入
*/
@Autowired
private CDBean cdBean;
@Override
public void playCD() {
System.out.println("正在播放:"+cdBean.getTitle()+" by "+cdBean.getAuthor());
}
}
④定義配置類CDConfig,並開啓註解掃描
@ComponentScan()//開啓註解掃描
@Configuration//指定當前類爲配置類
public class CDConfig {}
⑤編寫測試
此處使用Spring提供的測試類SpringJUnit4ClassRunner幫助測試,以便在測試開始的時候自動創建 Spring 的應用上下文
而@ContextConfiguration可以指定創建上下文的加載方式以及配置的位置等
@RunWith(SpringJUnit4ClassRunner.class)//輔助創建Spring應用上下文
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,classes = {CDConfig.class})//指定創建上下文的加載方式以及配置的位置
public class AppTest
{
/**
* 從Spring容器注入CdPlayer-----依賴注入
*/
@Autowired
private CDPlayer cdPlayer;
/**
* Rigorous Test :-)
*/
@Test
public void play()
{
//調用playCD方法測試是否自動裝配及依賴注入成功
this.cdPlayer.playCD();
}
}
⑥測試結果,自動裝配成功,依賴注入成功
正在播放:The World! by Mr.D
ⅲ.關注點:
① 提供另一種測試方式:通過上一篇文章中提及的AnnotationConfigApplicationContext上下文實現進行測試
public static void main(String[] args) {
/**
* 註解配置實現,同時指定配置類的位置,否則無法讀取配置進行掃描,默認只有Spring自帶的組件
*/
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(CDConfig.class);
/**
*獲取應用上下文中的所有Bean名稱
*/
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String className : beanDefinitionNames){
System.out.println(className);
}
/**
*從上下文(容器)中獲取對應的Bean
*/
CDPlayer cdPlayer = applicationContext.getBean(CDPlayer.class);
/**
*調用具體方法測試是否成功
*/
cdPlayer.playCD();
}
測試結果:自動裝配成功,依賴注入成功
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
CDConfig
app//當前測試類的名字
CDBean
CDPlayerImpl
正在播放:The World! by Mr.D
② 爲裝配的Bean命名
Spring 應用上下文中所有的 bean 都會給定一個 ID,默認是類名的首字母小寫命名,我們可以爲裝配的Bean裝配一個不一樣的ID,在 @Component註解中有一個屬性value,該值可指定bean在Spring容器中的ID,其在@Component註解的源碼定義如下:
public @interface Component {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
String value() default "";
}
我們可以給CDPlayerImpl指定一個別的ID:cdPlayer
@Component(value = "cdPlayer")
public class CDPlayerImpl implements CDPlayer {
@Autowired
private CDBean cdBean;
@Override
public void playCD() {
System.out.println("正在播放:"+cdBean.getTitle()+" by "+cdBean.getAuthor());
}
}
此時執行上一測試代碼結果中,CDPlayerImpl在容器中的ID類名將變成cdPlayer
另:可以使用@Name代替@Component註解爲Bean命名,但是爲了避免概念模糊,建議使用@Component註解
③ 設置組件掃描的基礎包
上述代碼中,配置類CDConfig僅能掃描自身所在包及其子包的組件,能否指定掃描某個或多個包及其子包中的組件呢?
我們看到@ComponentScan註解的源碼中定義的幾個屬性:
public @interface ComponentScan {
/**
* Alias for {@link #basePackages}.
* <p>Allows for more concise annotation declarations if no other attributes
* are needed — for example, {@code @ComponentScan("org.my.pkg")}
* instead of {@code @ComponentScan(basePackages = "org.my.pkg")}.
*/
@AliasFor("basePackages")
String[] value() default {};
/**
* Base packages to scan for annotated components.
* <p>{@link #value} is an alias for (and mutually exclusive with) this
* attribute.
* <p>Use {@link #basePackageClasses} for a type-safe alternative to
* String-based package names.
*/
@AliasFor("value")
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages} for specifying the packages
* to scan for annotated components. The package of each class specified will be scanned.
* <p>Consider creating a special no-op marker class or interface in each package
* that serves no purpose other than being referenced by this attribute.
*/
Class<?>[] basePackageClasses() default {};
}
從源碼得知,我們可以通過配置basePackages和basePackageClasses屬性來指定配置掃描的範圍,其中:
basePackages:指定組件掃描的包路徑(使用{}包含的數組)
@ComponentScan(basePackages = {"com.my.spring","com.my.test"})//指定掃描com.my.spring包和com.my.test包及其子包下的組件
@Configuration
public class CDConfig {}
basePackageClasses:指定以該類數組所在包及其子包爲組件掃描範圍
@ComponentScan(basePackageClasses = {App.class})//指定掃描App.class所在包及其子包下的組件
@Configuration
public class CDConfig {}
注:通常,爲了防止因包路徑更改以及業務實體類更改等因耦合而產生的問題,我們通常會使用一個不具備任何業務意義的空接口作爲掃描包的類
④自動注入註解@Autowired的使用
可以用在屬性,構造函數,setter以及任何一個普通方法中
@Component(value = "cdPlayer")
public class CDPlayerImpl implements CDPlayer {
//1.在屬性注入
@Autowired
private CDBean cdBean;
public CDPlayerImpl(){super()};
//2.在構造函數中注入
@Autowired
public CDPlayerImpl(CDBean cdBean){super()};
//3.在setter方法中注入
@Autowired
public void setCDBean(CDBean cdBean){this.cdBean = cdBean};
//4.在普通方法中使用
@Autowired
@Override
public void playCD(CDBean cdBean) {
System.out.println("正在播放:"+cdBean.getTitle()+" by "+cdBean.getAuthor());
}
}
總結
1.自動化裝配使用全註解方式替代XML和JavaConfig顯式裝配方式,方便簡潔
2.自動化裝配依賴於幾個特殊註解:@Autowired,@Component,@ComponentScan和@Configuration
3.@Component註解可以將類定義爲裝配的組件,同時可以爲改組件另起別名(ID)
4.@Configuration將使用該註解的當前類標註爲配置類,@ComponentScan開啓自動掃描,默認掃描當前配置類所在的包及其子包中含有或使用了@Component註解的類,可通過屬性指定掃描範圍
5.@Autowired註解可以用在屬性,構造函數,setter以及任何一個普通方法中,是Spring依賴注入的核心註解
參考文檔
【1】《Spring 實戰(第 4 版)》·Craig Walls