前言
HttpServletBean
实现了 EnvironmentCapable
和 EnvironmentAware
接口。
Environment可以对一些应用变量进行初始化, 存储到内存中。方便 Bean
进行获取。
Environment 初始化
查看 DispatcherServlet
的父类 HttpServletBean
的源码, 可以看到 HttpServletBean
的属性 ConfigurableEnvironment
接口的实现类是 StandardServletEnvironment
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware { private ConfigurableEnvironment environment; @Override public void setEnvironment(Environment environment) { this.environment = (ConfigurableEnvironment) environment; } @Override public ConfigurableEnvironment getEnvironment() { if (this.environment == null) { this.environment = createEnvironment(); } return this.environment; } protected ConfigurableEnvironment createEnvironment() { return new StandardServletEnvironment(); } }
|
继承树
先明确一个概念, Environment 是用来装载 属性
的。
StandardServletEnvironment
的父类 AbstractEnvironment
里面的 MutablePropertySources
属性(可以看成 LinkedHashMap<String, String>
)封装了 5 个属性(稍后说明为什么)
- ServletContext ( 封装 context-param )
- ServletConfig ( 封装 init-param )
- JndiProperty
- 系统环境变量
- JVM 系统属性变量
那么观察下子类 StandardServletEnvironment
的源码,以及父类StandardEnvironment
的源码, 发现主要的方法就是 customizePropertySources()
, 也就是对 MutablePropertySources
属性进行操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment { @Override protected void customizePropertySources(MutablePropertySources propertySources) { propertySources.addLast(new StubPropertySource("servletConfigInitParams")); propertySources.addLast(new StubPropertySource("servletContextInitParams")); if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) { propertySources.addLast(new JndiPropertySource("jndiProperties")); } super.customizePropertySources(propertySources); } @Override public void initPropertySources(ServletContext servletContext, ServletConfig servletConfig) { WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig); } }
public class StandardEnvironment extends AbstractEnvironment { @Override protected void customizePropertySources(MutablePropertySources propertySources) { propertySources.addLast(new MapPropertySource("systemEnvironment", getSystemProperties())); propertySources.addLast(new SystemEnvironmentPropertySource("systemProperties", getSystemEnvironment())); } @Override public Map<String, Object> getSystemProperties() { try { return (Map) System.getProperties(); } catch (AccessControlException ex) { return (Map) new ReadOnlySystemAttributesMap() { @Override protected String getSystemAttribute(String attributeName) { try { return System.getProperty(attributeName); } catch (AccessControlException ex) { return null; } } }; } } @Override public Map<String, Object> getSystemEnvironment() { if (suppressGetenvAccess()) { return Collections.emptyMap(); } try { return (Map) System.getenv(); } catch (AccessControlException ex) { return (Map) new ReadOnlySystemAttributesMap() { @Override protected String getSystemAttribute(String attributeName) { try { return System.getenv(attributeName); } catch (AccessControlException ex) { return null; } } }; } } }
|
可以猜测出方法的参数 propertySources
是一个装载 属性
的集合。那么 customizePropertySources
的参数 propertySources
一定不为 null
, 也就是 propertySources
被初始化过。
初始化一般发生在 构造方法
或者 field 直接 new
中。
但是 StandardServletEnvironment
并没有构造方法, 其父类
StandardEnvironment
也没有构造方法。
那么只能看最顶层的父类 AbstractEnvironment
。
1 2 3 4 5 6 7 8 9 10
| public abstract class AbstractEnvironment implements ConfigurableEnvironment { private final MutablePropertySources propertySources = new MutablePropertySources(this.logger); public AbstractEnvironment() { customizePropertySources(this.propertySources); } protected void customizePropertySources(MutablePropertySources propertySources) { } }
|
又看到了熟悉的 customizePropertySources()
方法。
可以看到 propertySources
是在 field 直接 new
出来的。
propertySources
的实现类是 MutablePropertySources
, 里面有一个
CopyOnWriteArrayList
集合,集合中的 PropertySource
是一个 键值对
, 可以看成是 Map.Entry
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| public class MutablePropertySources implements PropertySources { private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<PropertySource<?>>(); }
public interface PropertySources extends Iterable<PropertySource<?>> { boolean contains(String name); PropertySource<?> get(String name); }
public abstract class PropertySource<T> { protected final String name; protected final T source;
public boolean containsProperty(String name) { return (getProperty(name) != null); } public abstract Object getProperty(String name);
public static class StubPropertySource extends PropertySource<Object> { public StubPropertySource(String name) { super(name, new Object()); } @Override public String getProperty(String name) { return null; } }
static class ComparisonPropertySource extends StubPropertySource { } public static PropertySource<?> named(String name) { return new ComparisonPropertySource(name); } }
|
总结说, 就是 AbstractEnvironment
类的 MutablePropertySources
中的
CopyOnWriteArrayList
集合中, 装载了 5 个属性
- ServletContext ( StandardServletEnvironment 装载, 封装 context-param )
- ServletConfig ( StandardServletEnvironment 装载, 封装 init-param )
- JndiProperty ( StandardServletEnvironment 装载 )
- 系统环境变量 ( StandardEnvironment 装载 )
- JVM 系统属性变量 ( StandardEnvironment 装载 )、
需要注意的是,这里 ServletContext
和 ServletConfig
要等到
FrameworkServlet
刷新的时候调用 StandardServletEnvironment
的 initPropertySources
方法, 来刷新这两个属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment { @Override public void initPropertySources(ServletContext servletContext, ServletConfig servletConfig) { WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig); } }
public abstract class WebApplicationContextUtils { public static void initServletPropertySources( MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) { propertySources.replace("servletContextInitParams", new ServletContextPropertySource("servletContextInitParams", servletContext)); propertySources.replace("servletConfigInitParams", new ServletConfigPropertySource("servletConfigInitParams", servletConfig)); } }
|
Environment 的使用
Spring
开放了 EnvironmentAware
和 EnvironmentCapable
接口。
EnvironmentAware
由 Spring 框架往 Bean 内 注入Environment
EnvironmentCapable
通过 getEnvironment 往外面 暴露Environment
开发者只需要实现这两个接口,Spring就会自动注入。
1 2 3 4 5 6 7 8
| <?xml version="1.0" encoding="UTF-8"?> <web-app> <context-param> <param-name>myConfig</param-name> <param-value>hello</param-value> </context-param> </web-app>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class User implements EnvironmentAware, EnvironmentCapable { private String name; private int age; private Environment environment;
public String getHello() { return environment.getProperty("myConfig"); }
@Override public void setEnvironment(Environment environment) { this.environment = environment; }
@Override public Environment getEnvironment() { return environment; } }
|