当前位置: 首页 > news >正文

Spring中除DI之外获取 BEAN 的方式​

前言

在 Spring 框架的开发实践中,获取 Bean 是极为重要的基础操作。我们熟知通过依赖注入能便捷地获取 Bean,不过在许多场景下,还需要借助其他方式来获取 Bean。本文将深入探讨在 Spring 中除了依赖注入之外获取 Bean 的多种方式,并结合实际代码示例进行详细讲解。

一、ApplicationContext 获取 Bean​

1.1 ApplicationContext 简介​

ApplicationContext 是 BeanFactory 的子接口,它为开发者提供了更为丰富的功能支持,涵盖国际化、资源访问以及事件传播等方面。在日常开发工作里,ApplicationContext 是使用最为广泛的 Spring 容器。常见的 ApplicationContext 实现类包括 ClassPathXmlApplicationContext(用于加载类路径下的 XML 配置文件)、FileSystemXmlApplicationContext(用于加载文件系统中的 XML 配置文件)以及 AnnotationConfigApplicationContext(用于加载基于注解的配置类)。​

1.2 使用 ClassPathXmlApplicationContext 获取 Bean​

假设我们有一个简单的 Spring 项目,其中包含一个 UserService 类,通过 XML 配置文件将其注册为 Bean。​

先定义 UserService 类:

public class UserService {public void sayHello() {System.out.println("Hello from UserService!");}
}

接着在 Spring 的 XML 配置文件(如 applicationContext.xml)中配置 UserService:

<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userService" class="com.example.service.UserService"/>
</beans>

然后通过 ClassPathXmlApplicationContext 来获取 UserService 的 Bean 实例:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) applicationContext.getBean("userService");userService.sayHello();}
}

在这段代码中,首先创建了一个 ClassPathXmlApplicationContext 对象,并加载了 applicationContext.xml 配置文件。随后,调用 applicationContext.getBean ("userService") 方法,依据 Bean 的 id 从容器中获取了 UserService 的实例,进而调用了其 sayHello 方法。

1.3 使用 AnnotationConfigApplicationContext 获取 Bean​

当项目采用基于注解的配置方式时,可以利用 AnnotationConfigApplicationContext 来获取 Bean。​

先定义一个配置类,以此替代 XML 配置文件:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class AppConfig {@Beanpublic UserService userService() {return new UserService();}
}

接着通过 AnnotationConfigApplicationContext 来获取 UserService 的 Bean 实例:

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Main {public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = applicationContext.getBean(UserService.class);userService.sayHello();}
}

这里创建了一个 AnnotationConfigApplicationContext 对象,并传入了配置类 AppConfig.class。通过 applicationContext.getBean (UserService.class) 方法,依据 Bean 的类型从容器中获取了 UserService 的实例。

1.4 applicationContext.getBean () 的更多使用方式​

按类型获取多个 Bean:若容器中有多个同一类型的 Bean,可以使用applicationContext.getBeansOfType(Class<T> type)方法获取一个 Map,其中键为 Bean 的名称,值为对应的 Bean 实例。例如:

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.Map;public class Main {public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);Map<String, UserService> userServiceMap = applicationContext.getBeansOfType(UserService.class);userServiceMap.forEach((name, service) -> service.sayHello());}
}

通过名称和类型获取 Bean:可以使用applicationContext.getBean(String name, Class<T> requiredType)方法,同时指定 Bean 的名称和类型来获取 Bean,这样能确保获取到的 Bean 类型准确无误,避免类型转换异常。例如:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = applicationContext.getBean("userService", UserService.class);userService.sayHello();}
}

通过 BeanFactoryPostProcessor 获取 Bean 定义:​

BeanFactoryPostProcessor 允许我们在容器实例化 Bean 之前,对 Bean 的定义(BeanDefinition)进行修改。我们也可以利用它来获取 Bean 定义相关信息。首先定义一个实现了 BeanFactoryPostProcessor 接口的类:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {BeanDefinition userServiceDefinition = beanFactory.getBeanDefinition("userService");// 可以在这里对BeanDefinition进行操作,比如修改属性等System.out.println("获取到UserService的BeanDefinition,类名为:" + userServiceDefinition.getBeanClassName());}
}

