内容概要
ApplicationContextAware
接口能够轻松感知并在Spring中获取应用上下文,进而访问容器中的其他Bean和资源,这增强了组件间的解耦,了代码的灵活性和可扩展性,是Spring框架中实现高级功能的关键接口之一。
核心概念
它能用来干啥?
为了方便理解,模拟一个业务场景。假如有一个功能模块负责处理订单,在这个模块中,有一个OrderService
类,它负责订单的创建、更新和查询等操作,单在处理订单的过程中,可能需要访问其他一些服务,比如用户服务来获取用户信息,或者库存服务来检查商品库存。
在这个场景中,可以使用ApplicationContextAware
接口来解决,可以创建一个类,比如叫OrderServiceContextAware
,让它实现ApplicationContextAware
接口,当Spring容器初始化这个类时,它会自动将应用上下文注入到这个类中,一旦注入了应用上下文,OrderServiceContextAware
就能够访问到容器中的其他Bean,包括用户服务和库存服务。
可以在OrderService
类中使用OrderServiceContextAware
来获取用户服务和库存服务的实例,而无需直接在OrderService
中注入这些服务,这样OrderService
类不直接依赖于其他服务的具体实现,而是通过OrderServiceContextAware
来间接访问这些服务。
它有哪些特性?
ApplicationContextAware
是Spring框架提供的一个特殊的回调接口,用于帮助对象(特别是普通的Java Bean)访问到Spring的应用上下文ApplicationContext
。
当在自己的类中实现ApplicationContextAware
接口时,可以通过Spring提供的回调机制访问到Spring的应用上下文,从而获得Spring IoC容器中的bean实例、配置信息以及进行国际化操作、事件发布等操作。
在ApplicationContextAware
接口中定义了一个setApplicationContext
方法,当类实现了该接口之后,Spring IoC容器会自动调用该方法并将当前的ApplicationContext
注入到所实现的setApplicationContext
方法中,这样就可以在该类中使用Spring的应用上下文了。
在一些开源的Spring工具库中会看到这种技术的使用,因为这些库往往需要与Spring容器交互,比如读取容器的配置,访问其他的bean等等,通过实现ApplicationContextAware
接口就可以非常方便地完成这些工作。
但注意,一般不推荐在的业务代码中使用,因为这样会增加代码与Spring的耦合性,违反了“依赖于抽象,而非依赖于具体实现”的面向对象设计原则。业务代码应当尽可能减少对具体框架的依赖,以提高代码的通用性和可移植性。
代码案例
下面是一个简单的代码案例,展示了如何使用ApplicationContextAware
接口来创建一个能够访问Spring应用上下文的类,如下代码:
首先,创建一个实现ApplicationContextAware
接口的类:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringContextUtil implements ApplicationContextAware {
// Spring应用上下文
private static ApplicationContext applicationContext;
// 当Spring容器创建该类的实例时,会自动调用此方法,注入应用上下文
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
SpringContextUtil.applicationContext = context;
}
// 提供一个静态方法,返回应用上下文
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
// 提供一个获取Bean的静态方法
public static <T> T getBean(Class<T> beanClass) {
if (applicationContext != null) {
return applicationContext.getBean(beanClass);
} else {
throw new IllegalStateException("ApplicationContext is not initialized yet!");
}
}
}
接着,定义一个简单的服务类作为Spring Bean:
import org.springframework.stereotype.Service;
@Service
public class DemoService {
public String sayHello() {
return "Hello from DemoService!";
}
}
然后,可以使用Spring的Java配置或者XML配置来定义和初始化这些Bean,假设使用Java配置:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public SpringContextUtil springContextUtil() {
return new SpringContextUtil();
}
@Bean
public DemoService demoService() {
return new DemoService();
}
}
最后,在客户端代码中,我们可以通过SpringContextUtil
类来获取DemoService
的实例并调用其方法:
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class ApplicationClient {
public static void main(String[] args) {
// 创建Spring上下文,注册Java配置类
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 关闭Spring上下文,以便能够正常结束程序(尽管在这个示例中可能不需要)
context.registerShutdownHook();
// 通过SpringContextUtil获取DemoService实例
DemoService demoService = SpringContextUtil.getBean(DemoService.class);
// 调用DemoService的sayHello方法
System.out.println(demoService.sayHello());
}
}
输出结果:
复制代码
Hello from DemoService!
以上代码展示了如何通过SpringContextUtil
访问Spring的应用上下文,并获取其中的一个bean(DemoService
)。
ApplicationClient
类是客户端代码的入口点,它初始化了一个AnnotationConfigApplicationContext
,通过它Spring可以创建和管理beans,随后客户端代码使用SpringContextUtil.getBean
静态方法来获取DemoService
的实例,并调用它的sayHello
方法。
核心API
ApplicationContextAware
接口是一个特殊的标记接口。
它允许实现类能够访问到ApplicationContext
,即Spring的应用上下文,ApplicationContextAware
中有且只有一个核心方法,如下是该方法的说明:
public interface ApplicationContextAware extends Aware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
setApplicationContext
方法的含义如下:
void setApplicationContext(ApplicationContext applicationContext) throws BeansException
:
这个方法会在Spring容器初始化的时候被自动调用,Spring容器会将当前的ApplicationContext
作为参数传递给该方法,实现这个接口的类可以在这个方法中保存对ApplicationContext
的引用,从而能够在后续的操作中使用这个上下文。
如下是实现ApplicationContextAware
接口的类的代码案例,如下:
@Component
public class MyApplicationContextAware implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public ApplicationContext getApplicationContext() {
return this.applicationContext;
}
}
技术原理
ApplicationContextAware
接口是Spring框架中用于让Bean感知到Spring应用上下文ApplicationContext
的存在的一个标记接口。
当Spring容器创建一个实现了ApplicationContextAware
接口的Bean时,它会自动调用该Bean的setApplicationContext
方法,并将当前的ApplicationContext
作为参数传入,这使得Bean能够访问到Spring容器的上下文,进而可以获取容器中的其他Bean、资源、配置信息等。
实现原理
- 标记接口:
ApplicationContextAware
接口本身并没有定义任何具体的业务逻辑,它只是一个标记接口,用于告诉Spring容器这个Bean需要特殊处理。 - 容器回调: 当Spring容器初始化一个Bean时,如果这个Bean实现了
ApplicationContextAware
接口,Spring容器会调用该Bean的setApplicationContext
方法,并将当前的ApplicationContext
对象作为参数传入。 - 内部状态保存: 实现
ApplicationContextAware
接口的Bean通常会在其内部保存这个传入的ApplicationContext
引用,以便后续使用。 - 使用上下文: 一旦Bean保存了
ApplicationContext
的引用,它就可以使用这个上下文来访问容器中的其他Bean,或者获取容器的其他服务。
工作机制
- Bean创建: 当Spring容器根据配置信息创建一个Bean实例时,它会检查这个Bean是否实现了任何
Aware
接口(包括ApplicationContextAware
)。 - Aware接口处理: 如果Bean实现了
Aware
接口,Spring容器会调用相应的set*
方法,注入相应的依赖,对于ApplicationContextAware
,容器会调用setApplicationContext
方法。 - 依赖注入: 在调用
setApplicationContext
方法时,Spring容器会将当前的ApplicationContext
对象注入到Bean中,这个注入过程是通过Java反射机制实现的。 - Bean后处理: 在Bean的所有属性被设置之后,Spring容器会调用Bean的后处理器(如果配置了的话),进行额外的初始化工作,对于实现了
InitializingBean
接口的Bean,还会调用其afterPropertiesSet
方法。 - 使用ApplicationContext: 一旦Bean被初始化并注入了
ApplicationContext
,它就可以使用这个上下文来执行各种操作,比如获取其他Bean、访问资源、读取配置等。
核心总结
ApplicationContextAware
接口允许开发者在Bean中轻松获取到应用上下文ApplicationContext
,这使得Bean能够灵活地访问Spring容器中的其他Bean和资源,增强了组件间的解耦和可扩展性。
但是,过度使用ApplicationContextAware
可能导致代码与Spring容器紧密耦合,降低了代码的可移植性和可测试性。
END!
END!
END!
往期回顾
精品文章
Java并发基础:原子类之AtomicMarkableReference全面解析!
Java并发基础:concurrent Flow API全面解析