曹工說Spring Boot源碼(2)-- Bean Definition到底是什么,咱們對着接口,逐個方法講解


寫在前面的話

相關背景及資源:

曹工說Spring Boot源碼系列開講了(1)-- Bean Definition到底是什么,附spring思維導圖分享

工程代碼地址 思維導圖地址

工程結構圖:

正文

我這里,先把org.springframework.beans.factory.config.BeanDefinition接口的方法再簡單列一下:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
	// scope:單例
	String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
	// scope:prototype
	String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
	// 角色:屬於應用程序的bean
	int ROLE_APPLICATION = 0;
	// 角色:支持?不甚了解,先跳過
	int ROLE_SUPPORT = 1;
	// 角色:屬於框架自身的bean
	int ROLE_INFRASTRUCTURE = 2;
	// parent bean的名字
	String getParentName();
	
	void setParentName(String parentName);
	// 核心屬性,此為bean的class名稱
	String getBeanClassName();

	void setBeanClassName(String beanClassName);
	// 核心屬性,本屬性獲取工廠bean的名稱,getFactoryMethodName獲取工廠方法的名稱,配合使用,生成     	 // 本bean
	String getFactoryBeanName();

	void setFactoryBeanName(String factoryBeanName);

    String getFactoryMethodName();

	void setFactoryMethodName(String factoryMethodName);
	//scope,bean是單例的,還是每次new一個(prototype),就靠它了
	String getScope();

	void setScope(String scope);
	// 懶bean?默認情況下,都是容器啟動時,初始化;如果設置了這個值,容器啟動時不會初始化,首次getBean	// 時才初始化
	boolean isLazyInit();

	void setLazyInit(boolean lazyInit);
	// 在本bean初始化之前,需要先初始化的bean;注意,這里不是說本bean依賴的其他需要注入的bean
	String[] getDependsOn();

	void setDependsOn(String[] dependsOn);
	// 是否夠資格作為自動注入的候選bean。。。如果這里返回false,那就連自動注入的資格都沒得
	boolean isAutowireCandidate();

	void setAutowireCandidate(boolean autowireCandidate);
	// 當作為依賴,要注入給某個bean時,當有多個候選bean時,本bean是否為頭號選手
	boolean isPrimary();

	void setPrimary(boolean primary);
	
  	// 通過xml <bean>方式定義bean時,通過<constructor-arg>來定義構造器的參數,這里即:獲取構造器參數
	ConstructorArgumentValues getConstructorArgumentValues();
	
    // 通過xml <bean>方式定義bean時,通過 <property name="testService" ref="testService"/> 這種方     式來注入依賴,這里即:獲取property注入的參數值
	MutablePropertyValues getPropertyValues();
	// 是否單例
	boolean isSingleton();
	// 是否prototype
	boolean isPrototype();

	// 是否為抽象的,還記得<bean>方式定義的時候,可以這樣指定嗎?<bean id="testByPropertyController" class="org.springframework.simple.TestByPropertyController" abstract="true">
	boolean isAbstract();

	// 獲取角色
	int getRole();

	// 獲取描述
	String getDescription();
	
	String getResourceDescription();
	// 未知。。。
	BeanDefinition getOriginatingBeanDefinition();
}

beanName

雖然這個接口里沒這個東西,但這個我要重點說下,默認規則是:beanClassName按駝峰轉換后的名字。

這里面有個重點是,org.springframework.beans.factory.support.DefaultListableBeanFactory中,采用了下面的字段來存bean和對應的BeanDefinition。

/** Map of bean definition objects, keyed by bean name */
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);

這里說了,key是beanName。

那大家想過沒,我如果同一個上下文中,有兩個beanName相同的BeanDefinition會怎樣呢?

之前spring cloud項目集成feign時,我們的代碼是下面這樣的,即假設生產者提供了10個服務,分屬不同的模塊:

@FeignClient(name = "SCM",path = "scm",configuration = { FeignConfig.class })
public interface ScmCenterService extends ScmFeignCenterService {
}
@FeignClient(name = "SCM",path = "scm",configuration = { FeignConfig.class })
public interface ScmClientConfigService extends ScmFeignClientConfigService {
}

我們這里,就是按照不同模塊,在多個接口里來繼承feign api。

結果呢,啟動報錯了,就是因為beanName重復了,具體可以參考下面的鏈接:

【Feign】@FeignClient相同名字錯誤 The bean 'xxx.FeignClientSpecification', defined in null, could not be registered