然后在配置文件中注册这个 BeanFactoryPostProcessor:

<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userService" class="com.example.service.UserService"/><bean class="com.example.config.CustomBeanFactoryPostProcessor"/>
</beans>

当 Spring 容器启动时,会调用 CustomBeanFactoryPostProcessor 的 postProcessBeanFactory 方法,从而获取到 UserService 的 BeanDefinition。

二、BeanFactory 获取 Bean​

2.1 BeanFactory 简介​

BeanFactory 是 Spring 框架中最基础的容器接口,它定义了 IOC 容器的基本功能规范,例如 Bean 的加载、实例化、获取等操作。尽管其功能相对简单,但却是 ApplicationContext 的基础。理解 BeanFactory 对于深入掌握 Spring 的 IOC 机制具有至关重要的意义。常见的 BeanFactory 实现类曾经是 XmlBeanFactory,但从 Spring 3.1 开始,XmlBeanFactory 已被废弃,如今推荐使用 DefaultListableBeanFactory。​

2.2 使用 DefaultListableBeanFactory 获取 Bean​

同样以 UserService 为例,假设使用 DefaultListableBeanFactory 来获取 Bean。​

首先创建一个 BeanDefinition 对象,用于定义 UserService 的 Bean 信息:

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;public class Main {public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition();beanFactory.registerBeanDefinition("userService", beanDefinition);UserService userService = (UserService) beanFactory.getBean("userService");userService.sayHello();}
}

在这段代码中,先创建了一个 DefaultListableBeanFactory 对象。接着,通过 BeanDefinitionBuilder 创建了一个 BeanDefinition 对象,用于描述 UserService 的 Bean 信息。随后,使用 beanFactory.registerBeanDefinition ("userService", beanDefinition) 方法将 UserService 的 Bean 定义注册到容器中。最后,通过 beanFactory.getBean ("userService") 方法从容器中获取 UserService 的实例。

2.3 BeanFactory 获取 Bean 的特点

延迟加载:只有在调用 getBean 方法获取 Bean 时,才会实例化该 Bean,这种特性适合对资源消耗较大、使用频率较低的 Bean。

轻量级:与 ApplicationContext 相比,BeanFactory 的功能较为简单,资源占用少,适用于对功能要求不高的轻量级应用场景或者特定的底层框架开发。

三、在 Web 项目中高效使用这些容器获取 Bean​

3.1 在 Servlet 环境中使用 ApplicationContext 获取 Bean​

在基于 Servlet 的 Web 项目中,可以通过实现 ServletContextListener 接口来获取 ApplicationContext。首先在 web.xml 中配置 ContextLoaderListener:

<context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

然后在 Servlet 中获取 ApplicationContext 并获取 Bean:

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import java.io.IOException;public class MyServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {ServletContext servletContext = getServletContext();WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);UserService userService = applicationContext.getBean(UserService.class);userService.sayHello();response.getWriter().println("Hello from Servlet with UserService");}
}

通过这种方式,在 Servlet 中就能高效地获取到 Spring 容器中的 Bean。

3.2 在 Spring MVC 项目中使用 ApplicationContext 获取 Bean​

在 Spring MVC 项目中,通常已经配置好了 ApplicationContext。可以通过 @Autowired 注解直接将 Bean 注入到 Controller 中,这种方式极为便捷高效。例如:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;@Controller
public class MyController {@Autowiredprivate UserService userService;@GetMapping("/hello")@ResponseBodypublic String hello() {userService.sayHello();return "Hello from Controller with UserService";}
}

通过这种方式,Spring MVC 会自动从 ApplicationContext 中获取对应的 Bean 并注入,大大减少了手动获取 Bean 的繁琐过程。但这属于依赖注入方式,这里仅作对比说明。在 Spring MVC 中不通过注入获取 Bean 的话,也可以在 Controller 中通过如下方式获取:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;@Controller
public class MyController {@Autowiredprivate ApplicationContext applicationContext;@GetMapping("/hello")@ResponseBodypublic String hello() {UserService userService = applicationContext.getBean(UserService.class);userService.sayHello();return "Hello from Controller with UserService";}
}

