Spring?依賴查找的使用小結(jié)
前言
源碼在我github的guide-spring倉(cāng)庫(kù)中,可以克隆下來 直接執(zhí)行。
我們本文主要來介紹依賴查找的使用示例
依賴查找
什么是依賴查找
依賴查找并不是 Spring 框架特有的概念,它是一種在軟件開發(fā)中獲取依賴對(duì)象的方式。它通常用于獲取運(yùn)行時(shí)需要的服務(wù)、組件或其他對(duì)象的引用。在面向?qū)ο缶幊讨校蕾囃ǔsw現(xiàn)為一個(gè)對(duì)象需要另一個(gè)對(duì)象的服務(wù)或功能。
在不同的編程框架和容器中,依賴查找的方式可能會(huì)有所不同。我們簡(jiǎn)單羅列一些常見的依賴查找的例子:
Java中的依賴查找
在純 Java 環(huán)境中,依賴查找通常通過構(gòu)造函數(shù)、方法參數(shù)或其他手段來獲得依賴對(duì)象的引用。
例如,通過在一個(gè)對(duì)象的構(gòu)造函數(shù)中傳遞另一個(gè)對(duì)象的引用:
public class MyClass { private DependencyClass dependency; public MyClass(DependencyClass dependency) { this.dependency = dependency; } // ... }
Spring框架中的依賴查找
在Spring框架中,依賴查找通常通過 Spring 容器來實(shí)現(xiàn)。你可以使用ApplicationContextor BeanFactory 來獲取所需的 Bean 。
public class UseDependencyLookupDemo { public static void main(String[] args) throws Exception { BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup.xml"); // 1. 實(shí)時(shí)查找 realtimeLookup(beanFactory); } private static void realtimeLookup(BeanFactory beanFactory) { // 名稱+類型 User user = beanFactory.getBean("user", User.class); System.out.println("實(shí)時(shí)查找: " + user); } }
或者,通過在類中使用@Autowired注解來自動(dòng)注入依賴:
@Service public class MyService { @Autowired private DependencyClass dependency; // ... }
Java EE中的依賴查找:
在Java EE環(huán)境中,你可以使用JNDI(Java Naming and Directory Interface)進(jìn)行依賴查找。通過JNDI,你可以在運(yùn)行時(shí)查找和獲取命名對(duì)象。
public class JNDIDependencyLookupDemo { public static void main(String[] args) throws NamingException { // 設(shè)置JNDI環(huán)境屬性 Properties properties = new Properties(); properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory"); properties.put(Context.PROVIDER_URL, "file:/META-INF/jndi"); // 初始化InitialContext Context initialContext = new InitialContext(properties); // 在文件系統(tǒng)上查找一個(gè)名為 "user" 的對(duì)象 User user = (User) initialContext.lookup("user"); System.out.println("JNDI Lookup Result: " + user); } }
依賴查找的方式
依賴查找的方式有很多,我們先看下 BeanFactory 的 接口定義:
public interface BeanFactory { Object getBean(String name) throws BeansException; <T> T getBean(String name, Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType); <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType); }
可以看出上述定義,我們可以通過 Bean 名稱、Bean 名稱 + 類型、類型等方式進(jìn)行依賴查找 Bean。下面我們分別從單一類型、集合類型、層次類型、延遲等方式依次展示依賴查找的示例。
單一類型的依賴查找
單一類型的查找,需要要求容器中同一類型的Bean只能有一個(gè)為 primary (BeanDefinition中的概念),我們可以看下 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="user" class="com.markus.spring.ioc.overview.domain.User"> <property name="id" value="1"/> <property name="username" value="markus zhang"/> </bean> <!-- 當(dāng)有多個(gè) User 時(shí),需要指出 其中一個(gè) Bean 的 primary屬性為 true 否則會(huì)出現(xiàn) NoUniqueBeanDefinitionException --> <bean id="user2" class="com.markus.spring.ioc.overview.domain.User" lazy-init="true" primary="true"> <property name="id" value="2"/> <property name="username" value="markus zhang"/> </bean> </beans>
我們來看下使用示例
public class UseDependencyLookupDemo { public static void main(String[] args) throws Exception { BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup.xml"); typeLookup(beanFactory); } /** * ========================按照 Bean 類型查找======================== */ /** * 單個(gè)Bean類型查找 * * @param beanFactory */ private static void typeLookup(BeanFactory beanFactory) { User user = beanFactory.getBean(User.class); System.out.println(user); } }
集合類型的依賴查找
與單一類型查找的區(qū)別在于,它不需要指定 primary 并且 返回一個(gè) Map<String,T> 對(duì)象,key 為 Bean 的名稱,value 為 Bean 實(shí)例。
public class UseDependencyLookupDemo { public static void main(String[] args) throws Exception { BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup.xml"); // 4. 按照類型查找多個(gè)Bean collectionLookup(beanFactory); } /** * 根據(jù)集合類型查找 */ private static void collectionLookup(BeanFactory beanFactory) { if (beanFactory instanceof ListableBeanFactory) { ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory; Map<String, User> userMap = listableBeanFactory.getBeansOfType(User.class); userMap.forEach((beanName, user) -> System.out.println("Bean name: " + beanName + ", User: " + user)); } } }
層次類型的依賴查找
層次性依賴查找,體現(xiàn)在父子容器中,我們一般可能體會(huì)不到,實(shí)際上 Spring MVC 的底層就涉及父子容器的概念,即 Root ApplicationContext 和 Dispatcher-Servlet ApplicationContext。這里不展開了。我們通過一個(gè)簡(jiǎn)單的示例來展示層次性依賴查找 Bean
package com.markus.spring.dependency.lookup; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.HierarchicalBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionReader; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * @author: markus * @date: 2023/12/17 10:23 PM * @Description: {@link HierarchicalBeanFactory} * @Blog: https://markuszhang.com * It's my honor to share what I've learned with you! */ public class HierarchicalBeanFactoryDependencyDemo { public static void main(String[] args) { ConfigurableListableBeanFactory subBeanFactory = new DefaultListableBeanFactory(); // 設(shè)置父容器 subBeanFactory.setParentBeanFactory(createParent()); // 展示 僅在當(dāng)前 Bean 容器中是否 存在 System.out.println(displayContainBean(subBeanFactory, "user", true)); // 展示 父子 Bean 容器中是否 存在(體現(xiàn)出 可繼承 BeanFactory 的示例 即 HierarchicalBeanFactory) System.out.println(displayContainBean(subBeanFactory, "user", false)); } private static boolean displayContainBean(ConfigurableListableBeanFactory beanFactory, String beanName, boolean onlyLocal) { boolean result = beanFactory.containsLocalBean(beanName); if (!onlyLocal) { if (!result) { BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory(); if (parentBeanFactory != null) { result = parentBeanFactory.containsBean(beanName); } } } return result; } private static ConfigurableListableBeanFactory createParent() { ConfigurableListableBeanFactory parentBeanFactory = new DefaultListableBeanFactory(); BeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) parentBeanFactory); String location = "classpath:/META-INF/dependency-lookup.xml"; // 加載 父容器 的 Bean 實(shí)例 beanDefinitionReader.loadBeanDefinitions(location); return parentBeanFactory; } }
延遲依賴查找
延遲依賴查找通常體現(xiàn)在懶加載 Bean 的場(chǎng)景,比如一些大資源的Bean希望在使用到的時(shí)候才會(huì)觸發(fā)初始化以達(dá)到降低服務(wù)啟動(dòng)時(shí)間的目的,這個(gè)時(shí)候就可以使用懶加載模式,而在我們依賴查找的時(shí)候,使用延遲依賴查找的時(shí)候,也不會(huì)觸發(fā) Bean 的初始化,只有在真正使用到對(duì)象的時(shí)候才會(huì)觸發(fā)初始化。(ps 比較繞,我們直接看例子)
因?yàn)?Bean 元信息配置比較特殊,我把 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="user2" class="com.markus.spring.ioc.overview.domain.User" lazy-init="true" primary="true"> <property name="id" value="2"/> <property name="username" value="markus zhang"/> </bean> <bean id="factoryBean" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"> <property name="targetBeanName" value="user2"/> </bean> </beans>
使用示例
public class UseDependencyLookupDemo { public static void main(String[] args) throws Exception { BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup.xml"); lazyLookup(beanFactory); } /** * 延遲查找 */ private static void lazyLookup(BeanFactory beanFactory) throws Exception { @SuppressWarnings("unchecked") ObjectFactory<User> factoryBean = (ObjectFactory<User>) beanFactory.getBean("factoryBean"); System.out.println("延遲生效中...."); System.out.println("延遲查找: " + factoryBean.getObject()); } }
Spring內(nèi)建可查找的依賴
除了我們自己配置的Bean,我們還可以查找 Spring 框架內(nèi) 注冊(cè)的單例 Bean。具體如下:
- environment Environment 外部化配置以及Profiles
- systemProperties Properties Java系統(tǒng)屬性
- systemEnvironment Map<String,String> 操作系統(tǒng)環(huán)境變量
- messageSource MessageSource 國(guó)家化文案
- lifecycleProcessor LifecycleProcessor Lifecycle Bean 處理器
- applicationEventMulticaster ApplicationEventMulticaster Spring 事件廣播器
- internalConfigurationAnnotationProcessor ConfigurationClassPostProcessor 處理 Spring 的配置類
- internalAutowiredAnnotationProcessor AutowiredAnnotationBeanPostProcessor 處理 @Autowired 以及 @Value、@Inject注解
- internalCommonAnnotationProcessor CommonAnnotationBeanPostProcessor 處理 JSR-250 注解,如 @Resource、@PostConstruct等
- internalEventListenerProcessor EventListenerMethodProcessor 處理標(biāo)注 @EventListener 的 Spring 事件監(jiān)聽方法
- internalEventListenerFactory DefaultEventListenerFactory 處理@EventListener 事件監(jiān)聽方法適配為 ApplicationListener
我們來看下示例:
package com.markus.spring.dependency.lookup; import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.LifecycleProcessor; import org.springframework.context.MessageSource; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor; import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.context.event.ApplicationEventMulticaster; import org.springframework.context.event.DefaultEventListenerFactory; import org.springframework.context.event.EventListenerMethodProcessor; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.env.Environment; import java.util.Properties; /** * @author: markus * @date: 2023/12/17 10:54 PM * @Description: Spring 內(nèi)建依賴的 依賴查找示例 * @Blog: https://markuszhang.com * It's my honor to share what I've learned with you! */ public class SpringInternalBeanDependencyLookDemo { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup.xml"); displaySpringInternalBean(context, Environment.class); displaySpringInternalBean(context, Properties.class); displaySpringInternalBeanByName(context, "systemEnvironment"); displaySpringInternalBean(context, MessageSource.class); displaySpringInternalBean(context, LifecycleProcessor.class); displaySpringInternalBean(context, ApplicationEventMulticaster.class); // 關(guān)閉 Spring 容器上下文 context.close(); // 基于 注解驅(qū)動(dòng) 的應(yīng)用上下文 AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(SpringInternalBeanDependencyLookDemo.class); annotationConfigApplicationContext.refresh(); displaySpringInternalBean(annotationConfigApplicationContext, ConfigurationClassPostProcessor.class); displaySpringInternalBean(annotationConfigApplicationContext, AutowiredAnnotationBeanPostProcessor.class); displaySpringInternalBean(annotationConfigApplicationContext, CommonAnnotationBeanPostProcessor.class); displaySpringInternalBean(annotationConfigApplicationContext, EventListenerMethodProcessor.class); displaySpringInternalBean(annotationConfigApplicationContext, DefaultEventListenerFactory.class); annotationConfigApplicationContext.close(); } private static void displaySpringInternalBean(ApplicationContext context, Class<?> type) { Object bean = context.getBean(type); System.out.println(bean); } private static void displaySpringInternalBeanByName(ApplicationContext context, String beanName) { Object bean = context.getBean(beanName); System.out.println(bean); } }
可以看到上面我們引入了基于 Xml 驅(qū)動(dòng)的Spring應(yīng)用上下文以及基于 注解 驅(qū)動(dòng)的Spring應(yīng)用上下文來實(shí)現(xiàn) Spring 內(nèi)建 Bean 的依賴查找。
ps: 如果一個(gè)默認(rèn)的 ClassPathXmlAppplicationContext 不會(huì)包含ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor等這些注解的依賴,需要我們?cè)?xml 配置文件中開啟 注解啟動(dòng),才會(huì)注冊(cè)進(jìn) Spring IoC容器中。
大家可能會(huì)有疑問,這些 Spring 內(nèi)建的 Bean 是什么時(shí)候被注冊(cè)進(jìn)去呢?這里給下源碼位置,感興趣的可以自行查看:
org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory
org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)
依賴查找的常見異常
下面這些就是場(chǎng)景的在使用依賴查找的時(shí)候可能會(huì)觸發(fā)的異常,都是 BeansException 的子類型。場(chǎng)景比較清晰,這里就不寫具體的示例了。
異常類型 | 觸發(fā)條件 | 場(chǎng)景舉例 |
---|---|---|
NoSuchBeanDefinitionException | 當(dāng)查找 Bean 不存在于 IoC 容器 時(shí) | BeanFactory#getBean |
NoUniqueBeanDefinitionException | 類型依賴查找時(shí),IoC 容器存在多 個(gè) Bean 實(shí)例 | BeanFactory#getBean(Class) |
BeanInstantiationException | 當(dāng) Bean 所對(duì)應(yīng)的類型非具體類時(shí) | BeanFactory#getBean |
BeanCreationException | 當(dāng) Bean 初始化過程中 | Bean 初始化方法執(zhí)行異常時(shí) |
BeanDefinitionStoreException | 當(dāng) BeanDefinition 配置元信息非 法時(shí) | XML 配置資源無法打開時(shí) |
本文總結(jié)
好了,到這就基本上把 Spring 依賴查找相關(guān)的知識(shí)點(diǎn)就總結(jié)完了,本文我們主要總結(jié)了依賴查找的幾種方式,包括單一類型、集合類型、層次性、延遲性以及 Spring 內(nèi)建 Bean 的依賴查找,并給出了 Spring 內(nèi)建 Bean 注冊(cè)的源碼位置,最后提到了依賴查找的幾個(gè)常見的異常,并給出了常見場(chǎng)景觸發(fā)的條件。
關(guān)于 Spring 依賴 還有依賴注入、依賴來源等知識(shí) 后面會(huì)跟進(jìn)梳理。
到此這篇關(guān)于Spring 依賴查找的使用小結(jié)的文章就介紹到這了,更多相關(guān)Spring 依賴查找內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JAVA多線程Thread和Runnable的實(shí)現(xiàn)
java中實(shí)現(xiàn)多線程有兩種方法:一種是繼承Thread類,另一種是實(shí)現(xiàn)Runnable接口。2013-03-03Springboot 全局時(shí)間格式化三種方式示例詳解
時(shí)間格式化在項(xiàng)目中使用頻率是非常高的,當(dāng)我們的 API? 接口返回結(jié)果,需要對(duì)其中某一個(gè) date? 字段屬性進(jìn)行特殊的格式化處理,通常會(huì)用到 SimpleDateFormat? 工具處理,這篇文章主要介紹了3 種 Springboot 全局時(shí)間格式化方式,需要的朋友可以參考下2024-01-01關(guān)于各種排列組合java算法實(shí)現(xiàn)方法
這篇文章介紹了幾種用JAVA實(shí)現(xiàn)的排列組合算法,有需要的朋友可以參考一下2013-06-06