Spring?依賴查找的使用小結(jié)
前言
源碼在我github的guide-spring倉庫中,可以克隆下來 直接執(zhí)行。
我們本文主要來介紹依賴查找的使用示例
依賴查找
什么是依賴查找
依賴查找并不是 Spring 框架特有的概念,它是一種在軟件開發(fā)中獲取依賴對象的方式。它通常用于獲取運行時需要的服務、組件或其他對象的引用。在面向?qū)ο缶幊讨?,依賴通常體現(xiàn)為一個對象需要另一個對象的服務或功能。
在不同的編程框架和容器中,依賴查找的方式可能會有所不同。我們簡單羅列一些常見的依賴查找的例子:
Java中的依賴查找
在純 Java 環(huán)境中,依賴查找通常通過構(gòu)造函數(shù)、方法參數(shù)或其他手段來獲得依賴對象的引用。
例如,通過在一個對象的構(gòu)造函數(shù)中傳遞另一個對象的引用:
public class MyClass {
private DependencyClass dependency;
public MyClass(DependencyClass dependency) {
this.dependency = dependency;
}
// ...
}Spring框架中的依賴查找
在Spring框架中,依賴查找通常通過 Spring 容器來實現(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. 實時查找
realtimeLookup(beanFactory);
}
private static void realtimeLookup(BeanFactory beanFactory) {
// 名稱+類型
User user = beanFactory.getBean("user", User.class);
System.out.println("實時查找: " + user);
}
}或者,通過在類中使用@Autowired注解來自動注入依賴:
@Service
public class MyService {
@Autowired
private DependencyClass dependency;
// ...
}Java EE中的依賴查找:
在Java EE環(huán)境中,你可以使用JNDI(Java Naming and Directory Interface)進行依賴查找。通過JNDI,你可以在運行時查找和獲取命名對象。
public class JNDIDependencyLookupDemo {
public static void main(String[] args) throws NamingException {
// 設置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)上查找一個名為 "user" 的對象
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 名稱 + 類型、類型等方式進行依賴查找 Bean。下面我們分別從單一類型、集合類型、層次類型、延遲等方式依次展示依賴查找的示例。
單一類型的依賴查找
單一類型的查找,需要要求容器中同一類型的Bean只能有一個為 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>
<!-- 當有多個 User 時,需要指出 其中一個 Bean 的 primary屬性為 true 否則會出現(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 類型查找========================
*/
/**
* 單個Bean類型查找
*
* @param beanFactory
*/
private static void typeLookup(BeanFactory beanFactory) {
User user = beanFactory.getBean(User.class);
System.out.println(user);
}
}集合類型的依賴查找
與單一類型查找的區(qū)別在于,它不需要指定 primary 并且 返回一個 Map<String,T> 對象,key 為 Bean 的名稱,value 為 Bean 實例。
public class UseDependencyLookupDemo {
public static void main(String[] args) throws Exception {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup.xml");
// 4. 按照類型查找多個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)在父子容器中,我們一般可能體會不到,實際上 Spring MVC 的底層就涉及父子容器的概念,即 Root ApplicationContext 和 Dispatcher-Servlet ApplicationContext。這里不展開了。我們通過一個簡單的示例來展示層次性依賴查找 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();
// 設置父容器
subBeanFactory.setParentBeanFactory(createParent());
// 展示 僅在當前 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 實例
beanDefinitionReader.loadBeanDefinitions(location);
return parentBeanFactory;
}
}延遲依賴查找
延遲依賴查找通常體現(xiàn)在懶加載 Bean 的場景,比如一些大資源的Bean希望在使用到的時候才會觸發(fā)初始化以達到降低服務啟動時間的目的,這個時候就可以使用懶加載模式,而在我們依賴查找的時候,使用延遲依賴查找的時候,也不會觸發(fā) Bean 的初始化,只有在真正使用到對象的時候才會觸發(fā)初始化。(ps 比較繞,我們直接看例子)
因為 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) 注冊的單例 Bean。具體如下:
- environment Environment 外部化配置以及Profiles
- systemProperties Properties Java系統(tǒng)屬性
- systemEnvironment Map<String,String> 操作系統(tǒng)環(huán)境變量
- messageSource MessageSource 國家化文案
- lifecycleProcessor LifecycleProcessor Lifecycle Bean 處理器
- applicationEventMulticaster ApplicationEventMulticaster Spring 事件廣播器
- internalConfigurationAnnotationProcessor ConfigurationClassPostProcessor 處理 Spring 的配置類
- internalAutowiredAnnotationProcessor AutowiredAnnotationBeanPostProcessor 處理 @Autowired 以及 @Value、@Inject注解
- internalCommonAnnotationProcessor CommonAnnotationBeanPostProcessor 處理 JSR-250 注解,如 @Resource、@PostConstruct等
- internalEventListenerProcessor EventListenerMethodProcessor 處理標注 @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);
// 關閉 Spring 容器上下文
context.close();
// 基于 注解驅(qū)動 的應用上下文
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ū)動的Spring應用上下文以及基于 注解 驅(qū)動的Spring應用上下文來實現(xiàn) Spring 內(nèi)建 Bean 的依賴查找。
ps: 如果一個默認的 ClassPathXmlAppplicationContext 不會包含ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor等這些注解的依賴,需要我們在 xml 配置文件中開啟 注解啟動,才會注冊進 Spring IoC容器中。
大家可能會有疑問,這些 Spring 內(nèi)建的 Bean 是什么時候被注冊進去呢?這里給下源碼位置,感興趣的可以自行查看:
org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory
org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)
依賴查找的常見異常
下面這些就是場景的在使用依賴查找的時候可能會觸發(fā)的異常,都是 BeansException 的子類型。場景比較清晰,這里就不寫具體的示例了。
| 異常類型 | 觸發(fā)條件 | 場景舉例 |
|---|---|---|
| NoSuchBeanDefinitionException | 當查找 Bean 不存在于 IoC 容器 時 | BeanFactory#getBean |
| NoUniqueBeanDefinitionException | 類型依賴查找時,IoC 容器存在多 個 Bean 實例 | BeanFactory#getBean(Class) |
| BeanInstantiationException | 當 Bean 所對應的類型非具體類時 | BeanFactory#getBean |
| BeanCreationException | 當 Bean 初始化過程中 | Bean 初始化方法執(zhí)行異常時 |
| BeanDefinitionStoreException | 當 BeanDefinition 配置元信息非 法時 | XML 配置資源無法打開時 |
本文總結(jié)
好了,到這就基本上把 Spring 依賴查找相關的知識點就總結(jié)完了,本文我們主要總結(jié)了依賴查找的幾種方式,包括單一類型、集合類型、層次性、延遲性以及 Spring 內(nèi)建 Bean 的依賴查找,并給出了 Spring 內(nèi)建 Bean 注冊的源碼位置,最后提到了依賴查找的幾個常見的異常,并給出了常見場景觸發(fā)的條件。
關于 Spring 依賴 還有依賴注入、依賴來源等知識 后面會跟進梳理。
到此這篇關于Spring 依賴查找的使用小結(jié)的文章就介紹到這了,更多相關Spring 依賴查找內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
JAVA多線程Thread和Runnable的實現(xiàn)
java中實現(xiàn)多線程有兩種方法:一種是繼承Thread類,另一種是實現(xiàn)Runnable接口。2013-03-03