通过将 ApplicationContext 注入到 Controller,进而可以获取容器中的 Bean。

3.3 在 Web 项目中使用 BeanFactory 获取 Bean​

在 Web 项目中使用 BeanFactory 获取 Bean 的情况相对较少,但在一些特定场景下仍有应用。可以通过创建一个自定义的 ServletContextListener,在其中初始化 DefaultListableBeanFactory,并注册 BeanDefinition。例如:

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.web.context.ServletContextAware;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;@WebListener
public class BeanFactoryInitializer implements ServletContextListener, ServletContextAware {private ServletContext servletContext;@Overridepublic void contextInitialized(ServletContextEvent sce) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserService.class).getBeanDefinition();beanFactory.registerBeanDefinition("userService", beanDefinition);servletContext.setAttribute("beanFactory", beanFactory);}@Overridepublic void contextDestroyed(ServletContextEvent sce) {}@Overridepublic void setServletContext(ServletContext servletContext) {this.servletContext = servletContext;}
}

然后在 Servlet 中获取 BeanFactory 并获取 Bean:

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import java.io.IOException;public class MyServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {ServletContext servletContext = getServletContext();DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) servletContext.getAttribute("beanFactory");UserService userService = (UserService) beanFactory.getBean("userService");userService.sayHello();response.getWriter().println("Hello from Servlet with UserService from BeanFactory");}
}

不过,这种方式相对复杂,并且在 Web 项目中,ApplicationContext 通常能更好地满足需求,所以 BeanFactory 在 Web 项目中的使用相对受限。

3.4 在测试类中获取 Bean​

在进行单元测试或者集成测试时,我们也经常需要获取 Bean 来进行相关功能的测试。以 JUnit 和 Spring Boot 的测试为例:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertNotNull;@SpringBootTest
public class UserServiceTest {@Autowiredprivate ApplicationContext applicationContext;@Testpublic void testGetBean() {UserService userService = applicationContext.getBean(UserService.class);assertNotNull(userService);userService.sayHello();}
}

在这个测试类中,通过 @SpringBootTest 注解启动 Spring 应用上下文,然后将 ApplicationContext 注入到测试类中,进而可以获取到 UserService 的 Bean 实例并进行测试。这种方式在测试场景下为我们验证 Bean 的功能提供了便利。

四、ApplicationContext 与 BeanFactory 获取 Bean 的对比​

实例化时机:ApplicationContext 在容器启动时会实例化所有单例 Bean,而 BeanFactory 是在调用 getBean 方法时才实例化 Bean。​

功能丰富度:ApplicationContext 提供了更多的功能,如国际化、资源访问、事件发布等,而 BeanFactory 主要专注于 Bean 的基本管理功能。​

应用场景:ApplicationContext 适用于大多数企业级应用场景,尤其是对功能完整性要求较高的项目;BeanFactory 则适用于对资源消耗敏感、功能需求简单的轻量级应用或特定的底层框架开发。​

五、其他获取 Bean 的方式

5.1 使用 BeanLocatorFactoryBean

BeanLocatorFactoryBean是 Spring 提供的一个特殊的工厂 Bean,它可以通过指定的名称从当前或父容器中获取 Bean。首先,在配置文件中定义BeanLocatorFactoryBean:

<bean id="beanLocator" class="org.springframework.beans.factory.config.BeanLocatorFactoryBean"><property name="mappedName" value="userService"/>
</bean>

然后,在代码中获取beanLocator,实际上它返回的就是userService的 Bean 实例:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Main {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = (UserService) applicationContext.getBean("beanLocator");userService.sayHello();}
}

5.2 使用 ObjectFactory 接口​

ObjectFactory接口提供了一种延迟获取 Bean 的方式,特别适用于那些创建成本较高且可能不一定需要使用的 Bean。定义一个ObjectFactory:

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class AppConfig {@Beanpublic ObjectFactory<UserService> userServiceObjectFactory() {return () -> new UserService();}
}

