Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save dwelch2344/6017134 to your computer and use it in GitHub Desktop.

Select an option

Save dwelch2344/6017134 to your computer and use it in GitHub Desktop.
Some inheritable Spring containers for experimenting
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractRefreshableConfigApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.ui.context.Theme;
import org.springframework.ui.context.ThemeSource;
import org.springframework.ui.context.support.UiApplicationContextUtils;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.ConfigurableWebEnvironment;
import org.springframework.web.context.ServletConfigAware;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.support.ServletContextAwareProcessor;
import org.springframework.web.context.support.ServletContextResource;
import org.springframework.web.context.support.ServletContextResourcePatternResolver;
import org.springframework.web.context.support.StandardServletEnvironment;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.context.support.XmlWebApplicationContext;
/**
* {@link org.springframework.context.support.AbstractRefreshableApplicationContext}
* subclass which implements the
* {@link org.springframework.web.context.ConfigurableWebApplicationContext}
* interface for web environments. Provides a "configLocations" property,
* to be populated through the ConfigurableWebApplicationContext interface
* on web application startup.
*
* <p>This class is as easy to subclass as AbstractRefreshableApplicationContext:
* All you need to implements is the {@link #loadBeanDefinitions} method;
* see the superclass javadoc for details. Note that implementations are supposed
* to load bean definitions from the files specified by the locations returned
* by the {@link #getConfigLocations} method.
*
* <p>Interprets resource paths as servlet context resources, i.e. as paths beneath
* the web application root. Absolute paths, e.g. for files outside the web app root,
* can be accessed via "file:" URLs, as implemented by
* {@link org.springframework.core.io.DefaultResourceLoader}.
*
* <p>In addition to the special beans detected by
* {@link org.springframework.context.support.AbstractApplicationContext},
* this class detects a bean of type {@link org.springframework.ui.context.ThemeSource}
* in the context, under the special bean name "themeSource".
*
* <p><b>This is the web context to be subclassed for a different bean definition format.</b>
* Such a context implementation can be specified as "contextClass" context-param
* for {@link org.springframework.web.context.ContextLoader} or as "contextClass"
* init-param for {@link org.springframework.web.servlet.FrameworkServlet},
* replacing the default {@link XmlWebApplicationContext}. It will then automatically
* receive the "contextConfigLocation" context-param or init-param, respectively.
*
* <p>Note that WebApplicationContext implementations are generally supposed
* to configure themselves based on the configuration received through the
* {@link ConfigurableWebApplicationContext} interface. In contrast, a standalone
* application context might allow for configuration in custom startup code
* (for example, {@link org.springframework.context.support.GenericApplicationContext}).
*
* @author Juergen Hoeller
* @since 1.1.3
* @see #loadBeanDefinitions
* @see org.springframework.web.context.ConfigurableWebApplicationContext#setConfigLocations
* @see org.springframework.ui.context.ThemeSource
* @see XmlWebApplicationContext
*/
public abstract class InheritableAbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource {
/** Servlet context that this context runs in */
private ServletContext servletContext;
/** Servlet config that this context runs in, if any */
private ServletConfig servletConfig;
/** Namespace of this context, or {@code null} if root */
private String namespace;
/** the ThemeSource for this ApplicationContext */
private ThemeSource themeSource;
public InheritableAbstractRefreshableWebApplicationContext() {
setDisplayName("Root WebApplicationContext");
}
public InheritableAbstractRefreshableWebApplicationContext(ApplicationContext parentCtx) {
super(parentCtx);
setDisplayName("Root WebApplicationContext");
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public ServletContext getServletContext() {
return this.servletContext;
}
public void setServletConfig(ServletConfig servletConfig) {
this.servletConfig = servletConfig;
if (servletConfig != null && this.servletContext == null) {
setServletContext(servletConfig.getServletContext());
}
}
public ServletConfig getServletConfig() {
return this.servletConfig;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
if (namespace != null) {
setDisplayName("WebApplicationContext for namespace '" + namespace + "'");
}
}
public String getNamespace() {
return this.namespace;
}
@Override
public String[] getConfigLocations() {
return super.getConfigLocations();
}
@Override
public String getApplicationName() {
if (this.servletContext == null) {
return "";
}
if (this.servletContext.getMajorVersion() == 2 && this.servletContext.getMinorVersion() < 5) {
String name = this.servletContext.getServletContextName();
return (name != null ? name : "");
}
else {
// Servlet 2.5 available
return this.servletContext.getContextPath();
}
}
/**
* 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() {
return new StandardServletEnvironment();
}
/**
* Register request/session scopes, a {@link ServletContextAwareProcessor}, etc.
*/
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}
/**
* This implementation supports file paths beneath the root of the ServletContext.
* @see ServletContextResource
*/
@Override
protected Resource getResourceByPath(String path) {
return new ServletContextResource(this.servletContext, path);
}
/**
* This implementation supports pattern matching in unexpanded WARs too.
* @see ServletContextResourcePatternResolver
*/
@Override
protected ResourcePatternResolver getResourcePatternResolver() {
return new ServletContextResourcePatternResolver(this);
}
/**
* Initialize the theme capability.
*/
@Override
protected void onRefresh() {
this.themeSource = UiApplicationContextUtils.initThemeSource(this);
}
/**
* {@inheritDoc}
* <p>Replace {@code Servlet}-related property sources.
*/
@Override
protected void initPropertySources() {
super.initPropertySources();
ConfigurableEnvironment env = this.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment)env).initPropertySources(
this.servletContext, this.servletConfig);
}
}
public Theme getTheme(String themeName) {
return this.themeSource.getTheme(themeName);
}
}
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ScopeMetadataResolver;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.support.XmlWebApplicationContext;
/**
* {@link org.springframework.web.context.WebApplicationContext WebApplicationContext}
* implementation which accepts annotated classes as input - in particular
* {@link org.springframework.context.annotation.Configuration @Configuration}-annotated
* classes, but also plain {@link org.springframework.stereotype.Component @Component}
* classes and JSR-330 compliant classes using {@code javax.inject} annotations. Allows
* for registering classes one by one (specifying class names as config location) as well
* as for classpath scanning (specifying base packages as config location).
*
* <p>This is essentially the equivalent of
* {@link org.springframework.context.annotation.AnnotationConfigApplicationContext
* AnnotationConfigApplicationContext} for a web environment.
*
* <p>To make use of this application context, the
* {@linkplain ContextLoader#CONTEXT_CLASS_PARAM "contextClass"} context-param for
* ContextLoader and/or "contextClass" init-param for FrameworkServlet must be set to
* the fully-qualified name of this class.
*
* <p>As of Spring 3.1, this class may also be directly instantiated and injected into
* Spring's {@code DispatcherServlet} or {@code ContextLoaderListener} when using the
* new {@link org.springframework.web.WebApplicationInitializer WebApplicationInitializer}
* code-based alternative to {@code web.xml}. See its Javadoc for details and usage examples.
*
* <p>Unlike {@link XmlWebApplicationContext}, no default configuration class locations
* are assumed. Rather, it is a requirement to set the
* {@linkplain ContextLoader#CONFIG_LOCATION_PARAM "contextConfigLocation"}
* context-param for {@link ContextLoader} and/or "contextConfigLocation" init-param for
* FrameworkServlet. The param-value may contain both fully-qualified
* class names and base packages to scan for components. See {@link #loadBeanDefinitions}
* for exact details on how these locations are processed.
*
* <p>As an alternative to setting the "contextConfigLocation" parameter, users may
* implement an {@link org.springframework.context.ApplicationContextInitializer
* ApplicationContextInitializer} and set the
* {@linkplain ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM "contextInitializerClasses"}
* context-param / init-param. In such cases, users should favor the {@link #refresh()}
* and {@link #scan(String...)} methods over the {@link #setConfigLocation(String)}
* method, which is primarily for use by {@code ContextLoader}
*
* <p>Note: In case of multiple {@code @Configuration} classes, later {@code @Bean}
* definitions will override ones defined in earlier loaded files. This can be leveraged
* to deliberately override certain bean definitions via an extra Configuration class.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.0
* @see org.springframework.context.annotation.AnnotationConfigApplicationContext
*/
public class InheritableAnnotationConfigWebApplicationContext extends InheritableAbstractRefreshableWebApplicationContext {
private Class<?>[] annotatedClasses;
private String[] basePackages;
private BeanNameGenerator beanNameGenerator;
private ScopeMetadataResolver scopeMetadataResolver;
public InheritableAnnotationConfigWebApplicationContext() {
super();
}
public InheritableAnnotationConfigWebApplicationContext(ApplicationContext parent) {
super(parent);
}
/**
* {@inheritDoc}
* <p>This implementation accepts delimited values in the form of fully-qualified
* class names, (typically of {@code Configuration} classes) or fully-qualified
* packages to scan for annotated classes. During {@link #loadBeanDefinitions}, these
* locations will be processed in their given order, first attempting to load each
* value as a class. If class loading fails (i.e. a {@code ClassNotFoundException}
* occurs), the value is assumed to be a package and scanning is attempted.
* <p>Note that this method exists primarily for compatibility with Spring's
* {@link org.springframework.web.context.ContextLoader} and that if this application
* context is being configured through an
* {@link org.springframework.context.ApplicationContextInitializer}, use of the
* {@link #register} and {@link #scan} methods are preferred.
* @see #register(Class...)
* @see #scan(String...)
* @see #setConfigLocations(String[])
* @see #loadBeanDefinitions(DefaultListableBeanFactory)
*/
@Override
public void setConfigLocation(String location) {
super.setConfigLocation(location);
}
/**
* {@inheritDoc}
* <p>This implementation accepts individual location values as fully-qualified class
* names (typically {@code @Configuration} classes) or fully-qualified packages to
* scan. During {@link #loadBeanDefinitions}, these locations will be processed in
* order, first attempting to load values as a class, and upon class loading failure
* the value is assumed to be a package to be scanned.
* <p>Note that this method exists primarily for compatibility with Spring's
* {@link org.springframework.web.context.ContextLoader} and that if this application
* context is being configured through an
* {@link org.springframework.context.ApplicationContextInitializer}, use of the
* {@link #register} and {@link #scan} methods are preferred.
* @see #scan(String...)
* @see #register(Class...)
* @see #setConfigLocation(String)
* @see #loadBeanDefinitions(DefaultListableBeanFactory)
*/
@Override
public void setConfigLocations(String[] locations) {
super.setConfigLocations(locations);
}
/**
* Register one or more annotated classes to be processed.
* Note that {@link #refresh()} must be called in order for the context to fully
* process the new class.
* <p>Calls to {@link #register} are idempotent; adding the same
* annotated class more than once has no additional effect.
* @param annotatedClasses one or more annotated classes,
* e.g. {@link org.springframework.context.annotation.Configuration @Configuration} classes
* @see #scan(String...)
* @see #loadBeanDefinitions(DefaultListableBeanFactory)
* @see #setConfigLocation(String)
* @see #refresh()
*/
public void register(Class<?>... annotatedClasses) {
Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
this.annotatedClasses = annotatedClasses;
}
/**
* Perform a scan within the specified base packages.
* Note that {@link #refresh()} must be called in order for the context to
* fully process the new class.
* @param basePackages the packages to check for annotated classes
* @see #loadBeanDefinitions(DefaultListableBeanFactory)
* @see #register(Class...)
* @see #setConfigLocation(String)
* @see #refresh()
*/
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
this.basePackages = basePackages;
}
/**
* Register a {@link org.springframework.beans.factory.config.BeanDefinition} for
* any classes specified by {@link #register(Class...)} and scan any packages
* specified by {@link #scan(String...)}.
* <p>For any values specified by {@link #setConfigLocation(String)} or
* {@link #setConfigLocations(String[])}, attempt first to load each location as a
* class, registering a {@code BeanDefinition} if class loading is successful,
* and if class loading fails (i.e. a {@code ClassNotFoundException} is raised),
* assume the value is a package and attempt to scan it for annotated classes.
* <p>Enables the default set of annotation configuration post processors, such that
* {@code @Autowired}, {@code @Required}, and associated annotations can be used.
* <p>Configuration class bean definitions are registered with generated bean
* definition names unless the {@code value} attribute is provided to the stereotype
* annotation.
* @see #register(Class...)
* @see #scan(String...)
* @see #setConfigLocation(String)
* @see #setConfigLocations(String[])
* @see AnnotatedBeanDefinitionReader
* @see ClassPathBeanDefinitionScanner
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(beanFactory);
reader.setEnvironment(this.getEnvironment());
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.setEnvironment(this.getEnvironment());
BeanNameGenerator beanNameGenerator = getBeanNameGenerator();
ScopeMetadataResolver scopeMetadataResolver = getScopeMetadataResolver();
if (beanNameGenerator != null) {
reader.setBeanNameGenerator(beanNameGenerator);
scanner.setBeanNameGenerator(beanNameGenerator);
beanFactory.registerSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
}
if (scopeMetadataResolver != null) {
reader.setScopeMetadataResolver(scopeMetadataResolver);
scanner.setScopeMetadataResolver(scopeMetadataResolver);
}
if (!ObjectUtils.isEmpty(this.annotatedClasses)) {
if (logger.isInfoEnabled()) {
logger.info("Registering annotated classes: [" +
StringUtils.arrayToCommaDelimitedString(this.annotatedClasses) + "]");
}
reader.register(this.annotatedClasses);
}
if (!ObjectUtils.isEmpty(this.basePackages)) {
if (logger.isInfoEnabled()) {
logger.info("Scanning base packages: [" +
StringUtils.arrayToCommaDelimitedString(this.basePackages) + "]");
}
scanner.scan(this.basePackages);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
try {
Class<?> clazz = getClassLoader().loadClass(configLocation);
if (logger.isInfoEnabled()) {
logger.info("Successfully resolved class for [" + configLocation + "]");
}
reader.register(clazz);
}
catch (ClassNotFoundException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not load class for config location [" + configLocation +
"] - trying package scan. " + ex);
}
int count = scanner.scan(configLocation);
if (logger.isInfoEnabled()) {
if (count == 0) {
logger.info("No annotated classes found for specified class/package [" + configLocation + "]");
}
else {
logger.info("Found " + count + " annotated classes in package [" + configLocation + "]");
}
}
}
}
}
}
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
this.beanNameGenerator = beanNameGenerator;
}
/**
* Provide a custom {@link BeanNameGenerator} for use with {@link AnnotatedBeanDefinitionReader}
* and/or {@link ClassPathBeanDefinitionScanner}, if any.
* <p>Default is {@link org.springframework.context.annotation.AnnotationBeanNameGenerator}.
* @see AnnotatedBeanDefinitionReader#setBeanNameGenerator
* @see ClassPathBeanDefinitionScanner#setBeanNameGenerator
*/
protected BeanNameGenerator getBeanNameGenerator() {
return this.beanNameGenerator;
}
/**
* Set the {@link ScopeMetadataResolver} to use for detected bean classes.
* <p>The default is an {@link org.springframework.context.annotation.AnnotationScopeMetadataResolver}.
*/
public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
this.scopeMetadataResolver = scopeMetadataResolver;
}
/**
* Provide a custom {@link ScopeMetadataResolver} for use with {@link AnnotatedBeanDefinitionReader}
* and/or {@link ClassPathBeanDefinitionScanner}, if any.
* <p>Default is {@link org.springframework.context.annotation.AnnotationScopeMetadataResolver}.
* @see AnnotatedBeanDefinitionReader#setScopeMetadataResolver
* @see ClassPathBeanDefinitionScanner#setScopeMetadataResolver
*/
protected ScopeMetadataResolver getScopeMetadataResolver() {
return this.scopeMetadataResolver;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment