提到Spring框架可能更多的人想到的是它的IOC特性和AOP特性,很少有人会注意到基于Spring的这些特性做出的扩展,既然Spring框架能从众多的框架中脱颖而出一定是有它的过人之处的,其实在使用Spring框架或者是在使用Spring Boot框架的时候,我们很少能够发现它所具有的扩展能力。而下面这个系列我们就来跟大家一起分享关于Spring框架的最为常用的11个扩展点。
在SpringBoot中要想获取到容器中的一个Bean对象非常简单,但是有没有人相关如何获取一个容器对象呢?
如何获取容器对象?
在Spring中提供了很多的获取容器对象的方式,其中最为常见的就是通过ApplicationContext接口来获取。了解过Spring源码的人也知道,ApplicationContext是Spring框架中比较核心的一个接口,它提供了用来访问SpringIOC容器的各种功能和服务的出口。下面我们就来总结一下,在SpringBoot中如何获取容器对象。
第一种、通过注解的方式
按照Spring的概念,其实容器本身也是一个Bean对象,也就是说它也可以使用@Autowired或者是通过@Resource来引入进行使用。如下所示。
@Component
public class MyClass {
@Autowired
private ApplicationContext applicationContext;
// 使用 applicationContext 对象进行操作
}
第二种、通过静态类的方式
在一些非容器管理的类中,我们可以通过定义一个SpringContextHolder工具类来获取到ApplicationContext来进行扩展的操作,这种方式一般用在没有办法通过注解的方式来引入的场景中。如下所示。
@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
applicationContext = context;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
这里需要注意的是,虽然SpringContextHolder是通过@Component注入到了容器中,但是ApplicationContext的获取还是依托于静态的对象来进行获取操作。
第三种、通过BeanFactory获取
BeanFactory作为SpringIOC容器的最底层的实现,是ApplicationContext的父类,通过它也可以获取到容器对象。还有与之功能类似的BeanFactoryAware,都可以提供获取容器对象的方法,如下所示。
@Component
public class MyClass {
@Autowired
private BeanFactory beanFactory;
// 使用 beanFactory 对象进行操作
}
或者是通过BeanFactoryAware来实现如下的操作。
@Service
public class PersonService implements BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public void add() {
Person person = (Person) beanFactory.getBean("person");
}
}
第四种、通过ApplicationListener
通过ApplicationListener也可以获取到容器对象,ApplicationListener是在Spring框架中用来监听程序处理事件的接口,可以通过这个接口来实现对于Spring中的事件的监听操作,并且在相应的事件发生的时候,进行某个特定逻辑的执行,在这个接口的实现类中,可以通过注入ApplicationContext来获取容器对象,如下所示。
@Component
public class MyApplicationListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
// 在这里可以使用 applicationContext 对象进行操作
System.out.println("Received application event: " + event.getClass().getSimpleName());
}
}
获取容器对象可以做什么?
首先在我们对应用程序进行扩展的时候,必不可少的就是往容器中注入对象,那么在已知的情况下,容器中的对象都是在程序开发初期就进行了依赖的配置都是设置好的,在启动程序的时候就可以直接在容器中注入对象并且使用了。但是在有些场景中。需要对容器对象进行一些动态的操作,例如动态读取配置、动态发布事件、动态注册或者是获取Bean对象等。在这种场景下,就需要我们对容器对象进行操作了,下面我们通过两个例子来看看容器对象的使用场景。
动态注入并且获取Bean对象?
场景一、实现动态Bean的注入
整合Spring Boot项目中实现一个SpringUtils的工具类用来动态的注入或者获取容器中的Bean对象。
首先编写一个SpringUtils的工具类,如下所示。
public class SpringUtils {
private static final ApplicationContext applicationContext = new AnnotationConfigApplicationContext();
public static void registerBean(String beanName, Class> beanClass) {
BeanDefinition beanDefinition = ((AnnotationConfigApplicationContext) applicationContext).getBeanFactory().createBeanDefinition(beanClass);
((AnnotationConfigApplicationContext) applicationContext).getBeanFactory().registerSingleton(beanName, beanDefinition);
}
public static T getBean(String beanName, Class beanClass) {
return applicationContext.getBean(beanName, beanClass);
}
}
在工具类添加了一个ApplicationContext对象。并且通过registerBean()方法动态的注入一个Bean对象到容器中,然后通过getBean()方法来动态获取到容器中的Bean对象。如下所示。
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
// 注册动态 Bean
SpringUtils.registerBean("dynamicBean", MyBean.class);
// 获取动态 Bean 并调用方法
MyBean dynamicBean = SpringUtils.getBean("dynamicBean", MyBean.class);
dynamicBean.doSomething();
}
public static class MyBean {
public void doSomething() {
System.out.println("Dynamic bean is doing something.");
}
}
}
需要注意,这里只是一个简单的演示,在实际项目中我们的代码肯定不能这么写,需要对代码进行调整,但是基本的原理是不变的。在DemoApplication类的main方法中使用了SpringUtils工具类来注册动态Bean和获取动态Bean。
场景二、动态配置
当然了第二个场景也是在我们开发中比较常用的场景,就是动态配置的问题,我们经常想在不重启项目的情况下就对应用中的配置内容进行修改,那么容器扩展就提供了这样可以在不重启项目的情况下动态调整配置的操作。
在Spring Boot中,通过获取容器对象来动态调整配置通常是通过修改Environment对象来实现的。Environment对象包含了应用程序的配置属性,可以在运行时动态地修改这些属性来调整应用程序的行为。代码如下所示。
@Component
public class DynamicConfigUpdater {
private final ConfigurableEnvironment environment;
@Autowired
public DynamicConfigUpdater(ConfigurableEnvironment environment) {
this.environment = environment;
}
public void updateConfigProperty(String propertyName, String propertyValue) {
environment.getPropertySources().addFirst(new CustomPropertySource(propertyName, propertyValue));
}
private static class CustomPropertySource extends MapPropertySource {
public CustomPropertySource(String propertyName, String propertyValue) {
super(propertyName, Collections.singletonMap(propertyName, propertyValue));
}
}
}
这个类就是用于动态修改配置属性的类。注入了ConfigurableEnvironment对象,并提供了一个updateConfigProperty方法用于动态修改配置属性。该方法通过向PropertySource添加新的属性来实现。那么如何使用这个类呢?如下所示。
@SpringBootApplication
public class DynamicConfigApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DynamicConfigApplication.class, args);
// 获取 DynamicConfigUpdater bean
DynamicConfigUpdater configUpdater = context.getBean(DynamicConfigUpdater.class);
// 动态修改配置属性
configUpdater.updateConfigProperty("app.dynamic.property", "newValue");
// 重新加载上下文以应用新的配置
context.refresh();
}
}
在main方法中获取DynamicConfigUpdater Bean对象,并使用它来动态修改配置属性。然后,我们调用了refresh方法重新加载应用程序上下文,以应用新的配置。
通过这种方式,我们可以在运行时动态地调整Spring Boot应用程序的配置属性,而无需重启应用程序。
总结
总体来讲,获取容器对象可以让你对 Spring 容器进行全面的管理和控制,实现更加灵活和可扩展的应用程序架构。
Tags:propertysource