在需要使用UserService的地方,通过注入ObjectFactory<UserService>来获取 Bean 实例,只有在调用getObject()方法时才会创建 Bean:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class AnotherService {private final ObjectFactory<UserService> userServiceObjectFactory;@Autowiredpublic AnotherService(ObjectFactory<UserService> userServiceObjectFactory) {this.userServiceObjectFactory = userServiceObjectFactory;}public void doSomething() {UserService userService = userServiceObjectFactory.getObject();userService.sayHello();}
}

3.3 使用 BeanFactoryAware 和 ApplicationContextAware 接口​

实现BeanFactoryAware接口可以让一个 Bean 获取到所在的BeanFactory,从而通过BeanFactory来获取其他 Bean。同样,实现ApplicationContextAware接口可以获取到ApplicationContext。​

先定义一个实现BeanFactoryAware接口的类:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component;@Component
public class BeanFactoryAwareComponent implements BeanFactoryAware {private BeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;}public void doSomething() {UserService userService = (UserService) beanFactory.getBean("userService");userService.sayHello();}
}

再定义一个实现ApplicationContextAware接口的类:

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;@Component
public class ApplicationContextAwareComponent implements ApplicationContextAware {private ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}public void doSomething() {UserService userService = applicationContext.getBean(UserService.class);userService.sayHello();}
}

通过实现这些接口,在 Bean 初始化时 Spring 会自动注入对应的BeanFactory或ApplicationContext,方便在类内部获取其他 Bean。

六、总结​

在 Spring 开发过程中,除了依赖注入这种常用方式外,ApplicationContext 和 BeanFactory 为我们提供了多种获取 Bean 的有效途径。ApplicationContext 凭借其丰富的功能和强大的特性,在众多场景中发挥着重要作用。而 BeanFactory 的延迟加载和轻量级特性,也在特定场景下展现出独特优势。此外,在 Web 项目以及测试场景中,我们也有相应的方式高效获取 Bean。开发者应根据项目的实际需求,合理选择合适的方式来获取 Bean,从而实现高效、稳定的应用开发。通过本文的详细讲解,希望能帮助大家更好地理解和掌握 Spring 中除注入外获取 Bean 的多种关键方式。​

http://www.xdnf.cn/news/362899.html

相关文章:

  • 数据结构每日一题day18(链表)★★★★★
  • 在自然语言处理任务中,像 BERT 这样的模型会在输入前自动加上一些特殊token
  • MCP(Model Context Protocol)是专为LLM(大语言模型)应用设计的标准化协议
  • CKESC STONE 200A-M 工业级电调技术测评:全场景适配的动力控制核心
  • 【谭浩强】第七章第14题
  • 【C语言】--指针超详解(三)
  • Qwen智能体qwen_agent与Assistant功能初探
  • 昆仑万维一季度营收增长46% AI业务成新增长点
  • epoch、batch size和steps_per_epoch的区别
  • Linux 大于2T磁盘分区
  • FPGA 41 ,ICMP 协议详细解析之构建网络诊断系统( ICMP 协议与 IP 协议理论详细解析 )
  • windows下,docker虚拟化使用nginx镜像部署vue3+vite项目
  • 数据库基础:概念、原理与实战示例
  • 多账号管理与自动化中的浏览器指纹对抗方案
  • 北斗导航 | RTKLib中重难点技术,公式,代码
  • 【质量管理】TRIZ因果链分析:解码质量问题的“多米诺效应“
  • 20250509——TOPSIS计算各方案得分
  • 怎么判断是不是公网IP?如何查看自己本地路由器是内网ip还是公网?
  • Lightweight App Alternatives
  • gpu硬件,gpu驱动,cuda,CUDA Toolkit,cudatoolkit,cudnn,nvcc概念解析
  • python---kafka常规使用
  • awesome-digital-human本地部署及配置:打造高情绪价值互动指南
  • Conda激活环境无效
  • 【星海随笔】信息安全相关标准
  • 江西同为科技有限公司受邀参展2025长江流域跨博会
  • 智芯Z20K144x MCU开发之时钟架构
  • 数字人肢体动作控制:从基础原理到实践路径!
  • PostgreSQL可见性映射VM
  • 3D模型格式转换组件HOOPS Exchange:高效赋能航空航天设计协同、数据一致!
  • Uniapp app 安卓手机(红米)自定义基座进行真机调试