一提到Spring,大家最先想到的是啥?是AOP和IOC的兩大特性?是Spring中Bean的初始化流程?還是基于Spring的Spring Cloud全家桶呢?
今天我們就從Spring的IOC特性入手,聊一聊Spring中把Bean注入Spring容器的幾種方式,
我們先來簡單了解下IOC的概念:IOC即控制反轉,也稱為依賴注入,是指將物件的創建或者依賴關系的參考從具體的物件控制轉為框架或者IOC容器來完成,也就是依賴物件的獲得被反轉了,
可以簡單理解為原來由我們來創建物件,現在由
Spring來創建并控制物件,
xml 方式
依稀記得最早接觸Spring的時候,用的還是SSH框架,不知道大家對這個還有印象嗎?所有的bean的注入得依靠xml檔案來完成,
它的注入方式分為:set方法注入、構造方法注入、欄位注入,而注入型別分為值型別注入(8種基本資料型別)和參考型別注入(將依賴物件注入),
以下是set方法注入的簡單樣例
<bean name="teacher" >
<property name="name" value="https://www.cnblogs.com/aqsaycode/archive/2022/04/21/阿Q"></property>
</bean>
對應的物體類代碼
public class Teacher {
private String name;
public void setName(String name) {
this.name = name;
}
}
xml方式存在的缺點如下:
xml檔案配置起來比較麻煩,既要維護代碼又要維護組態檔,開發效率低;- 專案中組態檔過多,維護起來比較困難;
- 程式編譯期間無法對配置項的正確性進行驗證,只能在運行期發現并且出錯之后不易排查;
- 決議
xml時,無論是將xml一次性裝進記憶體,還是一行一行決議,都會占用記憶體資源,影響性能,
注解方式
隨著Spring的發展,Spring 2.5開始出現了一系列注解,除了我們經常使用的@Controller、@Service、@Repository、@Component 之外,還有一些比較常用的方式,接下來我們簡單了解下,
@Configuration + @Bean
當我們需要引入第三方的jar包時,可以用@Bean注解來標注,同時需要搭配@Configuration來使用,
-
@Configuration用來宣告一個配置類,可以理解為xml的<beans>標簽 -
@Bean用來宣告一個bean,將其加入到Spring容器中,可以理解為xml的<bean>標簽
簡單樣例:將 RedisTemplate 注入 Spring
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
......
return redisTemplate;
}
}
@Import
我們在翻看Spring原始碼的程序中,經常會看到@Import注解,它也可以用來將第三方jar包注入Spring,但是它只可以作用在類上,
例如在注解EnableSpringConfigured上就包含了@Import注解,用于將SpringConfiguredConfiguration組態檔加載進Spring容器,
@Import(SpringConfiguredConfiguration.class)
public @interface EnableSpringConfigured {}
@Import的value值是一個陣列,一個一個注入比較繁瑣,因此我們可以搭配ImportSelector介面來使用,用法如下:
@Configuration
@Import(MyImportSelector.class)
public class MyConfig {}
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"org.springframework.demo.model.Teacher","org.springframework.demo.model.Student"};
}
}
其中selectImports方法回傳的陣列就會通過@Import注解注入到Spring容器中,
無獨有偶,ImportBeanDefinitionRegistrar介面也為我們提供了注入bean的方法,
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
......
}
我們點擊AspectJAutoProxyRegistrar類,發現它實作了ImportBeanDefinitionRegistrar介面,它的registerBeanDefinitions方法便是注入bean的程序,可以參考下,
如果覺得源代碼比較難懂,可以看一下我們自定義的類
@Configuration
@Import(value = https://www.cnblogs.com/aqsaycode/archive/2022/04/21/{MyImportBeanDefinitionRegistrar.class})
public class MyConfig {}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
RootBeanDefinition tDefinition = new RootBeanDefinition(Teacher.class);
// 注冊 Bean,并指定bean的名稱和型別
registry.registerBeanDefinition("teacher", tDefinition);
}
}
}
這樣我們就把Teacher類注入到Spring容器中了,
FactoryBean
提到FactoryBean,就不得不與BeanFactory比較一番,
BeanFactory: 是Factory,IOC容器或者物件工廠,所有的Bean都由它進行管理FactoryBean: 是Bean,是一個能產生或者修飾物件生成的工廠Bean,實作與工廠模式和修飾器模式類似
那么FactoryBean是如何實作bean注入的呢?
先定義實作了FactoryBean介面的類
public class TeacherFactoryBean implements FactoryBean<Teacher> {
/**
* 回傳此工廠管理的物件實體
**/
@Override
public Teacher getObject() throws Exception {
return new Teacher();
}
/**
* 回傳此 FactoryBean 創建的物件的型別
**/
@Override
public Class<?> getObjectType() {
return Teacher.class;
}
}
然后通過 @Configuration + @Bean的方式將TeacherFactoryBean加入到容器中
@Configuration
public class MyConfig {
@Bean
public TeacherFactoryBean teacherFactoryBean(){
return new TeacherFactoryBean();
}
}
注意:我們沒有向容器中注入Teacher, 而是直接注入的TeacherFactoryBean,然后從容器中拿Teacher這個型別的bean,成功運行,
BDRegistryPostProcessor
看到這個介面,不知道對于翻看過Spring原始碼的你來說熟不熟悉,如果不熟悉的話請往下看,要是熟悉的話就再看一遍吧??,
原始碼
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
// 注冊bean到spring容器中
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
BeanFactoryPostProcessor介面是BeanFactory的后置處理器,方法postProcessBeanFactory對bean的定義進行控制,今天我們重點來看看postProcessBeanDefinitionRegistry方法:它的引數是BeanDefinitionRegistry,顧名思義就是與BeanDefinition注冊相關的,

通過觀察該類,我們發現它里邊包含了registerBeanDefinition方法,這個不就是我們想要的嗎?為了能更好的使用該介面來達到注入bean的目的,我們先來看看Spring是如何操作此介面的,

看下invokeBeanFactoryPostProcessors方法,會發現沒有實作PriorityOrdered和Ordered的bean(這種跟我們自定義的實作類有關)會執行以下代碼,
while (reiterate) {
......
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
......
}
進入該方法
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors,
BeanDefinitionRegistry registry) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
會發現實作了BeanDefinitionRegistryPostProcessor介面的bean,其postProcessBeanDefinitionRegistry方法會被呼叫,也就是說如果我們自定義介面實作該介面,它的postProcessBeanDefinitionRegistry方法也會被執行,
實戰
話不多說,直接上代碼,自定義介面實作類
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
/**
* 初始化程序中先執行
**/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Teacher.class);
//Teacher 的定義注冊到spring容器中
registry.registerBeanDefinition("teacher", rootBeanDefinition);
}
/**
* 初始化程序中后執行
**/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
}
啟動類代碼
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
MyBeanDefinitionRegistryPostProcessor postProcessor = new MyBeanDefinitionRegistryPostProcessor();
//將自定義實作類加入 Spring 容器
context.addBeanFactoryPostProcessor(postProcessor);
context.refresh();
Teacher bean = context.getBean(Teacher.class);
System.out.println(bean);
}
啟動并列印結果
org.springframework.demo.model.Teacher@2473d930
發現已經注入到Spring容器中了,
以上就是我們總結的幾種將bean注入Spring容器的方式,趕快行動起來實戰演練一下吧!
阿Q將持續更新java實戰方面的文章,感興趣的可以關注下,也可以來技術群討論問題呦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/460769.html
標籤:其他
