GenericFilterBean lazily initializes its default environment

Alignment with HttpServletBean; consistent use of EnvironmentCapable.

Issue: SPR-15469
(cherry picked from commit 1ea54eb)
This commit is contained in:
Juergen Hoeller 2017-04-27 21:38:30 +02:00
parent 9b647021f7
commit dab56dbb05
6 changed files with 196 additions and 172 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2016 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -102,13 +102,6 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life
*/ */
void setParent(ApplicationContext parent); void setParent(ApplicationContext parent);
/**
* Return the Environment for this application context in configurable form.
* @since 3.1
*/
@Override
ConfigurableEnvironment getEnvironment();
/** /**
* Set the {@code Environment} for this application context. * Set the {@code Environment} for this application context.
* @param environment the new environment * @param environment the new environment
@ -116,6 +109,14 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life
*/ */
void setEnvironment(ConfigurableEnvironment environment); void setEnvironment(ConfigurableEnvironment environment);
/**
* Return the {@code Environment} for this application context in configurable
* form, allowing for further customization.
* @since 3.1
*/
@Override
ConfigurableEnvironment getEnvironment();
/** /**
* Add a new BeanFactoryPostProcessor that will get applied to the internal * Add a new BeanFactoryPostProcessor that will get applied to the internal
* bean factory of this application context on refresh, before any of the * bean factory of this application context on refresh, before any of the

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -25,11 +25,12 @@ import org.springframework.core.env.Environment;
* *
* @author Chris Beams * @author Chris Beams
* @since 3.1 * @since 3.1
* @see org.springframework.core.env.EnvironmentCapable
*/ */
public interface EnvironmentAware extends Aware { public interface EnvironmentAware extends Aware {
/** /**
* Set the {@code Environment} that this object runs in. * Set the {@code Environment} that this component runs in.
*/ */
void setEnvironment(Environment environment); void setEnvironment(Environment environment);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2016 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -281,8 +281,22 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
} }
/** /**
* {@inheritDoc} * Set the {@code Environment} for this application context.
* <p>If {@code null}, a new environment will be initialized via * <p>Default value is determined by {@link #createEnvironment()}. Replacing the
* default with this method is one option but configuration through {@link
* #getEnvironment()} should also be considered. In either case, such modifications
* should be performed <em>before</em> {@link #refresh()}.
* @see org.springframework.context.support.AbstractApplicationContext#createEnvironment
*/
@Override
public void setEnvironment(ConfigurableEnvironment environment) {
this.environment = environment;
}
/**
* Return the {@code Environment} for this application context in configurable
* form, allowing for further customization.
* <p>If none specified, a default environment will be initialized via
* {@link #createEnvironment()}. * {@link #createEnvironment()}.
*/ */
@Override @Override
@ -294,16 +308,12 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
} }
/** /**
* {@inheritDoc} * Create and return a new {@link StandardEnvironment}.
* <p>Default value is determined by {@link #createEnvironment()}. Replacing the * <p>Subclasses may override this method in order to supply
* default with this method is one option but configuration through {@link * a custom {@link ConfigurableEnvironment} implementation.
* #getEnvironment()} should also be considered. In either case, such modifications
* should be performed <em>before</em> {@link #refresh()}.
* @see org.springframework.context.support.AbstractApplicationContext#createEnvironment
*/ */
@Override protected ConfigurableEnvironment createEnvironment() {
public void setEnvironment(ConfigurableEnvironment environment) { return new StandardEnvironment();
this.environment = environment;
} }
/** /**
@ -444,7 +454,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
//--------------------------------------------------------------------- //---------------------------------------------------------------------
/** /**
* {@inheritDoc} * Set the parent of this application context.
* <p>The parent {@linkplain ApplicationContext#getEnvironment() environment} is * <p>The parent {@linkplain ApplicationContext#getEnvironment() environment} is
* {@linkplain ConfigurableEnvironment#merge(ConfigurableEnvironment) merged} with * {@linkplain ConfigurableEnvironment#merge(ConfigurableEnvironment) merged} with
* this (child) application context environment if the parent is non-{@code null} and * this (child) application context environment if the parent is non-{@code null} and
@ -495,15 +505,6 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
return this.applicationListeners; return this.applicationListeners;
} }
/**
* Create and return a new {@link StandardEnvironment}.
* <p>Subclasses may override this method in order to supply
* a custom {@link ConfigurableEnvironment} implementation.
*/
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
@Override @Override
public void refresh() throws BeansException, IllegalStateException { public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) { synchronized (this.startupShutdownMonitor) {
@ -935,13 +936,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
/** /**
* DisposableBean callback for destruction of this instance. * DisposableBean callback for destruction of this instance.
* Only called when the ApplicationContext itself is running * <p>The {@link #close()} method is the native way to shut down
* as a bean in another BeanFactory or ApplicationContext, * an ApplicationContext, which this method simply delegates to.
* which is rather unusual.
* <p>The {@code close} method is the native way to
* shut down an ApplicationContext.
* @see #close() * @see #close()
* @see org.springframework.beans.factory.access.SingletonBeanFactoryLocator
*/ */
@Override @Override
public void destroy() { public void destroy() {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -41,7 +41,8 @@ package org.springframework.core.env;
public interface EnvironmentCapable { public interface EnvironmentCapable {
/** /**
* Return the {@link Environment} associated with this component. * Return the {@link Environment} associated with this component
* (may be {@code null} or a default environment).
*/ */
Environment getEnvironment(); Environment getEnvironment();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2012 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -38,10 +38,12 @@ import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.EnvironmentAware; import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceEditor; import org.springframework.core.io.ResourceEditor;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.context.ServletContextAware; import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.support.ServletContextResourceLoader; import org.springframework.web.context.support.ServletContextResourceLoader;
@ -75,25 +77,21 @@ import org.springframework.web.util.NestedServletException;
* @see #initFilterBean * @see #initFilterBean
* @see #doFilter * @see #doFilter
*/ */
public abstract class GenericFilterBean implements public abstract class GenericFilterBean implements Filter, BeanNameAware, EnvironmentAware,
Filter, BeanNameAware, EnvironmentAware, ServletContextAware, InitializingBean, DisposableBean { EnvironmentCapable, ServletContextAware, InitializingBean, DisposableBean {
/** Logger available to subclasses */ /** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
/** private String beanName;
* Set of required properties (Strings) that must be supplied as
* config parameters to this filter. private Environment environment;
*/
private final Set<String> requiredProperties = new HashSet<String>(); private ServletContext servletContext;
private FilterConfig filterConfig; private FilterConfig filterConfig;
private String beanName; private final Set<String> requiredProperties = new HashSet<String>(4);
private Environment environment = new StandardServletEnvironment();
private ServletContext servletContext;
/** /**
@ -104,24 +102,47 @@ public abstract class GenericFilterBean implements
* @see #getFilterName() * @see #getFilterName()
*/ */
@Override @Override
public final void setBeanName(String beanName) { public void setBeanName(String beanName) {
this.beanName = beanName; this.beanName = beanName;
} }
/** /**
* {@inheritDoc} * Set the {@code Environment} that this filter runs in.
* <p>Any environment set here overrides the {@link StandardServletEnvironment} * <p>Any environment set here overrides the {@link StandardServletEnvironment}
* provided by default. * provided by default.
* <p>This {@code Environment} object is used only for resolving placeholders in * <p>This {@code Environment} object is used only for resolving placeholders in
* resource paths passed into init-parameters for this filter. If no init-params are * resource paths passed into init-parameters for this filter. If no init-params are
* used, this {@code Environment} can be essentially ignored. * used, this {@code Environment} can be essentially ignored.
* @see #init(FilterConfig)
*/ */
@Override @Override
public void setEnvironment(Environment environment) { public void setEnvironment(Environment environment) {
this.environment = environment; this.environment = environment;
} }
/**
* Return the {@link Environment} associated with this filter.
* <p>If none specified, a default environment will be initialized via
* {@link #createEnvironment()}.
* @since 4.3.9
*/
@Override
public Environment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
/**
* Create and return a new {@link StandardServletEnvironment}.
* <p>Subclasses may override this in order to configure the environment or
* specialize the environment type returned.
* @since 4.3.9
*/
protected Environment createEnvironment() {
return new StandardServletEnvironment();
}
/** /**
* Stores the ServletContext that the bean factory runs in. * Stores the ServletContext that the bean factory runs in.
* <p>Only relevant in case of initialization as bean, to have a ServletContext * <p>Only relevant in case of initialization as bean, to have a ServletContext
@ -130,7 +151,7 @@ public abstract class GenericFilterBean implements
* @see #getServletContext() * @see #getServletContext()
*/ */
@Override @Override
public final void setServletContext(ServletContext servletContext) { public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext; this.servletContext = servletContext;
} }
@ -147,6 +168,16 @@ public abstract class GenericFilterBean implements
initFilterBean(); initFilterBean();
} }
/**
* Subclasses may override this to perform custom filter shutdown.
* <p>Note: This method will be called from standard filter destruction
* as well as filter bean destruction in a Spring application context.
* <p>This default implementation is empty.
*/
@Override
public void destroy() {
}
/** /**
* Subclasses can invoke this method to specify that this property * Subclasses can invoke this method to specify that this property
@ -180,11 +211,16 @@ public abstract class GenericFilterBean implements
this.filterConfig = filterConfig; this.filterConfig = filterConfig;
// Set bean properties from init parameters. // Set bean properties from init parameters.
try {
PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties); PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext()); ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment)); Environment env = this.environment;
if (env == null) {
env = new StandardServletEnvironment();
}
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, env));
initBeanWrapper(bw); initBeanWrapper(bw);
bw.setPropertyValues(pvs, true); bw.setPropertyValues(pvs, true);
} }
@ -194,6 +230,7 @@ public abstract class GenericFilterBean implements
logger.error(msg, ex); logger.error(msg, ex);
throw new NestedServletException(msg, ex); throw new NestedServletException(msg, ex);
} }
}
// Let subclasses do whatever initialization they like. // Let subclasses do whatever initialization they like.
initFilterBean(); initFilterBean();
@ -214,6 +251,20 @@ public abstract class GenericFilterBean implements
protected void initBeanWrapper(BeanWrapper bw) throws BeansException { protected void initBeanWrapper(BeanWrapper bw) throws BeansException {
} }
/**
* Subclasses may override this to perform custom initialization.
* All bean properties of this filter will have been set before this
* method is invoked.
* <p>Note: This method will be called from standard filter initialization
* as well as filter bean initialization in a Spring application context.
* Filter name and ServletContext will be available in both cases.
* <p>This default implementation is empty.
* @throws ServletException if subclass initialization fails
* @see #getFilterName()
* @see #getServletContext()
*/
protected void initFilterBean() throws ServletException {
}
/** /**
* Make the FilterConfig of this filter available, if any. * Make the FilterConfig of this filter available, if any.
@ -258,32 +309,6 @@ public abstract class GenericFilterBean implements
} }
/**
* Subclasses may override this to perform custom initialization.
* All bean properties of this filter will have been set before this
* method is invoked.
* <p>Note: This method will be called from standard filter initialization
* as well as filter bean initialization in a Spring application context.
* Filter name and ServletContext will be available in both cases.
* <p>This default implementation is empty.
* @throws ServletException if subclass initialization fails
* @see #getFilterName()
* @see #getServletContext()
*/
protected void initFilterBean() throws ServletException {
}
/**
* Subclasses may override this to perform custom filter shutdown.
* <p>Note: This method will be called from standard filter destruction
* as well as filter bean destruction in a Spring application context.
* <p>This default implementation is empty.
*/
@Override
public void destroy() {
}
/** /**
* PropertyValues implementation created from FilterConfig init parameters. * PropertyValues implementation created from FilterConfig init parameters.
*/ */
@ -300,12 +325,12 @@ public abstract class GenericFilterBean implements
public FilterConfigPropertyValues(FilterConfig config, Set<String> requiredProperties) public FilterConfigPropertyValues(FilterConfig config, Set<String> requiredProperties)
throws ServletException { throws ServletException {
Set<String> missingProps = (requiredProperties != null && !requiredProperties.isEmpty()) ? Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ?
new HashSet<String>(requiredProperties) : null; new HashSet<String>(requiredProperties) : null);
Enumeration<?> en = config.getInitParameterNames(); Enumeration<String> paramNames = config.getInitParameterNames();
while (en.hasMoreElements()) { while (paramNames.hasMoreElements()) {
String property = (String) en.nextElement(); String property = paramNames.nextElement();
Object value = config.getInitParameter(property); Object value = config.getInitParameter(property);
addPropertyValue(new PropertyValue(property, value)); addPropertyValue(new PropertyValue(property, value));
if (missingProps != null) { if (missingProps != null) {
@ -314,7 +339,7 @@ public abstract class GenericFilterBean implements
} }
// Fail if we are still missing properties. // Fail if we are still missing properties.
if (missingProps != null && missingProps.size() > 0) { if (!CollectionUtils.isEmpty(missingProps)) {
throw new ServletException( throw new ServletException(
"Initialization from FilterConfig for filter '" + config.getFilterName() + "Initialization from FilterConfig for filter '" + config.getFilterName() +
"' failed; the following required properties were missing: " + "' failed; the following required properties were missing: " +

View File

@ -84,14 +84,10 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
/** Logger available to subclasses */ /** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
/**
* Set of required properties (Strings) that must be supplied as
* config parameters to this servlet.
*/
private final Set<String> requiredProperties = new HashSet<String>();
private ConfigurableEnvironment environment; private ConfigurableEnvironment environment;
private final Set<String> requiredProperties = new HashSet<String>(4);
/** /**
* Subclasses can invoke this method to specify that this property * Subclasses can invoke this method to specify that this property
@ -106,6 +102,41 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
this.requiredProperties.add(property); this.requiredProperties.add(property);
} }
/**
* Set the {@code Environment} that this servlet runs in.
* <p>Any environment set here overrides the {@link StandardServletEnvironment}
* provided by default.
* @throws IllegalArgumentException if environment is not assignable to
* {@code ConfigurableEnvironment}
*/
@Override
public void setEnvironment(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment, "ConfigurableEnvironment required");
this.environment = (ConfigurableEnvironment) environment;
}
/**
* Return the {@link Environment} associated with this servlet.
* <p>If none specified, a default environment will be initialized via
* {@link #createEnvironment()}.
*/
@Override
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
/**
* Create and return a new {@link StandardServletEnvironment}.
* <p>Subclasses may override this in order to configure the environment or
* specialize the environment type returned.
*/
protected ConfigurableEnvironment createEnvironment() {
return new StandardServletEnvironment();
}
/** /**
* Map config parameters onto bean properties of this servlet, and * Map config parameters onto bean properties of this servlet, and
* invoke subclass initialization. * invoke subclass initialization.
@ -119,8 +150,9 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
} }
// Set bean properties from init parameters. // Set bean properties from init parameters.
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
@ -133,6 +165,7 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
} }
throw ex; throw ex;
} }
}
// Let subclasses do whatever initialization they like. // Let subclasses do whatever initialization they like.
initServletBean(); initServletBean();
@ -153,6 +186,15 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
protected void initBeanWrapper(BeanWrapper bw) throws BeansException { protected void initBeanWrapper(BeanWrapper bw) throws BeansException {
} }
/**
* Subclasses may override this to perform custom initialization.
* All bean properties of this servlet will have been set before this
* method is invoked.
* <p>This default implementation is empty.
* @throws ServletException if subclass initialization fails
*/
protected void initServletBean() throws ServletException {
}
/** /**
* Overridden method that simply returns {@code null} when no * Overridden method that simply returns {@code null} when no
@ -175,49 +217,6 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
} }
/**
* Subclasses may override this to perform custom initialization.
* All bean properties of this servlet will have been set before this
* method is invoked.
* <p>This default implementation is empty.
* @throws ServletException if subclass initialization fails
*/
protected void initServletBean() throws ServletException {
}
/**
* {@inheritDoc}
* @throws IllegalArgumentException if environment is not assignable to
* {@code ConfigurableEnvironment}.
*/
@Override
public void setEnvironment(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment, "ConfigurableEnvironment required");
this.environment = (ConfigurableEnvironment) environment;
}
/**
* {@inheritDoc}
* <p>If {@code null}, a new environment will be initialized via
* {@link #createEnvironment()}.
*/
@Override
public ConfigurableEnvironment 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 ConfigurableEnvironment createEnvironment() {
return new StandardServletEnvironment();
}
/** /**
* PropertyValues implementation created from ServletConfig init parameters. * PropertyValues implementation created from ServletConfig init parameters.
*/ */
@ -233,7 +232,7 @@ public abstract class HttpServletBean extends HttpServlet implements Environment
public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties) public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)
throws ServletException { throws ServletException {
Set<String> missingProps = (requiredProperties != null && !requiredProperties.isEmpty() ? Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ?
new HashSet<String>(requiredProperties) : null); new HashSet<String>(requiredProperties) : null);
Enumeration<String> paramNames = config.getInitParameterNames(); Enumeration<String> paramNames = config.getInitParameterNames();