引言:在使用 Spring 框架开发应用程序时,依赖注入是一个至关重要的概念。而对于 Bean 容器的注入方式,虽然我们可能都有一定的了解,但实际上很多人在被问及这个问题时可能并不能完整地回答。本文将深入探讨 Spring 中 Bean 容器的注入方式,包括 XML 配置方式、基于注解方式、Java 配置方式以及自动扫描方式,并提供了详细的代码示例和解析,以便读者全面了解并掌握这些方式。
题目
关于 Bean 容器的注入方式,99 % 的人都答不全!
推荐解析
1)XML配置方式
a) 基于属性注入
通过 <bean>
元素的属性来注入依赖。主要有以下几种方式:
-
构造器注入**:** 使用
<constructor-arg>
元素配置构造函数参数。<bean id="exampleBean" class="com.example.ExampleBean"> <constructor-arg ref="dependencyBean" /> </bean>
-
Setter方法注入: 使用
<property>
元素配置Setter方法注入的属性。<bean id="exampleBean" class="com.example.ExampleBean"> <property name="dependencyBean" ref="dependencyBean" /> </bean>
-
直接量注入: 使用
<constructor-arg>
或<property>
元素配置基本类型或字符串等直接值。<bean id="exampleBean" class="com.example.ExampleBean"> <constructor-arg value="exampleValue" /> </bean>
b) 基于注解方式
使用 @Autowired
、@Inject
或 @Resource
注解在类或字段上进行依赖注入。
-
构造器注入: 在类的构造器上使用
@Autowired
注解。@Component public class ExampleBean { private DependencyBean dependency; @Autowired public ExampleBean(DependencyBean dependency) { this.dependency = dependency; } }
-
Setter方法注入: 在Setter方法上使用
@Autowired
注解。@Component public class ExampleBean { private DependencyBean dependency; @Autowired public void setDependency(DependencyBean dependency) { this.dependency = dependency; } }
2)Java配置方式
通过Java类来配置Bean,并使用 @Bean
注解将Bean注册到Spring容器。
@Configuration
public class AppConfig {
@Bean
public ExampleBean exampleBean() {
return new ExampleBean(dependencyBean());
}
@Bean
public DependencyBean dependencyBean() {
return new DependencyBean();
}
}
3)自动扫描方式
使用 <context:component-scan>
或 @ComponentScan
注解来自动扫描和注册带有 @Component
及其衍生注解(如 @Service
、@Repository
、@Controller
等)的Bean。
<context:component-scan base-package="com.example" />
或者在Java配置类中使用:
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
// 配置其他Bean
}
4)注册BeanPostProcessor
实现 BeanPostProcessor
接口来在Bean初始化前后进行自定义处理,也可以用来动态注入Bean。
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ExampleBean) {
// 自定义处理逻辑,如动态注入其他依赖
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
基于条件的 Bean 注册
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
@Conditional(WindowsCondition.class) // 注册条件:Windows 操作系统
public OSInfo windowsInfo() {
return new OSInfo("Windows");
}
@Bean
@Conditional(LinuxCondition.class) // 注册条件:Linux 操作系统
public OSInfo linuxInfo() {
return new OSInfo("Linux");
}
}
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String osName = System.getProperty("os.name");
return osName.toLowerCase().contains("windows");
}
}
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String osName = System.getProperty("os.name");
return osName.toLowerCase().contains("linux");
}
}
其他补充
鱼聪明 AI 的回答:
鱼聪明 AI 地址:https://www.yucongming.com/
初始化阶段的回调
-
InitializingBean 接口:
InitializingBean
是 Spring 提供的一个接口,当 Bean 实现该接口时,Spring 容器在实例化 Bean 后会自动调用其afterPropertiesSet()
方法,用于执行初始化逻辑。import org.springframework.beans.factory.InitializingBean; public class MyBean implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { // 执行初始化逻辑 } }
-
@PostConstruct 注解:
@PostConstruct
是一个标准的 Java 注解,用于在 Bean 初始化之后执行特定的方法。当一个方法被@PostConstruct
注解标记时,Spring 容器会在实例化 Bean 后立即调用该方法。import javax.annotation.PostConstruct; public class MyBean { @PostConstruct public void init() { // 执行初始化逻辑 } }
销毁阶段的回调
-
DisposableBean 接口:
DisposableBean
是 Spring 提供的一个接口,当 Bean 实现该接口时,Spring 容器在销毁 Bean 之前会自动调用其destroy()
方法,用于执行销毁逻辑。import org.springframework.beans.factory.DisposableBean; public class MyBean implements DisposableBean { @Override public void destroy() throws Exception { // 执行销毁逻辑 } }
-
@PreDestroy 注解:
@PreDestroy
是一个标准的 Java 注解,用于在 Bean 销毁之前执行特定的方法。当一个方法被@PreDestroy
注解标记时,Spring 容器会在销毁 Bean 之前调用该方法。import javax.annotation.PreDestroy; public class MyBean { @PreDestroy public void cleanup() { // 执行销毁逻辑 } }
作用域(Scope)
在 Spring 中,Bean 的作用域决定了在容器中创建的 Bean 的可见范围和生命周期。Spring 框架提供了以下几种标准的作用域:
- Singleton(默认):在整个应用程序中只创建一个 Bean 实例,并在容器启动时就进行创建。所有对该 Bean 的请求都将返回同一个实例。
- Prototype:每次请求该 Bean 时都会创建一个新的实例。这意味着每次通过容器获取该 Bean 时,都会返回一个新的实例。
- Request:在每个 HTTP 请求中创建一个新的 Bean 实例,仅在 Web 应用程序上下文中有效。每个 HTTP 请求都会有自己的 Bean 实例,并且该实例仅在当前请求内有效。
- Session:在每个 HTTP Session 中创建一个新的 Bean 实例,也仅在 Web 应用程序上下文中有效。每个 HTTP Session 都会有自己的 Bean 实例,并且该实例仅在当前 Session 内有效。
- GlobalSession:在一个全局的 HTTP Session 中创建一个 Bean 实例,通常在分布式应用程序中使用。与 Session 作用域类似,但是在集群环境中只有一个 Bean 实例,仅在当前全局 Session 内有效。
生命周期管理
Spring 容器负责管理 Bean 的生命周期,确保它们在正确的时间点被创建、初始化、使用和销毁。生命周期管理主要涉及以下几个阶段:
- 实例化:Spring 容器根据配置信息或注解创建 Bean 的实例。
- 属性设置:Spring 容器将 Bean 的属性(依赖)注入到实例中。
- 初始化:如果 Bean 实现了
InitializingBean
接口,容器会调用其afterPropertiesSet()
方法,或者如果在方法上使用了@PostConstruct
注解,则在属性设置后立即调用被注解标记的方法。 - 使用:此时 Bean 实例已经完全初始化,可以被应用程序使用。
- 销毁:如果 Bean 实现了
DisposableBean
接口,容器会在销毁 Bean 之前调用其destroy()
方法,或者如果在方法上使用了@PreDestroy
注解,则在 Bean 销毁前调用被注解标记的方法。
自定义作用域和生命周期管理
除了上述提到的标准作用域和生命周期管理方式外,Spring 还允许开发者自定义作用域和生命周期管理策略。可以通过实现 Scope
接口来定义新的作用域,通过实现 BeanPostProcessor
接口来定义 Bean 的自定义初始化和销毁逻辑。
欢迎交流
本文主要介绍 Bean 容器注入方式和条件注册,另外有作用域和生命周期钩子函数的使用,面试中也经常会考实战开发方面的知识,大家可以去关注一下,在文末还有三个问题,欢迎小伙伴在评论多多留言!
1)Bean 容器注入方式有几个种?
2)Bean 的生命周期是怎么样的?
3)怎么实现基于条件的 Bean 注册?