环境准备
前言
环境准备是ApplicationContext扩展功能refresh函数中的第一个步骤,接下来我们就详细的看看准备了什么?
环境准备
prepareRefresh函数主要是做些准备工作,例如对系统属性及环境变量的初始化及验证。
protected void prepareRefresh() { this.startupDate = System.currentTimeMillis(); this.closed.set(false); this.active.set(true); if (logger.isDebugEnabled()) { if (logger.isTraceEnabled()) { logger.trace("Refreshing " + this); } else { logger.debug("Refreshing " + getDisplayName()); } } // 留给子类覆盖 initPropertySources(); // 验证需要的属性文件是否都已经放入环境中 getEnvironment().validateRequiredProperties(); // Allow for the collection of early ApplicationEvents, // to be published once the multicaster is available... this.earlyApplicationEvents = new LinkedHashSet<>(); }
最后的两句代码才是关键,代码中没有什么逻辑处理,initPropertySources是空方法,专门留给子类去实现扩展功能的,没有任何的逻辑。而getEnvironment().validateRequiredProperties也因为没有需要验证的属性而没有做任何处理,那这些函数有什么用呢?Spring中存在相信就一定有它的作用,我们先来探索下各个函数的作用。
(1)initPropertySources正符合Spring的开放式结构设计,给用户最大扩展Spring的能力。用户可以根据自身的需求重写initPropertySources方法,并在方法中进行个性化的属性处理及设置。
(2)validateRequiredProperties则是对属性进行验证,那么如何验证呢?举个小例子来帮助理解:
假如现在有这样的一个需求,工程在运行过程中用到的某个设置(例如VAR)是从系统环境变量中取得的,而如果用户没有在系统环境变量中配置这个参数,那么工程可能不会工作。这一要求可能会有各种各样的解决办法,当然,在Spring中可以这样做,你可以直接修改Spring的源码,例如,修改ClassPathXmlApplicationContext。当然,最好的办法就是对源码进行扩展,可以自定义一个类:
public class MyClassPathApplicationContext extends ClassPathXmlApplicationContext { public MyClassPathApplicationContext(String... configLocation){ super(configLocation); } @Override protected void initPropertySources() { //添加验证要求 getEnvironment().setRequiredProperties("VAR"); } }
上述自定义的这个类继承自ClassPathXmlApplicationContext的MyClassPathApplicationContext,并重写了initPropertySources方法,在方法中我们添加了个性化的需求,那么在验证的时候也就是程序走到getEnvironment().validateRequiredProperties()代码的时候,如果系统并没有检测到对应VAR的环境变量,那么将会抛出异常。当然还有一步就是我们需要在使用的时候替换掉原有的ClassPathXmlApplicationContext:
public static void main(String[] args){ ApplicationContext ac = new MyClassPathApplicationContext("spring-config.xml"); User user = (User) ac.getBean("user"); System.out.println(user.getName()); }
如果环境中没有配置VAR变量,运行上述测试代码:
12月 28, 2018 9:38:07 下午 com.joe.applicant.MyClassPathApplicationContext prepareRefresh 信息: Refreshing [email protected]: startup date [Fri Dec 28 21:38:07 CST 2018]; root of context hierarchy Exception in thread "main" org.springframework.core.env.MissingRequiredPropertiesException: The following properties were declared as required but could not be resolved: [VAR] at org.springframework.core.env.AbstractPropertyResolver.validateRequiredProperties(AbstractPropertyResolver.java:140) at org.springframework.core.env.AbstractEnvironment.validateRequiredProperties(AbstractEnvironment.java:512) at org.springframework.context.support.AbstractApplicationContext.prepareRefresh(AbstractApplicationContext.java:591) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:512) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:93) at com.joe.applicant.MyClassPathApplicationContext.<init>(MyClassPathApplicationContext.java:10) at com.joe.applicant.MyClassPathApplicationContext.main(MyClassPathApplicationContext.java:21)
发现报错,提示环境中没有VAR变量。
设置环境变量:因为LZ使用的是IDEA,所以这里先展示如何设置环境变量:
第一步:点击 Run -------> Edit Configurations
第二步:点击 需要运行的类,在Environment variables 点击设置变量
第三步:设置对应变量
保存后。在运行上述测试代码:
十二月 29, 2018 10:40:21 上午 com.joe.mytag.application.MyClassPathApplicationContext prepareRefresh 信息: Refreshing [email protected]: startup date [Sat Dec 29 10:40:21 CST 2018]; root of context hierarchy 十二月 29, 2018 10:40:21 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions 信息: Loading XML bean definitions from class path resource [spring.xml] Joe
发现没有报错,成功运行。
参考:《Spring源码深度解析》 郝佳 编著: