個(gè)人博客: 全是干貨,相信不會讓你失望
1.XML方式注入
在現(xiàn)在這個(gè)Springboot橫行的年代,以XML來注入的方式可能已經(jīng)不多見了,因?yàn)閴焊貌恢?,但畢竟是注入方式之一也得提一提,這種方式就是依賴于XML的解析來獲取我們需要注入的Bean對象
常見的方式有:set方法注入、構(gòu)造方法注入
這里舉幾個(gè)常見的例子:
set方式注入
// 實(shí)體類如下:
@Data
public class test {
private String name;
private Integer sex;
}
// 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--set方式注入
id是注入bean中的名字
class 是全限定類名
property 是按照set方式注入
-->
<bean id="student1" class="com.example.spkie.model.test">
<property name="name" value="test"/>
<property name="sex" value="10"/>
</bean>
</beans>
測試:
構(gòu)造方法注入
// 實(shí)體類如下:
@Data
public class test {
private String name;
private Integer sex;
public test(String name,Integer sex){
this.name=name;
this.sex=sex;
}
}
// XML文件如下 test.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--set方式注入
id是注入bean中的名字
class 是全限定類名
constructor-arg 是按照構(gòu)造方式注入
index 是按照成員變量在構(gòu)造函數(shù)中的參數(shù)的第幾個(gè)
name 表示成員變量名
type 表示類型
value 表示值
ref 表示引用 可引用另外一個(gè)注入到Spring的中的值
-->
<bean id="student1" class="com.example.spkie.model.test">
<constructor-arg index="0" name="name" value="構(gòu)造方法注入"></constructor-arg>
<constructor-arg index="1" name="sex" value="50"></constructor-arg>
</bean>
</beans>
測試:
2.注解方式注入
@Component+@ComponentScan
我們開發(fā)中常用的 @Service和 @Controller 都是 @Component下的注解 ,需要配合 @ComponentScan 注解才能被掃描到并放入IOC容器中
為什么平時(shí)卻沒用@ComponentScan注解呢?
因?yàn)槠綍r(shí)用的都是Springboot,Springboot啟動(dòng)類上的 @SpringbootApplication 注解類下已經(jīng)帶有 @ComponentScan 注解了,默認(rèn)掃描啟動(dòng)類同級包下的@Component
例子如下:
我們先準(zhǔn)備一個(gè)獲取IOC容器內(nèi)bean 的工具類 SpringUtils:
@Component
public final class SpringUtils implements BeanFactoryPostProcessor {
/**
* Spring應(yīng)用上下文環(huán)境
*/
private static ConfigurableListableBeanFactory beanFactory;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
SpringUtils.beanFactory = beanFactory;
}
public static <T> T getBean(Class<T> clz) throws BeansException {
T result = (T) beanFactory.getBean(clz);
return result;
}
public static <T> T getBean(String name) throws BeansException {
return (T) beanFactory.getBean(name);
}
}
測試要注入的Bean實(shí)體類:
@Component
@Data
public class ComponentTest {
private String name="@Component 注解注入";
private String remark="注意需要配合@ComponentScan 注解使用";
}
可以看到報(bào)錯(cuò)了,壓根找不到這個(gè)bean,因?yàn)槲覀兩厦嬲f過了springboot默認(rèn)掃描的是啟動(dòng)類同級下的路徑,我們把啟動(dòng)類放到了獨(dú)立的包下,所以掃描不到了,這時(shí)候我們要么在用@ComponentScan注解配置一次掃描路徑,要么把啟動(dòng)類提出來,我這里演示前者
我們在啟動(dòng)類上加上@ComponentScan注解配置一次掃描路徑,就可以看到注入成功啦
@Configuration+@Bean+@ComponentScan
@Configuration注解相信大家也都不陌生,這個(gè)注解同樣要配合@ComponentScan使用,那到底和@Component有什么區(qū)別呢?
@Configuration注入的是CGlib代理類,@Component注入的是類本身
我們與 @Component 一樣準(zhǔn)備個(gè) @Configuration 注入類:
@Configuration
@Data
public class ConfigurationTest {
private String name="@Configuration 注解注入";
private String remark="注意需要配合@ComponentScan 注解使用";
}
可以看到Bean類的本質(zhì)區(qū)別,難道為了這個(gè)就搞了@Configuration注解嗎?當(dāng)然不是,這個(gè)注解還可以配合@Bean注解一起使用,用來同時(shí)注入多個(gè)Bean
// 添加一個(gè)額外的Bean對象
public class ConfigurationTestBean {
public void test(){
System.out.println("我是在Configuration 內(nèi)部注入的 bean ");
}
}
// ConfigurationTest中添加Bean方法
@Configuration
@Data
public class ConfigurationTest {
private String name="@Configuration 注解注入";
private String remark="注意需要配合@ComponentScan 注解使用";
// ConfigurationTest 中需要注入的Bean
@Bean
public ConfigurationTestBean configurationTestBean(){
return new ConfigurationTestBean();
}
}
這樣的@Bean可以在同一個(gè)類中注入多個(gè),所以 @Component 更多的用來注入配置文件類,@Configuration 更多的用來注入多個(gè)實(shí)例類
@Import
這種方式一般用在第三方包的加載比較多,使用起來呢也簡單需要注入哪個(gè)Bean,導(dǎo)入哪個(gè)Bean的class就可以了,例如:
// 導(dǎo)入單個(gè)Bean
@Import(xxxxBean.class)
// 導(dǎo)入多個(gè)Bean
@Import({xxxxBean.class,xxxxBean.class})
但這個(gè)注解使用得注意,一定要能被掃描到才行,可以直接放在啟動(dòng)類上,如果是普通需要配合@Component或者@Configuration來使用,因?yàn)榇俗⒔鈫为?dú)使用是不會被掃描到的,也就不會被加載了
在一個(gè)注解上導(dǎo)入多個(gè)Bean要寫這么多可能不是很優(yōu)雅,所以還可以配合ImportSelector接口使用:
// 導(dǎo)入實(shí)現(xiàn)了ImportSelector接口的類即可
@Import(MyImportSelector.class)
// 實(shí)現(xiàn)ImportSelector 在數(shù)組中配置需要導(dǎo)入的Bean路徑 返回一個(gè)數(shù)組
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.example.spkie.importTest.xxxxBean","com.example.spkie.importTest.xxxxBean"};
}
}
3.實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口
看接口名稱就知道了是不是有點(diǎn)像Bean的注冊接口,需要配合@Import使用:
// 使用注解注入
@Import({MyImportBeanDefinitionRegistrar.class})
// 需要注入的Bean
public class DefinitionRegistrarBean {
public void test(){
System.out.println("我是通過Bean注冊接口注入的Bean,需要配合@Import注解同樣需要被掃描");
}
}
// 自定義類
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(DefinitionRegistrarBean.class);
registry.registerBeanDefinition("definitionRegistrarBean",beanDefinitionBuilder.getBeanDefinition());
}
}
測試:
我們平時(shí)開發(fā)中常用的openfeign也是采用的這種方式:
4.實(shí)現(xiàn)FactoryBean
用這個(gè)得先搞清楚FactoryBean和BeanFactory的區(qū)別:
- BeanFactory: IOC容器頂層接口,用來Bean容器管理
- FactoryBean: 是一個(gè)bean,是一個(gè)能產(chǎn)生bean的工廠bean,本身也會作為bean給容器管理,所以作為一個(gè)能產(chǎn)生Bean的工廠,我們可以自定義Bean(這也是最關(guān)鍵的點(diǎn))
讓我們來看看怎么用:
// 這是我們利用工廠想要生產(chǎn)的bean
public class FactoryTestBean {
public void test(){
System.out.println("我是通過實(shí)現(xiàn)FactoryBean接口注入的Bean");
}
}
// 工廠Bean 實(shí)現(xiàn)兩個(gè)方法
@Component
public class MyFactoryBean implements FactoryBean<FactoryTestBean> {
//這個(gè)方法就是我們要生產(chǎn)的Bean
@Override
public FactoryTestBean getObject() throws Exception {
return new FactoryTestBean();
}
@Override
public Class<?> getObjectType() {
return FactoryTestBean.class;
}
}
測試:
可以看到通過Class無論是工廠bean還是工廠生產(chǎn)的bean我們都可以獲取,但是發(fā)現(xiàn)通過beanName獲取bean的區(qū)別沒有,我們通過工廠的beanName獲取到的是實(shí)際生產(chǎn)的對象,要獲取真正的工廠需要在beanName前面加上&
為什么通過工廠的beanName獲取到的是實(shí)際生產(chǎn)的對象?
其實(shí)從上述注入的過程中也能看到我們往容器中注入的其實(shí)是工廠Bean,并沒有注入工廠生產(chǎn)的那個(gè)對象(可以打印容器所有的beanName驗(yàn)證),可以理解為在從容器中獲取Bean的時(shí)候有判斷是否實(shí)現(xiàn)了FactoryBean接口,實(shí)現(xiàn)了則會調(diào)用該bean的getObject()方法返回,所以此時(shí)會返回實(shí)際工廠生產(chǎn)的對象了
我們一樣以openfeign框架舉例:
此注入的feign接口實(shí)際注入的是FeignClientFactoryBean,所以在調(diào)用容器中feign接口的Bean對象的時(shí)候,實(shí)際執(zhí)行的是FeignClientFactoryBean.getObject()方法
5.實(shí)現(xiàn)BeanDefinitionRegistryPostProcessor
這個(gè)接口繼承了BeanFactoryPostProcessor接口,BeanFactoryPostProcessor是BeanFactory的后置處理器,該接口多個(gè)了一個(gè)對BeanDefination處理的方法,可以在BeanFactory生成后對里面的BeanDefination做一次處理,所以當(dāng)然可以注冊BeanDefination啦,后續(xù)就成了Bean
BeanDefinitionRegistryPostProcessor源代碼如下:
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
}
怎么用呢?先直接上例子吧:
// 還是要搭配注解
@Import(MyBeanDefinitionRegistryPostProcessor.class)
// 要注入的bean對象
public class RegistrarPostProcessorBean {
public void test(){
System.out.println("我是通過后置處理器注入的bean");
}
}
// 自定義類
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(RegistrarPostProcessorBean.class);
beanDefinitionRegistry.registerBeanDefinition("registrarPostProcessorBean",beanDefinitionBuilder.getBeanDefinition());
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
乍一看和ImportBeanDefinitionRegistrar類似,都是用了BeanDefinitionRegistry 來注冊的,但I(xiàn)mportBeanDefinitionRegistrar是Spring的擴(kuò)展點(diǎn)之一,提供給第三方對接使用的
BeanDefinitionRegistryPostProcessor這個(gè)源碼就不追溯了,后面再說(還是提一下吧,容器初始化的時(shí)候有調(diào)用)
既然是BeanFactory后置處理器,所以它還可以修改BeanDefination里面保存的Bean信息:
// 我們用到之前使用過的Bean
@Component
@Data
public class ComponentTest {
private String name="@Component 注解注入";
private String remark="注意需要配合@ComponentScan 注解使用";
}
// 修改后置處理器
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
// 新增如下代碼 修改ComponentTest Bean屬性
BeanDefinition configurationTestBean = beanDefinitionRegistry.getBeanDefinition("componentTest");
MutablePropertyValues propertyValues = configurationTestBean.getPropertyValues();
propertyValues.add("name","我是修改后的Bean屬性" );
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(RegistrarPostProcessorBean.class);
beanDefinitionRegistry.registerBeanDefinition("registrarPostProcessorBean",beanDefinitionBuilder.getBeanDefinition());
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
結(jié)果如下:文章來源:http://www.zghlxwxcb.cn/news/detail-428014.html
這里演示了修改字段的值,當(dāng)然還可以修改其他的比如是否加載優(yōu)先級、是否懶加載、單例多例等文章來源地址http://www.zghlxwxcb.cn/news/detail-428014.html
到了這里,關(guān)于Spring——Bean注入幾種方式(放入容器)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!