什么是Bean?
是spring對所有注入到IoC容器中的類的統稱。
我們要注冊進入spirng的bean千奇百怪,所以spring必須需要使用一個統一的定義來標識bean,就有了接下來的BeandDefinition,通過名稱我們就可以知道,他是對bean的定義。
Spring的BeanDefinition詳解
效果:將不同來源的bean進行歸一化處理
什么是BeandDefinition? 下面是BeanDefinition的源碼,BeanDefinition是一個接口,他繼承了兩個接口,分別是AttributeAccessor和BeanMetadataElement。
簡單來說,spring將所有需要裝在到容器中的類進行了歸一化處理,他們的共同屬性都通過BeanDefinition去約束。
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;int ROLE_APPLICATION = 0;int ROLE_SUPPORT = 1;int ROLE_INFRASTRUCTURE = 2;void setParentName(@Nullable String parentName);@NullableString getParentName();void setBeanClassName(@Nullable String beanClassName);@NullableString getBeanClassName();void setScope(@Nullable String scope);@NullableString getScope();void setLazyInit(boolean lazyInit);boolean isLazyInit();void setDependsOn(@Nullable String... dependsOn);@NullableString[] getDependsOn();void setAutowireCandidate(boolean autowireCandidate);boolean isAutowireCandidate();void setPrimary(boolean primary);boolean isPrimary();void setFactoryBeanName(@Nullable String factoryBeanName);@NullableString getFactoryBeanName();void setFactoryMethodName(@Nullable String factoryMethodName);@NullableString getFactoryMethodName();ConstructorArgumentValues getConstructorArgumentValues();default boolean hasConstructorArgumentValues() {return !getConstructorArgumentValues().isEmpty();}MutablePropertyValues getPropertyValues();default boolean hasPropertyValues() {return !getPropertyValues().isEmpty();}void setInitMethodName(@Nullable String initMethodName);@NullableString getInitMethodName();void setDestroyMethodName(@Nullable String destroyMethodName);@NullableString getDestroyMethodName();void setRole(int role);int getRole();void setDescription(@Nullable String description);@NullableString getDescription();ResolvableType getResolvableType();boolean isSingleton();boolean isPrototype();boolean isAbstract();@NullableString getResourceDescription();@NullableBeanDefinition getOriginatingBeanDefinition();
}
從代碼中,我們可以看出,BeanDefinition一共有兩種實現類型,一種是AnnotatedBeanDefinition,另一種則是AbstractBeanDefinition。
不難看出,AnnotatedBeanDefinition是對BeanDefinition接口的擴展,而AbstractBeanDefinition是一個抽象方法, 實現了大部分的模板方法,以便子類去使用。
有了存儲類的統一元數據結構,那么spring將需要一種特定的數據結構去存儲這些內容,spring提供了這么一個接口BeanDefinitionRegistry,能力如下:
public interface BeanDefinitionRegistry extends AliasRegistry {void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException;void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;boolean containsBeanDefinition(String beanName);String[] getBeanDefinitionNames();int getBeanDefinitionCount();boolean isBeanNameInUse(String beanName);
}
通過BeanDefinitionRegistry中提供的方法中我們可以看到他擁有什么能力,首先將名稱和對應的beanDefinition進行匹配。
其次,通過getBeanDefinitionNames,不難看出我們可以一個beandefinition可以擁有多個別名。
可以通過BeanDefinitionRegistry的實現類來窺探spring究竟是如何存儲的,其實很簡單,是通過一個map進行存儲的。
具體代碼如下:
public class SimpleBeanDefinitionRegistry extends SimpleAliasRegistry implements BeanDefinitionRegistry {private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {Assert.hasText(beanName, "'beanName' must not be empty");Assert.notNull(beanDefinition, "BeanDefinition must not be null");this.beanDefinitionMap.put(beanName, beanDefinition);}@Overridepublic void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {if (this.beanDefinitionMap.remove(beanName) == null) {throw new NoSuchBeanDefinitionException(beanName);}}@Overridepublic BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {BeanDefinition bd = this.beanDefinitionMap.get(beanName);if (bd == null) {throw new NoSuchBeanDefinitionException(beanName);}return bd;}@Overridepublic boolean containsBeanDefinition(String beanName) {return this.beanDefinitionMap.containsKey(beanName);}@Overridepublic String[] getBeanDefinitionNames() {return StringUtils.toStringArray(this.beanDefinitionMap.keySet());}@Overridepublic int getBeanDefinitionCount() {return this.beanDefinitionMap.size();}@Overridepublic boolean isBeanNameInUse(String beanName) {return isAlias(beanName) || containsBeanDefinition(beanName);}
}
通過registerBeanDefinition方法可以看出,將beanName與beanDefinition進行綁定,多個beanName可以綁定同一個beanDefinition。
怎樣去使用BeanDefinition的實現類呢?
首先創建一個實體類:
@Component // 后面測試注解的方式用到的
public class Person {private Integer id;private String name;public Person() {}public Person(Integer id, String name) {this.id = id;this.name = name;}@Overridepublic String toString() {return "Person{" +"id=" + id +", name='" + name + '\'' +'}';}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
1、通過讀取xml中的數據
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"><bean id="person" class="com.test.entity.Person"><property name="id" value="1"/><property name="name" value="不會敲代碼的呂小橋"/></bean></beans>
測試類:
BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
BeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
reader.loadBeanDefinitions("test.xml");
System.out.println(registry.getBeanDefinitionCount());
輸出內容:
> Task :spring-test:BeanDefinitionTest.main()
1
2、通過注解的方式讀取bean
注意:Person類需要加上Component注解
測試方法
BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry);
reader.register(Person.class);
System.out.println(registry.getBeanDefinitionNames());
輸出如下:
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
person
org.springframework.context.event.internalEventListenerFactory
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
3、通過包掃描的方式讀取bean
測試方法:
// 掃描指定包下的所有類
BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
// 掃描指定包下的所有類
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
scanner.scan("com.test.entity");
for (String beanDefinitionName : registry.getBeanDefinitionNames()) {System.err.println(beanDefinitionName);
}
輸出結果:
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
person
org.springframework.context.event.internalEventListenerFactory
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
思考總結:
1、在查看源碼的過程中,發現源碼中有很多地方用到了如下代碼:
Assert.hasText(beanName, "'beanName' must not be empty");
以上代碼可以有效的避免我們在開發中大量的if判斷,同時可以讓你的代碼可閱讀性更高。
hutools和spring依賴中均有該類型的寫法,可以參考一下。
2、設計模式&接口&抽象類,幫助更好的處理一系列方法
在面對一系列處理的時候,我們應該抽象出接口,并使用抽象類去約定模板方法,繼而使用子類去實現,這種方式會節省我們大量重復的工作,而且所有的方法都有跡可循,在模板方法中定義流程,在子類中實現,能夠有效的解耦流程和實現。這樣我們就可以只關心子類的具體實現了。