背景
Spring中,我們總是通過IOC肆無忌憚的去擷取Bean對象,本着飲水思源的道理,我們來針對Bean注入的各種姿勢進行詳細分析。其實重點還有一個,面試的時候我很喜歡提這個問題,Spring的Bean注入方式你了解哪些?而非Spring的Bean擷取方式。
1. XML注入
XML注入是一種最原生的注入方式,一個XML配置檔案,一個Bean配置對象,即可在Spring容器中注入一個Bean。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--通過反射機制生成 id與name字段指派則調用了 set 與 get 方法-->
<bean id="xmlUser" class="com.yiang.code.bean.entity.User">
<property name="id" value="10"/>
<property name="name" value="yiang"/>
</bean>
</beans>
以上代碼則代表在spring容器中注入一個id名為user的對象。擷取時通過 applicationContext.getBean("user", User.class); 擷取即可。
2. @Bean注入
@configuration + @Bean 注解,相當于XML注入方法的簡化注解形式,一個配置類加上@configuration 注解,在類中使用@Bean注解注入對象接口。ID為方法名稱
@Configuration
public class SpringTestConfig {
@Bean
public User user() {
return new User(2, "yiang");
}
}
以上代碼則表示在Spring容器中注入一個ID為2,名稱為yiang的使用者實體。關于Bean注解的其它說明可以參考該文章:偷窺Spring中的Bean加載。
3. @Conditional 條件判斷
@Bean的條件注入注解,與@Bean一起在對象注入時使用,可以通過@Conditional(WindowsCondition.class)指定條件處理類,處理類通過實作Condition接口,來進行相關的邏輯判斷,以下代碼則表示,如果作業系統為Windows10的情況下,才會放行将該Bean注入到Spring容器中。
public class WindowsCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
return "Windows 10".equals(property);
}
}
4. @Import 導入
@Import導入注解,使用在類頭,包含一個Class<?>[]的數組參數,可以傳入多個類,一般更多用于外部jar對象導入,導入的對象ID為其全路徑名,與@Bean注解的差別是@Bean的導入預設為方法名或者是首字母小寫的駝峰。使用方法如下
@Import({PayEntity.class})
public class SpringTestConfig {
表示注入配置的PayEntity類對象,注入後的ID如下:
com.yiang.code.entity.PayEntity
5. @EnableXXX 支援..導入
@EnableXXX是大部分架構的導包實作,很凸顯的栗子就是@EnableEureka(注冊中心)、@EnableFeignClients(Feign用戶端)、@EnableAsync(異步注解)。
我們翻看到@EnableAsync 異步注解源碼中,實際上在裡面的處理還是通過調用的@Import注解,那麼也就是說,@EnableXXX、注解實際上就是對@Import注解的一個封裝。使用時在配置導入的時候,僅僅隻需要使用一個@Enablexxx即可,無需指定導入某個類。更加簡單、整潔。
6. ImportSelector 接口 導入選擇器
通過手寫類實作ImportSelector接口,并且注入到Spring容器,可實作自定義去注入對象,實則在前面@EnableAsync時,指向的AsyncConfigurationSelector類就是采用了這種方式進行導包,通過實作ImportSelector(基類),然後實作selectImports方法,去傳回對應的類名注入到Spring容器。
7. ImportBeanDefinitionRegistrar接口 自定義Bean注冊器
通過手寫類實作ImportBeanDefinitionRegistrar接口,并且注入到Spring容器,注入形式和@ImportSelector接口無差距,通過實作接口方法即可。以下代碼則代表注入RegisterEntity
類到容器中,ID為registerEntity。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(RegisterEntity.class);
registry.registerBeanDefinition("registerEntity", rootBeanDefinition);
}
}
8. FactoryBean接口 執行個體注冊工廠
這裡需要注意的是FactoryBean接口 與BeanFactory的差別,本接口是注冊Bean對象的,而BeanFactory接口則是擷取Bean對象。
實作方式也比較簡單,isSingleton方法決定是否單例,預設不會強制實作,是false,實作改為true則是單例模式,其它就是傳回對應的對象與類執行個體。
public class MyFactoryBean implements FactoryBean<MyFactoryBeanEntity> {
public MyFactoryBeanEntity getObject() {
return new MyFactoryBeanEntity();
}
public Class<?> getObjectType() {
return MyFactoryBeanEntity.class;
}
public boolean isSingleton() {
return true;
}
}
注入時可以使用@Bean注冊該類,亦可以使用@Import導入
@Bean
public MyFactoryBean myFactoryBean() {
return new MyFactoryBean();
}
9. @Component元件注解 @Service、@Repository、@Controller
正常項目開發架構中,用的最多的也就是這幾個注解了。@Repository代表Mapper檔案Dao層,@Service代表業務邏輯層,@Controller代表控制層,這個需要注意的是@RestController并非Spring包自帶的注解。是以不在此說明。實際上進行代碼分組的三個注解核心都是依賴的@Component注解。隻做區分,其實内部并無差距。
總結
以上即為9種Spring中Bean的注入方式, 很多方式其實屬于大同小異,可以根據需求靈活運用。我們亦可以在實際項目中選擇自己喜歡的方式,保證其适用性與合理性即可。