本文小編為大家詳細(xì)介紹“Spring依賴注入的方式有哪些及原理是什么”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“Spring依賴注入的方式有哪些及原理是什么”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來(lái)學(xué)習(xí)新知識(shí)吧。
我們提供的服務(wù)有:成都網(wǎng)站制作、做網(wǎng)站、外貿(mào)營(yíng)銷(xiāo)網(wǎng)站建設(shè)、微信公眾號(hào)開(kāi)發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、海港ssl等。為1000+企事業(yè)單位解決了網(wǎng)站和推廣的問(wèn)題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的海港網(wǎng)站制作公司
在Spring中提供了三種實(shí)現(xiàn)依賴注入的方式:字段注入、構(gòu)造器注入、Setter方法注入
。
首先我們先創(chuàng)建一個(gè)Service層的接口以及對(duì)應(yīng)的實(shí)現(xiàn)類(lèi),基于以下實(shí)現(xiàn)類(lèi)來(lái)實(shí)現(xiàn)依賴注入的方式:
public interface UserService { public void UserInfo(); } public class UserServiceImpl implements UserService{ @Override public void UserInfo() { System.out.println("UserInfo to do ..."); } }
Spring中通過(guò)@Autowired注解,可以完成注入。
public class ClientService { @Autowired private UserService userService; public void UserInfo(){ userService.UserInfo(); } }
字段注入是三種注入方式最簡(jiǎn)單、最常用的一種方式,但是也是最需要避免使用的一種方式。那為什么要避免使用呢?接下來(lái)進(jìn)行分析一下。
在ClientService
類(lèi)中,我們定義了一個(gè)私有化的變量userService
來(lái)注入該接口的實(shí)例,但是這個(gè)實(shí)例只能在ClientService
類(lèi)中訪問(wèn)到,脫離容器環(huán)境無(wú)法訪問(wèn)到。
ClientService clientService = new ClientService(); clientService.UserInfo();
如上圖執(zhí)行結(jié)果拋出NullPointerException
空指針異常,原因很簡(jiǎn)單無(wú)法在ClientService 類(lèi)的外部實(shí)例化UserService 對(duì)象。采用字段注入的話,類(lèi)與容器的耦合度較高,無(wú)法脫離容器使用目標(biāo)對(duì)象。這就得出了避免使用字段注入的第一個(gè)原因:對(duì)象的外部可見(jiàn)性較差。
避免使用字段注入第二個(gè)原因:可能導(dǎo)致潛在的循環(huán)依賴。循環(huán)依賴指的是兩個(gè)類(lèi)之間互相進(jìn)行注入。代碼如下
public class ClassA { @Autowired private ClassB classB; } public class ClassB { @Autowired private ClassA classA; }
如上代碼顯然,ClassA和ClassB發(fā)生循環(huán)依賴。在Spring啟動(dòng)的時(shí)候不會(huì)發(fā)生錯(cuò)誤,但是在使用具體的某個(gè)類(lèi)時(shí)會(huì)報(bào)錯(cuò)。
構(gòu)造器注入就是使用類(lèi)的構(gòu)造函數(shù)來(lái)完成對(duì)象的注入。
public class ClientService { private UserService userService; @Autowired public ClientService(UserService userService) { this.userService = userService; } public void UserInfo(){ userService.UserInfo(); } }
通過(guò)構(gòu)造器注入可以解決對(duì)象的外部可見(jiàn)性的問(wèn)題,因?yàn)?code>userService是通過(guò)ClientService
構(gòu)造函數(shù)進(jìn)行注入的。基于構(gòu)造器注入,回顧一下之前循環(huán)依賴的問(wèn)題。代碼如下
public class ClassA { private ClassB classB; @Autowired public ClassA(ClassB classB) { this.classB = classB; } } public class ClassB { private ClassA classA; @Autowired public ClassB(ClassA classA) { this.classA = classA; } }
在Spring項(xiàng)目啟動(dòng)的時(shí)候,會(huì)拋出循環(huán)依賴異常,可以提醒開(kāi)發(fā)者避免使用循環(huán)依賴。但是構(gòu)造器注入也是有問(wèn)題的,當(dāng)構(gòu)造函數(shù)中存在較多的依賴對(duì)象時(shí),大量的構(gòu)造函數(shù)參數(shù)回訪代碼出現(xiàn)冗余。接下來(lái)就引入Setter方法注入。
Setter方法注入代碼如下
public class ClientService { private UserService userService; @Autowired public void setUserService(UserService userService) { this.userService = userService; } public void UserInfo(){ userService.UserInfo(); } }
Setter注入相比于構(gòu)造器注入可讀性更強(qiáng),可以將多個(gè)實(shí)例對(duì)象通過(guò)多個(gè)Setter方法逐一進(jìn)行注入?;仡欀暗难h(huán)依賴問(wèn)題。代碼如下
public class ClassA { private ClassB classB; @Autowired public void setClassB(ClassB classB) { this.classB = classB; } } public class ClassB { private ClassA classA; @Autowired public void setClassA(ClassA classA) { this.classA = classA; } }
在ClassA 和ClassB 作用域都為單例bean的前提下,代碼正常執(zhí)行。
總結(jié):Setter適合可選對(duì)象的注入;構(gòu)造方法適合強(qiáng)制對(duì)象的注入;字段注入避免使用。
前面介紹完依賴注入的三種實(shí)現(xiàn)方式,接下來(lái)結(jié)合Spring源碼深入的了解下依賴注入的原理,通過(guò)Bean 注冊(cè)和Bean 實(shí)例化兩個(gè)模塊進(jìn)行闡述。
在Spring中我們往往通過(guò)一個(gè)應(yīng)用的上下文(ApplicationContext
)對(duì)象來(lái)操作各種Bean。
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
xxxApplicationContext
接口在Spring中就代表一個(gè)Spring IOC 容器,Spring中存在大量的ApplicationContext
接口的實(shí)現(xiàn)類(lèi)。如果基于注解的配置方式,就使用AnnotationConfigApplicationContext
來(lái)初始化上下文容器對(duì)象。接下來(lái)進(jìn)入AnnotationConfigApplicationContext
的源碼,查看其構(gòu)造函數(shù)如下:
/** * Create a new AnnotationConfigApplicationContext, deriving bean definitions * from the given component classes and automatically refreshing the context. * @param componentClasses one or more component classes — for example, * {@link Configuration @Configuration} classes */ public AnnotationConfigApplicationContext(Class>... componentClasses) { this(); // 根據(jù)注解配置類(lèi)注冊(cè)Bean register(componentClasses); // 刷新容器 refresh(); } /** * Create a new AnnotationConfigApplicationContext, scanning for components * in the given packages, registering bean definitions for those components, * and automatically refreshing the context. * @param basePackages the packages to scan for component classes */ public AnnotationConfigApplicationContext(String... basePackages) { this(); // 根據(jù)包路徑掃描Bean scan(basePackages); // 刷新容器 refresh(); }
通過(guò)以上兩個(gè)構(gòu)造函數(shù)可以看出,一個(gè)是根據(jù)注解配置類(lèi)注冊(cè)Bean,另一個(gè)通過(guò)包路徑掃描Bean。點(diǎn)擊進(jìn)入register
方法:
//--------------------------------------------------------------------- // 注解ConfigRegistry的實(shí)現(xiàn) // Implementation of AnnotationConfigRegistry //--------------------------------------------------------------------- /** * Register one or more component classes to be processed. *Note that {@link #refresh()} must be called in order for the context * to fully process the new classes. * @param componentClasses one or more component classes — for example, * {@link Configuration @Configuration} classes * @see #scan(String...) * @see #refresh() */ @Override public void register(Class>... componentClasses) { Assert.notEmpty(componentClasses, "At least one component class must be specified"); this.reader.register(componentClasses); }
通過(guò)this.reader.register(componentClasses);
可以看出,調(diào)用當(dāng)前對(duì)象reader
里面的register
方法,而reader
實(shí)際上是AnnotatedBeanDefinitionReader
工具類(lèi)來(lái)完成Bean的注冊(cè)。繼續(xù)點(diǎn)進(jìn)register方法:
/** * Register one or more component classes to be processed. *Calls to {@code register} are idempotent; adding the same * component class more than once has no additional effect. * @param componentClasses one or more component classes, * e.g. {@link Configuration @Configuration} classes */ public void register(Class>... componentClasses) { for (Class> componentClass : componentClasses) { registerBean(componentClass); } } /** * Register a bean from the given bean class, deriving its metadata from * class-declared annotations. * @param beanClass the class of the bean */ public void registerBean(Class> beanClass) { doRegisterBean(beanClass, null, null, null, null); }
AnnotatedBeanDefinitionReader
會(huì)遍歷所有的componentClasses
組件類(lèi),通過(guò)registerBean
方法中的doRegisterBean
方法完成Bean的注冊(cè)。進(jìn)入doRegisterBean
:
/** * Register a bean from the given bean class, deriving its metadata from * class-declared annotations. * @param beanClass the class of the bean * @param name an explicit name for the bean * @param supplier a callback for creating an instance of the bean * (may be {@code null}) * @param qualifiers specific qualifier annotations to consider, if any, * in addition to qualifiers at the bean class level * @param customizers one or more callbacks for customizing the factory's * {@link BeanDefinition}, e.g. setting a lazy-init or primary flag * @since 5.0 */ privatevoid doRegisterBean(Class beanClass, @Nullable String name, @Nullable Class extends Annotation>[] qualifiers, @Nullable Supplier supplier, @Nullable BeanDefinitionCustomizer[] customizers) { // 將注解配置類(lèi)信息轉(zhuǎn)換成一種 BeanDefinition AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass); if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; } abd.setInstanceSupplier(supplier); // 獲取bean的作用域元數(shù)據(jù) ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); // 將bean的作用域?qū)懟?nbsp;BeanDefinition abd.setScope(scopeMetadata.getScopeName()); // 生成 beanName String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); // 解析AnnotatedGenericBeanDefinition 中的 @lazy 和 @Primary注解 AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); // 處理@Qualifier 注解 if (qualifiers != null) { for (Class extends Annotation> qualifier : qualifiers) { if (Primary.class == qualifier) { // 如果設(shè)置了@Primary注解,設(shè)置當(dāng)前bean為首選bean abd.setPrimary(true); } else if (Lazy.class == qualifier) { // 如果設(shè)置了@lazy注解,則設(shè)置當(dāng)前bean為延遲加載模式 abd.setLazyInit(true); } else { abd.addQualifier(new AutowireCandidateQualifier(qualifier)); } } } if (customizers != null) { for (BeanDefinitionCustomizer customizer : customizers) { customizer.customize(abd); } } BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); // 注冊(cè) bean對(duì)象 BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); }
總的來(lái)看:
① 首先需要構(gòu)造描述bean實(shí)例化信息的BeanDefinition
對(duì)象,需要將注解配置類(lèi)信息轉(zhuǎn)化為AnnotatedGenericBeanDefinition
類(lèi)型,此處的AnnotatedGenericBeanDefinition
就是一種BeanDefinition
類(lèi)型,包含了Bean的構(gòu)造函數(shù)參數(shù),屬性值以及添加的注解信息。
② 設(shè)置BeanDefinition
屬性,完成對(duì)@Scope、@Lazy、@Primary
等注解的處理
③ 最后通過(guò)registerBeanDefinition()
方法完成Bean的注冊(cè)。
現(xiàn)在Spring IOC容器對(duì)Bean的創(chuàng)建過(guò)程并沒(méi)有完成,目前只是將Bean的定義加載到了容器中,但是可能容器本身已經(jīng)存在這些Bean的定義,所以需要使用refresh()方法刷新容器,回到最開(kāi)始進(jìn)入AnnotationConfigApplicationContext
的源碼,查看其構(gòu)造函數(shù)如下:
/** * Create a new AnnotationConfigApplicationContext, deriving bean definitions * from the given component classes and automatically refreshing the context. * @param componentClasses one or more component classes — for example, * {@link Configuration @Configuration} classes */ public AnnotationConfigApplicationContext(Class>... componentClasses) { this(); // 根據(jù)注解配置類(lèi)注冊(cè)Bean register(componentClasses); // 刷新容器 refresh(); }
接下來(lái)分析refresh
方法,點(diǎn)擊進(jìn)入:
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { ... // 提取配置信息,注冊(cè)到BeanFactory中 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); ... try { ...... // 初始化所有的單例 bean finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { ...... } finally { ...... } } }
可以看出obtainFreshBeanFactory
完成對(duì)Bean的注冊(cè)返回一個(gè)BeanFactory
。而finishBeanFactoryInitialization
方法真正完成Bean實(shí)例化的入口。真正完成實(shí)例化的方法為DefaultListableBeanFactory
類(lèi)中的preInstantiateSingletons
方法,進(jìn)入此方法:
@Override public void preInstantiateSingletons() throws BeansException { ListbeanNames = new ArrayList<>(this.beanDefinitionNames); // 觸發(fā)所有非懶加載的單例Bean的初始化操作 for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { ...... } else { // 獲取Bean getBean(beanName); } } } ...... }
進(jìn)入到getBean
()方法:
//--------------------------------------------------------------------- // Implementation of BeanFactory interface //--------------------------------------------------------------------- @Override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); }
Bean的初始化過(guò)程就在這個(gè)方法中。在當(dāng)前的抽象類(lèi)AbstractBeanFactory
中有一個(gè)抽象方法createBean
如下:
protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException;
在Spring中實(shí)現(xiàn)這個(gè)抽象方法的唯一BeanFactory是AbstractAutowireCapableBeanFactory
,真正完成Bean創(chuàng)建是在doCreateBean
:
/** * 此類(lèi)的中心方法:創(chuàng)建一個(gè)bean實(shí)例, * Central method of this class: creates a bean instance, * populates the bean instance, applies post-processors, etc. * @see #doCreateBean */ @Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { ...... try { // 真正創(chuàng)建Bean Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // A previously detected exception with proper bean creation context already, // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry. throw ex; } catch (Throwable ex) { throw new BeanCreationException( mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); } }
最后進(jìn)入到doCreateBean
如下:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { ...... // 初始化一個(gè)bean if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } ...... Object exposedObject = bean; try { // 初始化Bean實(shí)例 populateBean(beanName, mbd, instanceWrapper); // 執(zhí)行初始化bean實(shí)例回調(diào) 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); } } ...... // 將bean注冊(cè)為一次性。 try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
總的來(lái)看:
① createBeanInstance
方法用于根據(jù)配置生成具體的Bean,最終通過(guò)反射方法實(shí)現(xiàn),執(zhí)行完后Bean已經(jīng)被創(chuàng)建,但是不完整,沒(méi)有屬性的注入。
② populateBean
方法用于實(shí)現(xiàn)屬性的自動(dòng)注入,包含byName、byType、@Autowired、@Value屬性的設(shè)置,執(zhí)行完之后Bean就是完整的。
③ initializeBean
方法是一種擴(kuò)展性的機(jī)制,用于Bean初始化完成后的一些定制化操作。
讀到這里,這篇“Spring依賴注入的方式有哪些及原理是什么”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過(guò)才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。