实现方式

实现ApplicationContextAware类,重写setApplicationContext方法,并且添加@Component注解

@Component
public class SpringUtils implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) {
        if (Objects.isNull(SpringUtils.applicationContext)) {
            SpringUtils.applicationContext = applicationContext;
        }
    }

    public static String getProperty(String key) {
        return applicationContext.getEnvironment().getProperty(key);
    }
}

对静态变量进行赋值

public static String XXX = SpringUtils.getProperty("配置名");

⚠️ 如果获取配置时报空指针异常(applicationContext 还没有被初始化导致),在获取配置的类上面加入@DependsOn(“springUtils”) 即可

实现原理

1.Springboot项目启动的时候会执行main方法

SpringApplication.run(Application.class, args);

2.SpringApplication.run方法里面会调用AbstractApplicationContext.refresh()方法(这里涉及到springboot的启动流程)

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 刷新容器准备环境
        prepareRefresh();

        // 告诉子类刷新内部bean工厂
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 准备Bean工厂
        prepareBeanFactory(beanFactory);

        try {
            // 执行子类中的后置处理器:空方法
            postProcessBeanFactory(beanFactory);

            // 执行bean工厂的后置处理器
            invokeBeanFactoryPostProcessors(beanFactory);

            // 注册Bean的后置处理器
            registerBeanPostProcessors(beanFactory);

            // 初始化MessageSource
            initMessageSource();

            // 为此上下文初始化事件多播器。
            initApplicationEventMulticaster();

            // 让子类有点参与感,留给子类的初始化方法
            onRefresh();

            // 给容器中将所有项目里面的ApplicationListener注册进来 
            registerListeners();

            // 最后初始化bean工厂,初始化所有剩下的单实例bean 
            finishBeanFactoryInitialization(beanFactory);

            // 完成BeanFactory初始化创建工作
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

3.refresh()中的prepareBeanFactory()方法会设置ApplicationContextAwareProcessor(创建bean时会调用这个类的方法)

        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
		beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
		beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
		beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
		beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

4.ApplicationContextAwareProcessor 中有个重写了的前置处理方法,如果创建的这个bean的类实现了ApplicationContextAware接口,那么就会调用该类setApplicationContext方法进行初始化

@Override
@Nullable
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
	AccessControlContext acc = null;

	if (System.getSecurityManager() != null &&
			(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
					bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
					bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
		acc = this.applicationContext.getBeanFactory().getAccessControlContext();
	}

	if (acc != null) {
		AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
		    
		    // 1.看下这里!!!
			invokeAwareInterfaces(bean);
			return null;
		}, acc);
	}
	else {
		invokeAwareInterfaces(bean);
	}

	return bean;
}

private void invokeAwareInterfaces(Object bean) {
	if (bean instanceof Aware) {
		if (bean instanceof EnvironmentAware) {
			((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
		}
		if (bean instanceof EmbeddedValueResolverAware) {
			((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
		}
		if (bean instanceof ResourceLoaderAware) {
			((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
		}
		if (bean instanceof ApplicationEventPublisherAware) {
			((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
		}
		if (bean instanceof MessageSourceAware) {
			((MessageSourceAware) bean).setMessageSource(this.applicationContext);
		}
		
		// 2.看下这里!!!
		if (bean instanceof ApplicationContextAware) {
			((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
		}
	}
}

5.配置文件的数据是在 SpringApplication.run 中调用 prepareEnvironment()方法通准备环境数据时加载的(在refresh()执行之前就加载好了)

StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
	ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
	
	// 在这里加载配置文件中的数据
	ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
	configureIgnoreBeanInfo(environment);
	Banner printedBanner = printBanner(environment);
	context = createApplicationContext();
	exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
			new Class[] { ConfigurableApplicationContext.class }, context);
	
	// 在这里把配置文件的数据放到 applicationContext 中
	prepareContext(context, environment, listeners, applicationArguments, printedBanner);
	refreshContext(context);
	afterRefresh(context, applicationArguments);
	stopWatch.stop();
	if (this.logStartupInfo) {
		new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
	}
	listeners.started(context);
	callRunners(context, applicationArguments);
}
catch (Throwable ex) {
	handleRunFailure(context, ex, exceptionReporters, listeners);
	throw new IllegalStateException(ex);
}

6.初始化的处理逻辑有了,配置又有了,接下来就是触发初始化的逻辑了。
springboot启动流程中的这个方法:refresh() -> finishBeanFactoryInitialization(beanFactory) -> beanFactory.preInstantiateSingletons(),会通过getBean方法获取bean对象

for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
				    // 看下这里!!!
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged(
									(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
						    // 看下这里!!!
							getBean(beanName);
						}
					}
				}
				else {
				    // 看下这里!!!
					getBean(beanName);
				}
			}
		}

7.深入看下getBean() ->doGetBean()->createBean()->doCreateBean()->initializeBean()->applyBeanPostProcessorsBeforeInitialization()方法后,发现这里会遍历第3点设置的类,并执行第4点的前置处理方法

Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessBeforeInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;

总结

Springboot项目启动时(第一点),会设置自动处理类,并把applicationContext放进去(第二、三点),自动处理类里面会有些已经实现好的功能(第四点),springboot读取到配置文件后会把配置放到applicationContext中(第五点),在springboot初始化bean工厂时(第六点),在getBean方法里面会调用到自动处理类的相关方法进行处理(第七点)。

所以实现 ApplicationContextAware 接口,并加上 @Component 注解,可以在获取 bean 的时候自动调用重写的 setApplicationContext 方法,拿到包含配置文件中的配置的 applicationContext,再从 applicationContext 中获取自己想要的配置。