Drop outdated BeanFactoryLocator / beanRefContext.xml mechanism

Issue: SPR-15154
This commit is contained in:
Juergen Hoeller
2017-01-17 13:58:37 +01:00
parent d96738d613
commit ac6aa53031
30 changed files with 74 additions and 2388 deletions

View File

@@ -1,68 +0,0 @@
/*
* Copyright 2002-2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.access;
import org.springframework.beans.BeansException;
/**
* Defines a contract for the lookup, use, and release of a
* {@link org.springframework.beans.factory.BeanFactory},
* or a {@code BeanFactory} subclass such as an
* {@link org.springframework.context.ApplicationContext}.
*
* <p>Where this interface is implemented as a singleton class such as
* {@link SingletonBeanFactoryLocator}, the Spring team <strong>strongly</strong>
* suggests that it be used sparingly and with caution. By far the vast majority
* of the code inside an application is best written in a Dependency Injection
* style, where that code is served out of a
* {@code BeanFactory}/{@code ApplicationContext} container, and has
* its own dependencies supplied by the container when it is created. However,
* even such a singleton implementation sometimes has its use in the small glue
* layers of code that is sometimes needed to tie other code together. For
* example, third party code may try to construct new objects directly, without
* the ability to force it to get these objects out of a {@code BeanFactory}.
* If the object constructed by the third party code is just a small stub or
* proxy, which then uses an implementation of this class to get a
* {@code BeanFactory} from which it gets the real object, to which it
* delegates, then proper Dependency Injection has been achieved.
*
* <p>As another example, in a complex Java EE app with multiple layers, with
* each layer having its own {@code ApplicationContext} definition (in a
* hierarchy), a class like {@code SingletonBeanFactoryLocator} may be used
* to demand load these contexts.
*
* @author Colin Sampaleanu
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.context.access.DefaultLocatorFactory
* @see org.springframework.context.ApplicationContext
*/
public interface BeanFactoryLocator {
/**
* Use the {@link org.springframework.beans.factory.BeanFactory} (or derived
* interface such as {@link org.springframework.context.ApplicationContext})
* specified by the {@code factoryKey} parameter.
* <p>The definition is possibly loaded/created as needed.
* @param factoryKey a resource name specifying which {@code BeanFactory} the
* {@code BeanFactoryLocator} must return for usage. The actual meaning of the
* resource name is specific to the implementation of {@code BeanFactoryLocator}.
* @return the {@code BeanFactory} instance, wrapped as a {@link BeanFactoryReference} object
* @throws BeansException if there is an error loading or accessing the {@code BeanFactory}
*/
BeanFactoryReference useBeanFactory(String factoryKey) throws BeansException;
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.access;
import org.springframework.beans.factory.BeanFactory;
/**
* Used to track a reference to a {@link BeanFactory} obtained through
* a {@link BeanFactoryLocator}.
*
* <p>It is safe to call {@link #release()} multiple times, but
* {@link #getFactory()} must not be called after calling release.
*
* @author Colin Sampaleanu
* @see BeanFactoryLocator
* @see org.springframework.context.access.ContextBeanFactoryReference
*/
public interface BeanFactoryReference {
/**
* Return the {@link BeanFactory} instance held by this reference.
* @throws IllegalStateException if invoked after {@code release()} has been called
*/
BeanFactory getFactory();
/**
* Indicate that the {@link BeanFactory} instance referred to by this object is not
* needed any longer by the client code which obtained the {@link BeanFactoryReference}.
* <p>Depending on the actual implementation of {@link BeanFactoryLocator}, and
* the actual type of {@code BeanFactory}, this may possibly not actually
* do anything; alternately in the case of a 'closeable' {@code BeanFactory}
* or derived class (such as {@link org.springframework.context.ApplicationContext})
* may 'close' it, or may 'close' it once no more references remain.
* <p>In an EJB usage scenario this would normally be called from
* {@code ejbRemove()} and {@code ejbPassivate()}.
* <p>This is safe to call multiple times.
* @see BeanFactoryLocator
* @see org.springframework.context.access.ContextBeanFactoryReference
* @see org.springframework.context.ConfigurableApplicationContext#close()
*/
void release();
}

View File

@@ -1,48 +0,0 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.access;
import org.springframework.beans.FatalBeanException;
/**
* Exception thrown if a bean factory could not be loaded by a bootstrap class.
*
* @author Rod Johnson
* @since 02.12.2002
*/
@SuppressWarnings("serial")
public class BootstrapException extends FatalBeanException {
/**
* Create a new BootstrapException with the specified message.
* @param msg the detail message
*/
public BootstrapException(String msg) {
super(msg);
}
/**
* Create a new BootstrapException with the specified message
* and root cause.
* @param msg the detail message
* @param cause the root cause
*/
public BootstrapException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,536 +0,0 @@
/*
* Copyright 2002-2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.access;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
/**
* <p>Keyed-singleton implementation of {@link BeanFactoryLocator},
* which accesses shared Spring {@link BeanFactory} instances.</p>
*
* <p>Please see the warning in BeanFactoryLocator's javadoc about appropriate usage
* of singleton style BeanFactoryLocator implementations. It is the opinion of the
* Spring team that the use of this class and similar classes is unnecessary except
* (sometimes) for a small amount of glue code. Excessive usage will lead to code
* that is more tightly coupled, and harder to modify or test.</p>
*
* <p>In this implementation, a BeanFactory is built up from one or more XML
* definition file fragments, accessed as resources. The default resource name
* searched for is 'classpath*:beanRefFactory.xml', with the Spring-standard
* 'classpath*:' prefix ensuring that if the classpath contains multiple copies
* of this file (perhaps one in each component jar) they will be combined. To
* override the default resource name, instead of using the no-arg
* {@link #getInstance()} method, use the {@link #getInstance(String selector)}
* variant, which will treat the 'selector' argument as the resource name to
* search for.</p>
*
* <p>The purpose of this 'outer' BeanFactory is to create and hold a copy of one
* or more 'inner' BeanFactory or ApplicationContext instances, and allow those
* to be obtained either directly or via an alias. As such, this class provides
* both singleton style access to one or more BeanFactories/ApplicationContexts,
* and also a level of indirection, allowing multiple pieces of code, which are
* not able to work in a Dependency Injection fashion, to refer to and use the
* same target BeanFactory/ApplicationContext instance(s), by different names.<p>
*
* <p>Consider an example application scenario:
*
* <ul>
* <li>{@code com.mycompany.myapp.util.applicationContext.xml} -
* ApplicationContext definition file which defines beans for 'util' layer.
* <li>{@code com.mycompany.myapp.dataaccess-applicationContext.xml} -
* ApplicationContext definition file which defines beans for 'data access' layer.
* Depends on the above.
* <li>{@code com.mycompany.myapp.services.applicationContext.xml} -
* ApplicationContext definition file which defines beans for 'services' layer.
* Depends on the above.
* </ul>
*
* <p>In an ideal scenario, these would be combined to create one ApplicationContext,
* or created as three hierarchical ApplicationContexts, by one piece of code
* somewhere at application startup (perhaps a Servlet filter), from which all other
* code in the application would flow, obtained as beans from the context(s). However
* when third party code enters into the picture, things can get problematic. If the
* third party code needs to create user classes, which should normally be obtained
* from a Spring BeanFactory/ApplicationContext, but can handle only newInstance()
* style object creation, then some extra work is required to actually access and
* use object from a BeanFactory/ApplicationContext. One solutions is to make the
* class created by the third party code be just a stub or proxy, which gets the
* real object from a BeanFactory/ApplicationContext, and delegates to it. However,
* it is not normally workable for the stub to create the BeanFactory on each
* use, as depending on what is inside it, that can be an expensive operation.
* Additionally, there is a fairly tight coupling between the stub and the name of
* the definition resource for the BeanFactory/ApplicationContext. This is where
* SingletonBeanFactoryLocator comes in. The stub can obtain a
* SingletonBeanFactoryLocator instance, which is effectively a singleton, and
* ask it for an appropriate BeanFactory. A subsequent invocation (assuming the
* same class loader is involved) by the stub or another piece of code, will obtain
* the same instance. The simple aliasing mechanism allows the context to be asked
* for by a name which is appropriate for (or describes) the user. The deployer can
* match alias names to actual context names.
*
* <p>Another use of SingletonBeanFactoryLocator, is to demand-load/use one or more
* BeanFactories/ApplicationContexts. Because the definition can contain one of more
* BeanFactories/ApplicationContexts, which can be independent or in a hierarchy, if
* they are set to lazy-initialize, they will only be created when actually requested
* for use.
*
* <p>Given the above-mentioned three ApplicationContexts, consider the simplest
* SingletonBeanFactoryLocator usage scenario, where there is only one single
* {@code beanRefFactory.xml} definition file:
*
* <pre class="code">&lt;?xml version="1.0" encoding="UTF-8"?>
* &lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
*
* &lt;beans>
*
* &lt;bean id="com.mycompany.myapp"
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
* &lt;constructor-arg>
* &lt;list>
* &lt;value>com/mycompany/myapp/util/applicationContext.xml&lt;/value>
* &lt;value>com/mycompany/myapp/dataaccess/applicationContext.xml&lt;/value>
* &lt;value>com/mycompany/myapp/dataaccess/services.xml&lt;/value>
* &lt;/list>
* &lt;/constructor-arg>
* &lt;/bean>
*
* &lt;/beans>
* </pre>
*
* The client code is as simple as:
*
* <pre class="code">
* BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance();
* BeanFactoryReference bf = bfl.useBeanFactory("com.mycompany.myapp");
* // now use some bean from factory
* MyClass zed = bf.getFactory().getBean("mybean");
* </pre>
*
* Another relatively simple variation of the {@code beanRefFactory.xml} definition file could be:
*
* <pre class="code">&lt;?xml version="1.0" encoding="UTF-8"?>
* &lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
*
* &lt;beans>
*
* &lt;bean id="com.mycompany.myapp.util" lazy-init="true"
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
* &lt;constructor-arg>
* &lt;value>com/mycompany/myapp/util/applicationContext.xml&lt;/value>
* &lt;/constructor-arg>
* &lt;/bean>
*
* &lt;!-- child of above -->
* &lt;bean id="com.mycompany.myapp.dataaccess" lazy-init="true"
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
* &lt;constructor-arg>
* &lt;list>&lt;value>com/mycompany/myapp/dataaccess/applicationContext.xml&lt;/value>&lt;/list>
* &lt;/constructor-arg>
* &lt;constructor-arg>
* &lt;ref bean="com.mycompany.myapp.util"/>
* &lt;/constructor-arg>
* &lt;/bean>
*
* &lt;!-- child of above -->
* &lt;bean id="com.mycompany.myapp.services" lazy-init="true"
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
* &lt;constructor-arg>
* &lt;list>&lt;value>com/mycompany/myapp/dataaccess.services.xml&lt;/value>&lt;/value>
* &lt;/constructor-arg>
* &lt;constructor-arg>
* &lt;ref bean="com.mycompany.myapp.dataaccess"/>
* &lt;/constructor-arg>
* &lt;/bean>
*
* &lt;!-- define an alias -->
* &lt;bean id="com.mycompany.myapp.mypackage"
* class="java.lang.String">
* &lt;constructor-arg>
* &lt;value>com.mycompany.myapp.services&lt;/value>
* &lt;/constructor-arg>
* &lt;/bean>
*
* &lt;/beans>
* </pre>
*
* <p>In this example, there is a hierarchy of three contexts created. The (potential)
* advantage is that if the lazy flag is set to true, a context will only be created
* if it's actually used. If there is some code that is only needed some of the time,
* this mechanism can save some resources. Additionally, an alias to the last context
* has been created. Aliases allow usage of the idiom where client code asks for a
* context with an id which represents the package or module the code is in, and the
* actual definition file(s) for the SingletonBeanFactoryLocator maps that id to
* a real context id.
*
* <p>A final example is more complex, with a {@code beanRefFactory.xml} for every module.
* All the files are automatically combined to create the final definition.
*
* <p>{@code beanRefFactory.xml} file inside jar for util module:
*
* <pre class="code">&lt;?xml version="1.0" encoding="UTF-8"?>
* &lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
*
* &lt;beans>
* &lt;bean id="com.mycompany.myapp.util" lazy-init="true"
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
* &lt;constructor-arg>
* &lt;value>com/mycompany/myapp/util/applicationContext.xml&lt;/value>
* &lt;/constructor-arg>
* &lt;/bean>
* &lt;/beans>
* </pre>
*
* {@code beanRefFactory.xml} file inside jar for data-access module:<br>
*
* <pre class="code">&lt;?xml version="1.0" encoding="UTF-8"?>
* &lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
*
* &lt;beans>
* &lt;!-- child of util -->
* &lt;bean id="com.mycompany.myapp.dataaccess" lazy-init="true"
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
* &lt;constructor-arg>
* &lt;list>&lt;value>com/mycompany/myapp/dataaccess/applicationContext.xml&lt;/value>&lt;/list>
* &lt;/constructor-arg>
* &lt;constructor-arg>
* &lt;ref bean="com.mycompany.myapp.util"/>
* &lt;/constructor-arg>
* &lt;/bean>
* &lt;/beans>
* </pre>
*
* {@code beanRefFactory.xml} file inside jar for services module:
*
* <pre class="code">&lt;?xml version="1.0" encoding="UTF-8"?>
* &lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
*
* &lt;beans>
* &lt;!-- child of data-access -->
* &lt;bean id="com.mycompany.myapp.services" lazy-init="true"
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
* &lt;constructor-arg>
* &lt;list>&lt;value>com/mycompany/myapp/dataaccess/services.xml&lt;/value>&lt;/list>
* &lt;/constructor-arg>
* &lt;constructor-arg>
* &lt;ref bean="com.mycompany.myapp.dataaccess"/>
* &lt;/constructor-arg>
* &lt;/bean>
* &lt;/beans>
* </pre>
*
* {@code beanRefFactory.xml} file inside jar for mypackage module. This doesn't
* create any of its own contexts, but allows the other ones to be referred to be
* a name known to this module:
*
* <pre class="code">&lt;?xml version="1.0" encoding="UTF-8"?>
* &lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
*
* &lt;beans>
* &lt;!-- define an alias for "com.mycompany.myapp.services" -->
* &lt;alias name="com.mycompany.myapp.services" alias="com.mycompany.myapp.mypackage"/&gt;
* &lt;/beans>
* </pre>
*
* @author Colin Sampaleanu
* @author Juergen Hoeller
* @see org.springframework.context.access.ContextSingletonBeanFactoryLocator
* @see org.springframework.context.access.DefaultLocatorFactory
*/
public class SingletonBeanFactoryLocator implements BeanFactoryLocator {
private static final String DEFAULT_RESOURCE_LOCATION = "classpath*:beanRefFactory.xml";
protected static final Log logger = LogFactory.getLog(SingletonBeanFactoryLocator.class);
/** The keyed BeanFactory instances */
private static final Map<String, BeanFactoryLocator> instances = new HashMap<>();
/**
* Returns an instance which uses the default "classpath*:beanRefFactory.xml",
* as the name of the definition file(s). All resources returned by calling the
* current thread context ClassLoader's {@code getResources} method with
* this name will be combined to create a BeanFactory definition set.
* @return the corresponding BeanFactoryLocator instance
* @throws BeansException in case of factory loading failure
*/
public static BeanFactoryLocator getInstance() throws BeansException {
return getInstance(null);
}
/**
* Returns an instance which uses the specified selector, as the name of the
* definition file(s). In the case of a name with a Spring 'classpath*:' prefix,
* or with no prefix, which is treated the same, the current thread context
* ClassLoader's {@code getResources} method will be called with this value
* to get all resources having that name. These resources will then be combined to
* form a definition. In the case where the name uses a Spring 'classpath:' prefix,
* or a standard URL prefix, then only one resource file will be loaded as the
* definition.
* @param selector the name of the resource(s) which will be read and
* combined to form the definition for the BeanFactoryLocator instance.
* Any such files must form a valid BeanFactory definition.
* @return the corresponding BeanFactoryLocator instance
* @throws BeansException in case of factory loading failure
*/
public static BeanFactoryLocator getInstance(String selector) throws BeansException {
String resourceLocation = selector;
if (resourceLocation == null) {
resourceLocation = DEFAULT_RESOURCE_LOCATION;
}
// For backwards compatibility, we prepend 'classpath*:' to the selector name if there
// is no other prefix (i.e. classpath*:, classpath:, or some URL prefix.
if (!ResourcePatternUtils.isUrl(resourceLocation)) {
resourceLocation = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resourceLocation;
}
synchronized (instances) {
if (logger.isTraceEnabled()) {
logger.trace("SingletonBeanFactoryLocator.getInstance(): instances.hashCode=" +
instances.hashCode() + ", instances=" + instances);
}
BeanFactoryLocator bfl = instances.get(resourceLocation);
if (bfl == null) {
bfl = new SingletonBeanFactoryLocator(resourceLocation);
instances.put(resourceLocation, bfl);
}
return bfl;
}
}
// We map BeanFactoryGroup objects by String keys, and by the definition object.
private final Map<String, BeanFactoryGroup> bfgInstancesByKey = new HashMap<>();
private final Map<BeanFactory, BeanFactoryGroup> bfgInstancesByObj = new HashMap<>();
private final String resourceLocation;
/**
* Constructor which uses the specified name as the resource name
* of the definition file(s).
* @param resourceLocation the Spring resource location to use
* (either a URL or a "classpath:" / "classpath*:" pseudo URL)
*/
protected SingletonBeanFactoryLocator(String resourceLocation) {
this.resourceLocation = resourceLocation;
}
@Override
public BeanFactoryReference useBeanFactory(String factoryKey) throws BeansException {
synchronized (this.bfgInstancesByKey) {
BeanFactoryGroup bfg = this.bfgInstancesByKey.get(this.resourceLocation);
if (bfg != null) {
bfg.refCount++;
}
else {
// This group definition doesn't exist, we need to try to load it.
if (logger.isTraceEnabled()) {
logger.trace("Factory group with resource name [" + this.resourceLocation +
"] requested. Creating new instance.");
}
// Create the BeanFactory but don't initialize it.
BeanFactory groupContext = createDefinition(this.resourceLocation, factoryKey);
// Record its existence now, before instantiating any singletons.
bfg = new BeanFactoryGroup();
bfg.definition = groupContext;
bfg.refCount = 1;
this.bfgInstancesByKey.put(this.resourceLocation, bfg);
this.bfgInstancesByObj.put(groupContext, bfg);
// Now initialize the BeanFactory. This may cause a re-entrant invocation
// of this method, but since we've already added the BeanFactory to our
// mappings, the next time it will be found and simply have its
// reference count incremented.
try {
initializeDefinition(groupContext);
}
catch (BeansException ex) {
this.bfgInstancesByKey.remove(this.resourceLocation);
this.bfgInstancesByObj.remove(groupContext);
throw new BootstrapException("Unable to initialize group definition. " +
"Group resource name [" + this.resourceLocation + "], factory key [" + factoryKey + "]", ex);
}
}
try {
BeanFactory beanFactory;
if (factoryKey != null) {
beanFactory = bfg.definition.getBean(factoryKey, BeanFactory.class);
}
else {
beanFactory = bfg.definition.getBean(BeanFactory.class);
}
return new CountingBeanFactoryReference(beanFactory, bfg.definition);
}
catch (BeansException ex) {
throw new BootstrapException("Unable to return specified BeanFactory instance: factory key [" +
factoryKey + "], from group with resource name [" + this.resourceLocation + "]", ex);
}
}
}
/**
* Actually creates definition in the form of a BeanFactory, given a resource name
* which supports standard Spring resource prefixes ('classpath:', 'classpath*:', etc.)
* This is split out as a separate method so that subclasses can override the actual
* type used (to be an ApplicationContext, for example).
* <p>The default implementation simply builds a
* {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
* and populates it using an
* {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}.
* <p>This method should not instantiate any singletons. That function is performed
* by {@link #initializeDefinition initializeDefinition()}, which should also be
* overridden if this method is.
* @param resourceLocation the resource location for this factory group
* @param factoryKey the bean name of the factory to obtain
* @return the corresponding BeanFactory reference
*/
protected BeanFactory createDefinition(String resourceLocation, String factoryKey) {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
try {
Resource[] configResources = resourcePatternResolver.getResources(resourceLocation);
if (configResources.length == 0) {
throw new FatalBeanException("Unable to find resource for specified definition. " +
"Group resource name [" + this.resourceLocation + "], factory key [" + factoryKey + "]");
}
reader.loadBeanDefinitions(configResources);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Error accessing bean definition resource [" + this.resourceLocation + "]", ex);
}
catch (BeanDefinitionStoreException ex) {
throw new FatalBeanException("Unable to load group definition: " +
"group resource name [" + this.resourceLocation + "], factory key [" + factoryKey + "]", ex);
}
return factory;
}
/**
* Instantiate singletons and do any other normal initialization of the factory.
* Subclasses that override {@link #createDefinition createDefinition()} should
* also override this method.
* @param groupDef the factory returned by {@link #createDefinition createDefinition()}
*/
protected void initializeDefinition(BeanFactory groupDef) {
if (groupDef instanceof ConfigurableListableBeanFactory) {
((ConfigurableListableBeanFactory) groupDef).preInstantiateSingletons();
}
}
/**
* Destroy definition in separate method so subclass may work with other definition types.
* @param groupDef the factory returned by {@link #createDefinition createDefinition()}
* @param selector the resource location for this factory group
*/
protected void destroyDefinition(BeanFactory groupDef, String selector) {
if (groupDef instanceof ConfigurableBeanFactory) {
if (logger.isTraceEnabled()) {
logger.trace("Factory group with selector '" + selector +
"' being released, as there are no more references to it");
}
((ConfigurableBeanFactory) groupDef).destroySingletons();
}
}
/**
* We track BeanFactory instances with this class.
*/
private static class BeanFactoryGroup {
private BeanFactory definition;
private int refCount = 0;
}
/**
* BeanFactoryReference implementation for this locator.
*/
private class CountingBeanFactoryReference implements BeanFactoryReference {
private BeanFactory beanFactory;
private BeanFactory groupContextRef;
public CountingBeanFactoryReference(BeanFactory beanFactory, BeanFactory groupContext) {
this.beanFactory = beanFactory;
this.groupContextRef = groupContext;
}
@Override
public BeanFactory getFactory() {
return this.beanFactory;
}
// Note that it's legal to call release more than once!
@Override
public void release() throws FatalBeanException {
synchronized (bfgInstancesByKey) {
BeanFactory savedRef = this.groupContextRef;
if (savedRef != null) {
this.groupContextRef = null;
BeanFactoryGroup bfg = bfgInstancesByObj.get(savedRef);
if (bfg != null) {
bfg.refCount--;
if (bfg.refCount == 0) {
destroyDefinition(savedRef, resourceLocation);
bfgInstancesByKey.remove(resourceLocation);
bfgInstancesByObj.remove(savedRef);
}
}
else {
// This should be impossible.
logger.warn("Tried to release a SingletonBeanFactoryLocator group definition " +
"more times than it has actually been used. Resource name [" + resourceLocation + "]");
}
}
}
}
}
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright 2002-2008 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.access.el;
import javax.el.ELContext;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.util.Assert;
/**
* Simple concrete variant of {@link SpringBeanELResolver}, delegating
* to a given {@link BeanFactory} that the resolver was constructed with.
*
* @author Juergen Hoeller
* @since 2.5.2
*/
public class SimpleSpringBeanELResolver extends SpringBeanELResolver {
private final BeanFactory beanFactory;
/**
* Create a new SimpleSpringBeanELResolver for the given BeanFactory.
* @param beanFactory the Spring BeanFactory to delegate to
*/
public SimpleSpringBeanELResolver(BeanFactory beanFactory) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
this.beanFactory = beanFactory;
}
@Override
protected BeanFactory getBeanFactory(ELContext elContext) {
return this.beanFactory;
}
}

View File

@@ -1,122 +0,0 @@
/*
* Copyright 2002-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.access.el;
import java.beans.FeatureDescriptor;
import java.util.Iterator;
import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ELResolver;
import javax.el.PropertyNotWritableException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
/**
* Unified EL {@code ELResolver} that delegates to a Spring BeanFactory,
* resolving name references to Spring-defined beans.
*
* @author Juergen Hoeller
* @since 2.5.2
* @see org.springframework.web.jsf.el.SpringBeanFacesELResolver
*/
public abstract class SpringBeanELResolver extends ELResolver {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
@Override
public Object getValue(ELContext elContext, Object base, Object property) throws ELException {
if (base == null) {
String beanName = property.toString();
BeanFactory bf = getBeanFactory(elContext);
if (bf.containsBean(beanName)) {
if (logger.isTraceEnabled()) {
logger.trace("Successfully resolved variable '" + beanName + "' in Spring BeanFactory");
}
elContext.setPropertyResolved(true);
return bf.getBean(beanName);
}
}
return null;
}
@Override
public Class<?> getType(ELContext elContext, Object base, Object property) throws ELException {
if (base == null) {
String beanName = property.toString();
BeanFactory bf = getBeanFactory(elContext);
if (bf.containsBean(beanName)) {
elContext.setPropertyResolved(true);
return bf.getType(beanName);
}
}
return null;
}
@Override
public void setValue(ELContext elContext, Object base, Object property, Object value) throws ELException {
if (base == null) {
String beanName = property.toString();
BeanFactory bf = getBeanFactory(elContext);
if (bf.containsBean(beanName)) {
if (value == bf.getBean(beanName)) {
// Setting the bean reference to the same value is alright - can simply be ignored...
elContext.setPropertyResolved(true);
}
else {
throw new PropertyNotWritableException(
"Variable '" + beanName + "' refers to a Spring bean which by definition is not writable");
}
}
}
}
@Override
public boolean isReadOnly(ELContext elContext, Object base, Object property) throws ELException {
if (base == null) {
String beanName = property.toString();
BeanFactory bf = getBeanFactory(elContext);
if (bf.containsBean(beanName)) {
return true;
}
}
return false;
}
@Override
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext elContext, Object base) {
return null;
}
@Override
public Class<?> getCommonPropertyType(ELContext elContext, Object base) {
return Object.class;
}
/**
* Retrieve the Spring BeanFactory to delegate bean name resolution to.
* @param elContext the current ELContext
* @return the Spring BeanFactory (never {@code null})
*/
protected abstract BeanFactory getBeanFactory(ELContext elContext);
}

View File

@@ -1,4 +0,0 @@
/**
* Support classes for accessing a Spring BeanFactory from Unified EL.
*/
package org.springframework.beans.factory.access.el;

View File

@@ -1,8 +0,0 @@
/**
* Helper infrastructure to locate and access bean factories.
*
* <p><b>Note: This package is only relevant for special sharing of bean
* factories, for example behind EJB facades. It is <i>not</i> used in a
* typical web application or standalone application.</b>
*/
package org.springframework.beans.factory.access;

View File

@@ -1,241 +0,0 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.access;
import java.util.List;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.util.ClassUtils;
import static org.junit.Assert.*;
/**
* Unit tests for {@link SingletonBeanFactoryLocator}.
*
* @author Colin Sampaleanu
* @author Chris Beams
*/
public class SingletonBeanFactoryLocatorTests {
private static final Class<?> CLASS = SingletonBeanFactoryLocatorTests.class;
private static final String REF1_XML = CLASS.getSimpleName() + "-ref1.xml";
@Test
public void testBasicFunctionality() {
SingletonBeanFactoryLocator facLoc = new SingletonBeanFactoryLocator(
"classpath*:" + ClassUtils.addResourcePathToPackagePath(CLASS, REF1_XML));
basicFunctionalityTest(facLoc);
}
/**
* Worker method so subclass can use it too.
*/
protected void basicFunctionalityTest(SingletonBeanFactoryLocator facLoc) {
BeanFactoryReference bfr = facLoc.useBeanFactory("a.qualified.name.of.some.sort");
BeanFactory fac = bfr.getFactory();
BeanFactoryReference bfr2 = facLoc.useBeanFactory("another.qualified.name");
fac = bfr2.getFactory();
// verify that the same instance is returned
TestBean tb = (TestBean) fac.getBean("beans1.bean1");
assertTrue(tb.getName().equals("beans1.bean1"));
tb.setName("was beans1.bean1");
BeanFactoryReference bfr3 = facLoc.useBeanFactory("another.qualified.name");
fac = bfr3.getFactory();
tb = (TestBean) fac.getBean("beans1.bean1");
assertTrue(tb.getName().equals("was beans1.bean1"));
BeanFactoryReference bfr4 = facLoc.useBeanFactory("a.qualified.name.which.is.an.alias");
fac = bfr4.getFactory();
tb = (TestBean) fac.getBean("beans1.bean1");
assertTrue(tb.getName().equals("was beans1.bean1"));
// Now verify that we can call release in any order.
// Unfortunately this doesn't validate complete release after the last one.
bfr2.release();
bfr3.release();
bfr.release();
bfr4.release();
}
/**
* This test can run multiple times, but due to static keyed lookup of the locators,
* 2nd and subsequent calls will actuall get back same locator instance. This is not
* an issue really, since the contained beanfactories will still be loaded and released.
*/
@Test
public void testGetInstance() {
// Try with and without 'classpath*:' prefix, and with 'classpath:' prefix.
BeanFactoryLocator facLoc = SingletonBeanFactoryLocator.getInstance(
ClassUtils.addResourcePathToPackagePath(CLASS, REF1_XML));
getInstanceTest1(facLoc);
facLoc = SingletonBeanFactoryLocator.getInstance(
"classpath*:/" + ClassUtils.addResourcePathToPackagePath(CLASS, REF1_XML));
getInstanceTest2(facLoc);
// This will actually get another locator instance, as the key is the resource name.
facLoc = SingletonBeanFactoryLocator.getInstance(
"classpath:" + ClassUtils.addResourcePathToPackagePath(CLASS, REF1_XML));
getInstanceTest3(facLoc);
}
/**
* Worker method so subclass can use it too
*/
protected void getInstanceTest1(BeanFactoryLocator facLoc) {
BeanFactoryReference bfr = facLoc.useBeanFactory("a.qualified.name.of.some.sort");
BeanFactory fac = bfr.getFactory();
BeanFactoryReference bfr2 = facLoc.useBeanFactory("another.qualified.name");
fac = bfr2.getFactory();
// verify that the same instance is returned
TestBean tb = (TestBean) fac.getBean("beans1.bean1");
assertTrue(tb.getName().equals("beans1.bean1"));
tb.setName("was beans1.bean1");
BeanFactoryReference bfr3 = facLoc.useBeanFactory("another.qualified.name");
fac = bfr3.getFactory();
tb = (TestBean) fac.getBean("beans1.bean1");
assertTrue(tb.getName().equals("was beans1.bean1"));
BeanFactoryReference bfr4 = facLoc.useBeanFactory("a.qualified.name.which.is.an.alias");
fac = bfr4.getFactory();
tb = (TestBean) fac.getBean("beans1.bean1");
assertTrue(tb.getName().equals("was beans1.bean1"));
bfr.release();
bfr3.release();
bfr2.release();
bfr4.release();
}
/**
* Worker method so subclass can use it too
*/
protected void getInstanceTest2(BeanFactoryLocator facLoc) {
BeanFactoryReference bfr;
BeanFactory fac;
BeanFactoryReference bfr2;
TestBean tb;
BeanFactoryReference bfr3;
BeanFactoryReference bfr4;
bfr = facLoc.useBeanFactory("a.qualified.name.of.some.sort");
fac = bfr.getFactory();
bfr2 = facLoc.useBeanFactory("another.qualified.name");
fac = bfr2.getFactory();
// verify that the same instance is returned
tb = (TestBean) fac.getBean("beans1.bean1");
assertTrue(tb.getName().equals("beans1.bean1"));
tb.setName("was beans1.bean1");
bfr3 = facLoc.useBeanFactory("another.qualified.name");
fac = bfr3.getFactory();
tb = (TestBean) fac.getBean("beans1.bean1");
assertTrue(tb.getName().equals("was beans1.bean1"));
bfr4 = facLoc.useBeanFactory("a.qualified.name.which.is.an.alias");
fac = bfr4.getFactory();
tb = (TestBean) fac.getBean("beans1.bean1");
assertTrue(tb.getName().equals("was beans1.bean1"));
bfr.release();
bfr2.release();
bfr4.release();
bfr3.release();
}
/**
* Worker method so subclass can use it too
*/
protected void getInstanceTest3(BeanFactoryLocator facLoc) {
BeanFactoryReference bfr;
BeanFactory fac;
BeanFactoryReference bfr2;
TestBean tb;
BeanFactoryReference bfr3;
BeanFactoryReference bfr4;
bfr = facLoc.useBeanFactory("a.qualified.name.of.some.sort");
fac = bfr.getFactory();
bfr2 = facLoc.useBeanFactory("another.qualified.name");
fac = bfr2.getFactory();
// verify that the same instance is returned
tb = (TestBean) fac.getBean("beans1.bean1");
assertTrue(tb.getName().equals("beans1.bean1"));
tb.setName("was beans1.bean1");
bfr3 = facLoc.useBeanFactory("another.qualified.name");
fac = bfr3.getFactory();
tb = (TestBean) fac.getBean("beans1.bean1");
assertTrue(tb.getName().equals("was beans1.bean1"));
bfr4 = facLoc.useBeanFactory("a.qualified.name.which.is.an.alias");
fac = bfr4.getFactory();
tb = (TestBean) fac.getBean("beans1.bean1");
assertTrue(tb.getName().equals("was beans1.bean1"));
bfr4.release();
bfr3.release();
bfr2.release();
bfr.release();
}
}
class TestBean {
private String name;
private List<?> list;
private Object objRef;
/**
* @return Returns the name.
*/
public String getName() {
return name;
}
/**
* @param name The name to set.
*/
public void setName(String name) {
this.name = name;
}
/**
* @return Returns the list.
*/
public List<?> getList() {
return list;
}
/**
* @param list The list to set.
*/
public void setList(List<?> list) {
this.list = list;
}
/**
* @return Returns the object.
*/
public Object getObjRef() {
return objRef;
}
/**
* @param object The object to set.
*/
public void setObjRef(Object object) {
this.objRef = object;
}
}

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- $Id: beans1.xml,v 1.3 2006/08/20 19:08:40 jhoeller Exp $ -->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="beans1.bean1" class="org.springframework.beans.factory.access.TestBean">
<property name="name"><value>beans1.bean1</value></property>
</bean>
<bean id="beans1.bean2" class="org.springframework.beans.factory.access.TestBean">
<property name="name"><value>bean2</value></property>
<property name="objRef"><ref bean="beans1.bean2"/></property>
</bean>
</beans>

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- $Id: beans2.xml,v 1.3 2006/08/20 19:08:40 jhoeller Exp $ -->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="beans2.bean1" class="org.springframework.beans.factory.access.TestBean">
<property name="name"><value>beans2.bean1</value></property>
</bean>
<bean id="beans2.bean2" class="org.springframework.beans.factory.access.TestBean">
<property name="name"><value>beans2.bean2</value></property>
<property name="objRef"><ref bean="beans1.bean1"/></property>
</bean>
</beans>

View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<!-- We are only using one definition file for the purposes of this test, since we do not have multiple
classloaders available in the environment to allow combining multiple files of the same name, but
of course the contents within could be spread out across multiple files of the same name withing
different jars -->
<beans>
<!-- this definition could be inside one beanRefFactory.xml file -->
<bean id="a.qualified.name.of.some.sort"
class="org.springframework.beans.factory.xml.XmlBeanFactory">
<constructor-arg value="org/springframework/beans/factory/access/SingletonBeanFactoryLocatorTests-beans1.xml"/>
</bean>
<!-- while the following two could be inside another, also on the classpath,
perhaps coming from another component jar -->
<bean id="another.qualified.name"
class="org.springframework.beans.factory.xml.XmlBeanFactory">
<constructor-arg value="org/springframework/beans/factory/access/SingletonBeanFactoryLocatorTests-beans1.xml"/>
<constructor-arg ref="a.qualified.name.of.some.sort"/> <!-- parent bean factory -->
</bean>
<alias name="another.qualified.name" alias="a.qualified.name.which.is.an.alias"/>
</beans>

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- $Id: beans1.xml,v 1.3 2006/08/20 19:08:40 jhoeller Exp $ -->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="beans1.bean1" class="org.springframework.beans.factory.access.TestBean">
<property name="name"><value>beans1.bean1</value></property>
</bean>
<bean id="beans1.bean2" class="org.springframework.beans.factory.access.TestBean">
<property name="name"><value>bean2</value></property>
<property name="objRef"><ref bean="beans1.bean2"/></property>
</bean>
</beans>

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- $Id: beans2.xml,v 1.3 2006/08/20 19:08:40 jhoeller Exp $ -->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="beans2.bean1" class="org.springframework.beans.factory.access.TestBean">
<property name="name"><value>beans2.bean1</value></property>
</bean>
<bean id="beans2.bean2" class="org.springframework.beans.factory.access.TestBean">
<property name="name"><value>beans2.bean2</value></property>
<property name="objRef"><ref bean="beans1.bean1"/></property>
</bean>
</beans>