Spring核心功能
感謝拉勾教育
控制反轉和依賴注入的了解(通俗易懂)
Spring5–03—IOC 操作 Bean 管理
Resource與@Autowired用法差別
文章目錄
- Spring核心功能
- Spring Bean 的作用域有哪些?它的注冊方式有幾種?
- Bean的注冊方式有三種
- XML 配置檔案注冊方式
- Java 注解注冊方式
- Java API 注冊方式
- Bean 的作用域一共有 5 個
- 配置方式
- 同名Bean問題
- Bean 生命周期
- ==Bean的執行個體化==
- createBean()源碼
- ==注入對象屬性==
- doCreateBean 源碼如下:
- ==執行Aware回調==
- ==BeanPostProcessor==
- ==InitializingBean==
- ==afterPropertiesSet==
- ==注冊回調方法==
- ==使用Bean==
- ==銷毀==
- 生命周期
- IOC和DI
- Spring IoC 的優點
- IOC優點
- SpringIOC容器原理:
- IOC容器基本概念
- 建立對象的方式由哪些:
- Iocr容器底層實作的技術
- IOC核心接口
- Spring IoC 注入方式彙總
- AOP 概述
Spring Bean 的作用域有哪些?它的注冊方式有幾種?
在Spring容器中管理一個或者多個Bean,這些Bean的定義表示為beanDefinition對象,這些對象包括一下重要資訊
- Bean的實際作用域
- Bean的作用範圍
- Bean的引用或者依賴項
Bean的注冊方式有三種
- XML配置檔案的注冊方式
- Java注解的注冊方式
- JavaAPI的注冊方式
XML 配置檔案注冊方式
<bean id="person" class="org.springframework.beans.Person">
<property name="id" value="1"/>
<property name="name" value="Java"/>
</bean>
Java 注解注冊方式
@Component
public class Person {
private Integer id;
private String name
// 忽略其他方法
}
也可以使用 @Bean 注解方式來注冊 Bean,代碼如下:
@Configuration
public class Person {
@Bean
public Person person(){
return new Person();
}
// 忽略其他方法
}
@Configuration可以了解為XML配置裡的<Beans>标簽,而 @Bean 可了解為用 XML 配置裡面的 标簽。
Java API 注冊方式
使用 BeanDefinitionRegistry.registerBeanDefinition() 方法的方式注冊 Bean,代碼如下:
public class CustomBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
RootBeanDefinition personBean = new RootBeanDefinition(Person.class);
// 新增 Bean
registry.registerBeanDefinition("person", personBean);
}
}
Bean 的作用域一共有 5 個
- singleton作用域: 表示Spring容器隻有一個bean執行個體,以單例的形式存在,是預設開啟的
- prototype 作用域: 原型域,每次調用bean時都會建立一個新執行個體,也就是說每次調用getbean()方法時,相當于執行的New Bean();
- request作用域: 每次http請求時都會建立一個新的Bean,該作用域僅适應于 WebApplicationContext 環境。
- session 作用域:同一個HTTP Session 共享一個Bean對象,不同的Session擁有不同的Bean對象,僅适用于WebApplicationContext 環境。
- application 作用域:全局的WEB作用域,類似于 Servlet 中的 Application
配置方式
- XML的配置方式
<bean class="..." scope="application"></bean>
- Java 注解的配置方式如下:
@Scope(WebApplicationContext.SCOPE_APPLICATION)
- 或是:
@RequestScope(WebApplicationContext.SCOPE_APPLICATION)
同名Bean問題
- 每個Bean擁有一個或者多個辨別符,在基于XML的配置中,我們可以使用id或者name來作為Bean的辨別符,通常Bean的辨別符由字母組成,允許使用特殊字元
-
同一個Spring配置檔案中的BEAn和id 和 name 是不能夠重複的,否則Spring 容器啟動時會報錯
但如果 Spring 加載了多個配置檔案的話,可能會出現同名 Bean 的問題。同名 Bean 指的是多個 Bean 有相同的 name 或者 id。
- pring 對待同名 Bean 的處理規則是使用最後面的 Bean 覆寫前面的 Bean,是以我們在定義 Bean 時,盡量使用長命名非重複的方式來定義,避免産生同名 Bean 的問題。
- Bean 的 id 或 name 屬性并非必須指定,如果留白的話,容器會為 Bean 自動生成一個唯一的
Bean 生命周期
Bean的執行個體化
對于Spring bean來說,并不是啟動階段就會觸發Bean的執行個體化,隻有當用戶端通通過顯示或者隐式的方法調用BeanFactory的GetBean() 方法時,他才會觸發該類的執行個體化方法,當然對于BeanFactory來說,也不是所有的getBean()方法否會執行個體化bean對象,例如作用域為singleton 時,就會在第一次,執行個體化該Bean對象,之後直接傳回該對象,如果使用的時ApplicationContext ,則會在該容器啟動的時候,立即調用注冊到該容器所有 Bean 的執行個體化方法。
getBean()既然時Bean對象的入口,我們就先從這個方法說起,getBean()方法是屬于BeanFActory接口,它是通過AbstractAutowireCapableBeanFactory 的 createBean() 方法,而 createBean() 是通過 doCreateBean() 來實作的,具體源碼實作如下:
createBean()源碼
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
if (logger.isTraceEnabled()) {
logger.trace("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
// 确定并加載 Bean 的 class
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// 驗證以及準備需要覆寫的方法
try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
// 給BeanPostProcessors 一個機會來傳回代理對象來代替真正的 Bean 執行個體,在這裡實作建立代理對象功能
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
// 建立 Bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
注入對象屬性
doCreateBean 源碼如下:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// 執行個體化 bean,BeanWrapper 對象提供了設定和擷取屬性值的功能
BeanWrapper instanceWrapper = null;
// 如果 RootBeanDefinition 是單例,則移除未完成的 FactoryBean 執行個體的緩存
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 建立 bean 執行個體
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 擷取 BeanWrapper 中封裝的 Object 對象,其實就是 bean 對象的執行個體
final Object bean = instanceWrapper.getWrappedInstance();
// 擷取 BeanWrapper 中封裝 bean 的 Class
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 應用 MergedBeanDefinitionPostProcessor 後處理器,合并 bean 的定義資訊
// Autowire 等注解資訊就是在這一步完成預解析,并且将注解需要的資訊放入緩存
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
} catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 為了避免循環依賴,在 bean 初始化完成前,就将建立 bean 執行個體的 ObjectFactory 放入工廠緩存(singletonFactories)
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 對 bean 屬性進行填充
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
// 調用初始化方法,如 init-method 注入 Aware 對象
exposedObject = initializeBean(beanName, exposedObject, mbd);
} catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
} else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
// 如果存在循環依賴,也就是說該 bean 已經被其他 bean 遞歸加載過,放入了提早公布的 bean 緩存中
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 如果 exposedObject 沒有在 initializeBean 初始化方法中被增強
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
} else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// 依賴檢測
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
// 如果 actualDependentBeans 不為空,則表示依賴的 bean 并沒有被建立完,即存在循環依賴
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
try {
// 注冊 DisposableBean 以便在銷毀時調用
registerDisposableBeanIfNecessary(beanName, bean, mbd);
} catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
- 由上訴源碼可以看出,在doCreateBean()方法中,首先是對Bean進行執行個體化,他是通過調用createBeanInstance()方法來實作的,該方法傳回一個BeanWrapper 對象,BeanWrapper 對象是Spring 中一個基礎的 Bean 結構接口,說它是基礎接口是因為它連基本的屬性都沒有
- BeanWrapper 接口有一個預設實作類,BeanWrapperImpl,主要作用說就是對Bean 進行填充,比如填充和注入 Bean 的屬性等。
- 當Spring完成Bean對象執行個體化并且設定完相關屬性和依賴後,則會調用Bean的初始化方法initializeBean(),初始化第一個階段時檢查目前的Bean對象,否則實作了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware 等接口
執行Aware回調
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
- BeanNameAware 是把 Bean 對象定義的 beanName 設定到目前對象執行個體中
- BeanClassLoaderAware 是将目前 Bean 對象相應的 ClassLoader 注入到目前對象執行個體中
- BeanFactoryAware 是 BeanFactory 容器會将自身注入到目前對象執行個體中,這樣目前對象就會擁有一個 BeanFactory 容器的引用。
初始化第二個階段
BeanPostProcessor
InitializingBean
afterPropertiesSet
注冊回調方法
使用Bean
銷毀
- BeanPostProcessor 增強處理,他主要是對Spring容器提供的Bean執行個體對象進行有效的擴充,允許Sping在初始化Bean階段進行定制修改,比如處理标記接口或者為其提供代理實作
- 初始化的前置處理完成之後就會檢查和執行 InitializingBean 和 init-method 方法。
- InitializingBean 是一個接口,他有一個==afterPropertiesSet()==方法,在Bean初始化時會判斷目前Bean是否實作InitializingBean,如果實作了則調用afterPropertiesSet()方法,進行初始化工作;然後再檢查是否也指定init-method,如果指定了則通過反射機制調用指定的init-method方法,
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
// 判斷目前 Bean 是否實作了 InitializingBean,如果是的話需要調用 afterPropertiesSet()
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) { // 安全模式
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet(); // 屬性初始化
return null;
}, getAccessControlContext());
} catch (PrivilegedActionException pae) {
throw pae.getException();
}
} else {
((InitializingBean) bean).afterPropertiesSet(); // 屬性初始化
}
}
// 判斷是否指定了 init-method()
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
// 利用反射機制執行指定方法
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
初始化完成之後就可以正常的使用Bean對象,再Spring容器關閉的時候會執行銷毀方法,但是Spring 容器不會自動去調用銷毀方法,而是需要我們主動的調用。
- 如果是BeanFactory 容器,那麼我們需要主動調用==destroySingletons()==方法通知 BeanFactory 容器去執行相應的銷毀方法;
- 如果是ApplicationContext 容器,那麼我們需要主動調用registerShutdownHook(),告知ApplicationContext 容器執行相應的銷毀方法。
生命周期
IOC和DI
控制反轉和依賴注入的了解(通俗易懂)
IOC不是一個技術,是一種設計思想.與傳統的控制流相比,IoC 會颠倒控制流,在傳統的程式設計中需要開發者自行建立并銷毀對象,而在IOC中會把這些操作交給架構進行處理,這樣開發者就這樣開發者就不用關注具體的實作細節了,拿來直接用就可以了,這就是控制反轉。
IOC很好的額展現出了面向對象的設計法則之一——好萊塢法則:“别找我們,我們找你”。即由 IoC 容器幫對象找到相應的依賴對象并注入,而不是由對象主動去找。
DI依賴注入,表示元件之間的依賴關系交由容器再運作期自動生成,也就是說,由容器動态的将某個依賴關系注入到元件之中,這樣就能提升元件的重用頻率,通過依賴注入機制,我們隻需要通過簡單的配置,就可指定目标需要的資源,完成自身的業務邏輯,而不需要關心資源來自哪裡、由誰實作等問題。
IoC 和 DI 其實是同一個概念從不同角度的描述的,由于控制反轉這個概念比較含糊(可能隻了解成了容器控制對象這一個層面,很難讓人想到誰來維護對象關系),是以 2004 年被開發者尊稱為“教父”的 Martin Fowler(世界頂級專家,靈活開發方法的創始人之一)又給出了一個新的名字“依賴注入”,相對 IoC 而言,“依賴注入”明确描述了“被注入的對象依賴IOC容器配置依賴對象”。
Spring IoC 的優點
IOC優點
- 使用友善,拿來即用,無需顯示的建立和銷毀的過程
- 可以很容易提供衆多的服務,比如事務管理,消息服務等
- 提供了單例模式的支援
- 提供了AOP抽象,利用他很很容易進行攔截,運作期監控等功能
- 更符合面向對象的設計法則
- 低侵入設計,代碼的污染低,降低了業務對象替換的複雜性。
SpringIOC容器原理:
IOC容器中非常核心的接口: BeanFactory bean對象 Factory工廠
IOC容器基本概念
IOC容器基本概念::控制反轉: 降低計算機代碼的備援度
把對象的建立對象與使用統一交給我們的Spring來進行管理 不需要開發者自己去new對象
建立對象的方式由哪些:
- 單獨的new方式----耦合度太高
每次單獨new對象,沒有實作統一的管理對象,如果後期需要userDao的名稱資訊釋出的情況下,需要改變的引用的地方比較多,耦合度太高
- 工廠模式–降低我們的耦合度
概念:統一管理和維護我們每個對象建立與使用的過程
不需自己new對象
- 反射模式
Iocr容器底層實作的技術
:反射技術, 工廠模式 .解析XML
- 使用解析XML技術.解析Spring.xml配置檔案
- 擷取<bean\ id=’’ class=’’/> 類的完整路徑位址
- 使用到反射技術初始化對象
- 使用工廠模式封裝初始化對象
IOC核心接口
- IOC容器接口
- IOC容器底層基于反射 +工廠實作的
- Spring體系中提供了兩種IOC容器的實作方案
3.1BeanFactory不是提供給給開發人員的 主要提供給我們Spring内部自己實作
加載配置檔案的時候不會根據配置檔案的内容建立對象,當我們真正徐娅使用該對象的時候才會建立對象
___3.2 ApplicationFactory是提供給我們開發人員使用的,對于我們傳統的BeanFactory實作非常多的擴 展 功能,ApplicationFactory屬性BeanFactory接口下的子接口
加載配置檔案的時候,會根據配置檔案的内容建立對象,并且緩存起來
javaweb項目的時候, 使用ApplicationFactory
剛啟動的時候,程式卡,後面就好了,
Spring IoC 注入方式彙總
IoC 的注入方式有三種:構造方法注入、Setter 注入和接口注入,注解注入。
- 構造方法注入
- Setter 注入
- 接口注入
- 注解注入
Spring5–03—IOC 操作 Bean 管理
Resource與@Autowired用法差別
AOP 概述
AOP 為 Aspect Oriented Programming 的縮寫,意為:面向切面程式設計,通過預編譯方式和運作期間動态代理實作程式功能的統一維護的一種技術。
AOP是OOP的延續,是軟體開發中的一個熱點,也是spring架構中的的一個重 要内容。利用 AOP 可以對業務邏輯的各個部分進行隔離,進而使得業務邏輯各 部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率
AOP(Aspect-OrientedProgramming,面向切面程式設計)可以說是 OOP(Object-Oriented Programing,面向對象程式設計)的補充和完善,OOP 引入封裝、繼承和多态性等概念來建立一種公共對象處理的能力,當我們需要處理公共行為的時候,OOP 就會顯得無能為力,而 AOP 的出現正好解決了這個問題。比如統一的日志處理子產品、授權驗證子產品等都可以使用 AOP 很輕松的處理。
Spring AOP 目前提供了三種配置方式:
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 判斷目标類是否為接口
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
// 是接口使用 jdk 的代理
return new JdkDynamicAopProxy(config);
}
// 其他情況使用 CgLib 代理
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
// 忽略其他代碼
}