最終解決這個問題,就是要加個配置,

spring:
  main:
    allow-bean-definition-overriding: true

這個配置,在spring之前的版本里,默認是true,結果在spring boot里,默認改為false了。

我這邊通過下面的代碼測試了一下:

  1. 當這個配置為true時

    	public static void main(String[] args) throws Exception {
    		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:application-context.xml"},false);
            // 這里設為true,不設也可以,默認就是true
            context.setAllowBeanDefinitionOverriding(true);
    		context.refresh();
    
            DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory();
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
            beanDefinition.setBeanClass(String.class);
            beanFactory.registerBeanDefinition("testService", beanDefinition);
    

    console中有如下提示:

    信息: Overriding bean definition for bean 'testService': replacing [Generic bean: class [org.springframework.simple.TestService]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [application-context.xml]] with [Generic bean: class [java.lang.String]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null]

  2. 當這個配置為false時

    context.setAllowBeanDefinitionOverriding(false);
    

    會直接報錯:

    Invalid bean definition with name 'testService' defined in null: Cannot register bean definition [Generic bean: class [java.lang.String]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] for bean 'testService':

    //這里說,早已存在xxx

    There is already [Generic bean: class [org.springframework.simple.TestService]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [application-context.xml]] bound.

    測試代碼在:

    https://gitee.com/ckl111/spring-boot-first-version-learn/tree/master/all-demo-in-spring-learning/spring-xml-demo

scope

這個不多說了,默認為singleton,在容器內只會有一個bean。prototype,每次getBean的時候都會new一個對象,這個一般不會在啟動時去初始化,如果寫的有問題的話,啟動時不報錯,runtime的時候報運行時異常。

其他幾個scope,web相關的,先不多說。

parentName

指定parentBean的名稱,以前xml的時候可能會用,現在注解驅動了,基本很少用了。

beanClassName

核心屬性,bean的class類型,這里說的是實際類型,而一般不是接口的名稱。比如,我們的注解一般是加在class上的,而不是加在接口上,對吧;即使加在接口上,那肯定也是動態代理技術,對吧,畢竟,bean是要以這個class的元數據來進行創建(一般通過反射)

factoryBeanName、factoryMethodName

如果本bean是通過其他工廠bean來創建,則這兩個字段為對應的工廠bean的名稱,和對應的工廠方法的名稱

lazyInit

是否延遲初始化,取值為true、false、default。

Indicates whether or not this bean is to be lazily initialized.
If false, it will be instantiated on startup by bean factories
that perform eager initialization of singletons. The default is
"false".

簡單說:如果設了這個為true,則啟動時不初始化;否則在啟動時進行初始化,這也是spring官方推薦的,可以盡早發現問題。

dependsOn

通過這個屬性設置的bean,會保證先於本bean被初始化

The names of the beans that this bean depends on being initialized.
The bean factory will guarantee that these beans get initialized
before this bean.

autowireCandidate,布爾

這個是個boolean值,表示是否可以被其他的bean使用@autowired的方式來注入。如果設置為false的話,那完了,沒資格被別人注入。

primary,布爾

表示當有多個候選bean滿足@autowired要求時,其中primary被設置為true的,會被注入;否則會報二義性錯誤,即:程序期待注入一個,卻發現了很多個。

constructorArgumentValues

構造函數屬性值。我測試發現,通過下面的方式,這個字段是用不上的:

public class TestController {

    TestService testService;
    
    @Autowired
    public TestController(TestService testService) {
        this.testService = testService;
    }
}

這個字段,什么時候有值呢,當采用下面的方式的時候,就會用這種:

public class TestController {

    TestService testService;

    public TestController(TestService testService) {
        this.testService = testService;
    }
}
	<bean name="testService" class="org.springframework.simple.TestService" />

	<bean id="testController" class="org.springframework.simple.TestController">
		<constructor-arg ref="testService"/>
	</bean>

演示圖如下:

propertyValues

property方式注入時的屬性值。在以下方式時,生效:

public class TestByPropertyController {

    TestService testService;
	
    //注意,這里是set方法注入
    public void setTestService(TestService testService) {
        this.testService = testService;
    }
}
<bean name="testService" class="org.springframework.simple.TestService" />
  
<bean id="testByPropertyController" class="org.springframework.simple.TestByPropertyController"  >
		<property name="testService" ref="testService"/>
	</bean>

演示圖如下:

總結

今天內容大概到這里,有問題請留言


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM