Refactor to lazy Environment creation where possible

This commit avoids eager creation of Environment instances, favoring
delegation of already existing Environment objects where possible. For
example, FrameworkServlet creates an ApplicationContext; both require
a StandardServletEnvironment instance, and prior to this change, two
instances were created where one would suffice - indeed these two
instances may reasonably be expected to be the same. Now, the
FrameworkServlet defers creation of its Environment, allowing users to
supply a custom instance via its #setEnvironment method (e.g. within a
WebApplicationInitializer); the FrameworkServlet then takes care to
delegate that instance to the ApplicationContext created
in #createWebApplicationContext.

This behavior produces more consistent behavior with regard to
delegation of the environment, saves unnecessary cycles by avoiding
needless instantiation and calls to methods like
StandardServletEnvironment#initPropertySources and leads to better
logging output, as the user sees only one Environment created and
initialized when working with the FrameworkServlet/DispatcherServlet.

This commit also mirrors these changes across the corresponding
Portlet* classes.

Issue: SPR-9763
This commit is contained in:
Chris Beams 2012-09-05 21:55:41 +02:00
parent 9f8d219146
commit 6517517ca9
8 changed files with 117 additions and 23 deletions

View File

@ -73,6 +73,7 @@ import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
@ -224,7 +225,6 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
public AbstractApplicationContext(ApplicationContext parent) {
this.parent = parent;
this.resourcePatternResolver = getResourcePatternResolver();
this.environment = this.createEnvironment();
}
@ -276,7 +276,15 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
return this.parent;
}
/**
* {@inheritDoc}
* <p>If {@code null}, a new environment will be initialized via
* {@link #createEnvironment()}.
*/
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = this.createEnvironment();
}
return this.environment;
}
@ -387,9 +395,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
public void setParent(ApplicationContext parent) {
this.parent = parent;
if (parent != null) {
Object parentEnvironment = parent.getEnvironment();
Environment parentEnvironment = parent.getEnvironment();
if (parentEnvironment instanceof ConfigurableEnvironment) {
this.environment.merge((ConfigurableEnvironment)parentEnvironment);
this.getEnvironment().merge((ConfigurableEnvironment)parentEnvironment);
}
}
}
@ -505,7 +513,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
this.environment.validateRequiredProperties();
this.getEnvironment().validateRequiredProperties();
}
/**

View File

@ -134,7 +134,8 @@ public abstract class AbstractRefreshableWebApplicationContext extends AbstractR
}
/**
* Create and return a new {@link StandardServletEnvironment}.
* Create and return a new {@link StandardServletEnvironment}. Subclasses may override
* in order to configure the environment or specialize the environment type returned.
*/
@Override
protected ConfigurableEnvironment createEnvironment() {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -345,6 +345,7 @@ public abstract class FrameworkPortlet extends GenericPortletBean
pac.setId(ConfigurablePortletApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + getPortletName());
}
pac.setEnvironment(this.getEnvironment());
pac.setParent(parent);
pac.setPortletContext(getPortletContext());
pac.setPortletConfig(getPortletConfig());

View File

@ -34,13 +34,16 @@ import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceEditor;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.portlet.context.StandardPortletEnvironment;
import org.springframework.web.portlet.context.PortletContextResourceLoader;
import org.springframework.web.portlet.context.StandardPortletEnvironment;
/**
* Simple extension of <code>javax.portlet.GenericPortlet</code> that treats
@ -65,7 +68,8 @@ import org.springframework.web.portlet.context.PortletContextResourceLoader;
* @see #processAction
* @see FrameworkPortlet
*/
public abstract class GenericPortletBean extends GenericPortlet implements EnvironmentAware {
public abstract class GenericPortletBean extends GenericPortlet
implements EnvironmentCapable, EnvironmentAware {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
@ -76,7 +80,7 @@ public abstract class GenericPortletBean extends GenericPortlet implements Envir
*/
private final Set<String> requiredProperties = new HashSet<String>();
private Environment environment = new StandardPortletEnvironment();
private ConfigurableEnvironment environment;
/**
@ -107,7 +111,7 @@ public abstract class GenericPortletBean extends GenericPortlet implements Envir
PropertyValues pvs = new PortletConfigPropertyValues(getPortletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new PortletContextResourceLoader(getPortletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
@ -167,17 +171,39 @@ public abstract class GenericPortletBean extends GenericPortlet implements Envir
/**
* {@inheritDoc}
* <p>Any environment set here overrides the {@link StandardPortletEnvironment}
* provided by default.
* @throws IllegalArgumentException if environment is not assignable to
* {@code ConfigurableEnvironment}.
*/
public void setEnvironment(Environment environment) {
this.environment = environment;
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
this.environment = (ConfigurableEnvironment)environment;
}
/**
* {@inheritDoc}
* <p>If {@code null}, a new environment will be initialized via
* {@link #createEnvironment()}.
*/
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = this.createEnvironment();
}
return this.environment;
}
/**
* Create and return a new {@link StandardPortletEnvironment}. Subclasses may override
* in order to configure the environment or specialize the environment type returned.
*/
protected ConfigurableEnvironment createEnvironment() {
return new StandardPortletEnvironment();
}
/**
* PropertyValues implementation created from PortletConfig init parameters.
*/
@SuppressWarnings("serial")
private static class PortletConfigPropertyValues extends MutablePropertyValues {
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -75,7 +75,6 @@ public class StandardPortletEnvironment extends StandardEnvironment {
* @see org.springframework.core.env.AbstractEnvironment#customizePropertySources
* @see PortletConfigPropertySource
* @see PortletContextPropertySource
* @see AbstractRefreshablePortletApplicationContext#initPropertySources
* @see PortletApplicationContextUtils#initPortletPropertySources
*/
@Override

View File

@ -588,6 +588,7 @@ public abstract class FrameworkServlet extends HttpServletBean {
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(this.getEnvironment());
wac.setParent(parent);
wac.setConfigLocation(getContextConfigLocation());

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -35,11 +35,15 @@ import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceEditor;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.context.ConfigurableWebEnvironment;
import org.springframework.web.context.support.StandardServletEnvironment;
import org.springframework.web.context.support.ServletContextResourceLoader;
@ -76,7 +80,8 @@ import org.springframework.web.context.support.ServletContextResourceLoader;
* @see #doPost
*/
@SuppressWarnings("serial")
public abstract class HttpServletBean extends HttpServlet implements EnvironmentAware {
public abstract class HttpServletBean extends HttpServlet
implements EnvironmentCapable, EnvironmentAware {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
@ -87,7 +92,7 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
*/
private final Set<String> requiredProperties = new HashSet<String>();
private Environment environment = new StandardServletEnvironment();
private ConfigurableWebEnvironment environment;
/**
@ -120,7 +125,7 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
@ -182,11 +187,32 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
/**
* {@inheritDoc}
* <p>Any environment set here overrides the {@link StandardServletEnvironment}
* provided by default.
* @throws IllegalArgumentException if environment is not assignable to
* {@code ConfigurableWebEnvironment}.
*/
public void setEnvironment(Environment environment) {
this.environment = environment;
Assert.isInstanceOf(ConfigurableWebEnvironment.class, environment);
this.environment = (ConfigurableWebEnvironment)environment;
}
/**
* {@inheritDoc}
* <p>If {@code null}, a new environment will be initialized via
* {@link #createEnvironment()}.
*/
public ConfigurableWebEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = this.createEnvironment();
}
return this.environment;
}
/**
* Create and return a new {@link StandardServletEnvironment}. Subclasses may override
* in order to configure the environment or specialize the environment type returned.
*/
protected ConfigurableWebEnvironment createEnvironment() {
return new StandardServletEnvironment();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,6 +18,7 @@ package org.springframework.web.servlet;
import java.io.IOException;
import java.util.Locale;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
@ -33,14 +34,18 @@ import org.springframework.beans.PropertyValue;
import org.springframework.beans.TestBean;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletConfig;
import org.springframework.mock.web.MockServletContext;
import org.springframework.web.bind.EscapedErrors;
import org.springframework.web.context.ConfigurableWebEnvironment;
import org.springframework.web.context.ServletConfigAwareBean;
import org.springframework.web.context.ServletContextAwareBean;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.StandardServletEnvironment;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartHttpServletRequest;
@ -55,6 +60,9 @@ import org.springframework.web.servlet.theme.AbstractThemeResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.util.WebUtils;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
/**
* @author Rod Johnson
* @author Juergen Hoeller
@ -823,6 +831,30 @@ public class DispatcherServletTests extends TestCase {
servlet.destroy();
}
public void testEnvironmentOperations() {
DispatcherServlet servlet = new DispatcherServlet();
ConfigurableWebEnvironment defaultEnv = servlet.getEnvironment();
assertThat(defaultEnv, notNullValue());
ConfigurableEnvironment env1 = new StandardServletEnvironment();
servlet.setEnvironment(env1); // should succeed
assertThat(servlet.getEnvironment(), sameInstance(env1));
try {
servlet.setEnvironment(new StandardEnvironment());
fail("expected exception");
}
catch (IllegalArgumentException ex) {
}
class CustomServletEnvironment extends StandardServletEnvironment { }
@SuppressWarnings("serial")
DispatcherServlet custom = new DispatcherServlet() {
@Override
protected ConfigurableWebEnvironment createEnvironment() {
return new CustomServletEnvironment();
}
};
assertThat(custom.getEnvironment(), instanceOf(CustomServletEnvironment.class));
}
public static class ControllerFromParent implements Controller {