📄 singletonbeanfactorylocator.java
字号:
* <!-- define an alias for "com.mycompany.myapp.services" -->
* <alias name="com.mycompany.myapp.services" alias="com.mycompany.myapp.mypackage"/>
* </beans>
* </pre>
*
* @author Colin Sampaleanu
* @see org.springframework.context.access.DefaultLocatorFactory
*/
public class SingletonBeanFactoryLocator implements BeanFactoryLocator {
private static final String BEANS_REFS_XML_NAME = "classpath*:beanRefFactory.xml";
protected static final Log logger = LogFactory.getLog(SingletonBeanFactoryLocator.class);
// the keyed singleton instances
private static Map 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's context classloader's getResources() method with this name
* will be combined to create a definition, which is just a BeanFactory.
*/
public static BeanFactoryLocator getInstance() throws FatalBeanException {
return getInstance(BEANS_REFS_XML_NAME);
}
/**
* Returns an instance which uses the 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's context
* classloader's 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 combine to
* form the definition for the SingletonBeanFactoryLocator instance. The one file
* or multiple fragments with this name must form a valid BeanFactory definition.
*/
public static BeanFactoryLocator getInstance(String selector) throws FatalBeanException {
// 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 (selector.indexOf(':') == -1) {
selector = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + selector;
}
synchronized (instances) {
if (logger.isDebugEnabled()) {
logger.debug("SingletonBeanFactoryLocator.getInstance(): instances.hashCode=" +
instances.hashCode() + ", instances=" + instances);
}
BeanFactoryLocator bfl = (BeanFactoryLocator) instances.get(selector);
if (bfl == null) {
bfl = new SingletonBeanFactoryLocator(selector);
instances.put(selector, bfl);
}
return bfl;
}
}
// We map BeanFactoryGroup objects by String keys, and by the definition object.
private final Map bfgInstancesByKey = new HashMap();
private final Map bfgInstancesByObj = new HashMap();
private final String resourceName;
/**
* Constructor which uses the default "beanRefFactory.xml", as the name of the
* definition file(s). All resources returned by the definition classloader's
* getResources() method with this name will be combined to create a definition.
*/
protected SingletonBeanFactoryLocator() {
this.resourceName = BEANS_REFS_XML_NAME;
}
/**
* Constructor which uses the the specified name as the name of the
* definition file(s). All resources returned by the definition classloader's
* getResources() method with this name will be combined to create a definition
* definition.
*/
protected SingletonBeanFactoryLocator(String resourceName) {
this.resourceName = resourceName;
}
public BeanFactoryReference useBeanFactory(String factoryKey) throws BeansException {
synchronized (this.bfgInstancesByKey) {
BeanFactoryGroup bfg = (BeanFactoryGroup) this.bfgInstancesByKey.get(this.resourceName);
if (bfg != null) {
bfg.refCount++;
}
else {
// This group definition doesn't exist, we need to try to load it.
if (logger.isDebugEnabled()) {
logger.debug("Factory group with resource name [" + this.resourceName +
"] requested. Creating new instance.");
}
// Create the BeanFactory but don't initialize it.
BeanFactory groupContext = createDefinition(this.resourceName, factoryKey);
// Record its existence now, before instantiating any singletons.
bfg = new BeanFactoryGroup();
bfg.definition = groupContext;
bfg.refCount = 1;
this.bfgInstancesByKey.put(this.resourceName, 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) {
throw new BootstrapException("Unable to initialize group definition. " +
"Group resource name [" + this.resourceName + "], factory key [" + factoryKey + "]", ex);
}
}
final BeanFactory groupContext = bfg.definition;
String beanName = factoryKey;
Object bean;
try {
bean = groupContext.getBean(beanName);
if (bean instanceof String) {
logger.warn("You're using the deprecated alias-through-String-bean feature, " +
"which will be removed as of Spring 2.0. It is recommended to replace this " +
"with an <alias> tag (see SingletonBeanFactoryLocator javadoc).");
beanName = (String) bean;
bean = groupContext.getBean(beanName);
}
}
catch (BeansException ex) {
throw new BootstrapException("Unable to return specified BeanFactory instance: factory key [" +
factoryKey + "], from group with resource name [" + this.resourceName + "]", ex);
}
if (!(bean instanceof BeanFactory)) {
throw new BootstrapException("Bean '" + beanName + "' is not a BeanFactory: factory key [" +
factoryKey + "], from group with resource name [" + this.resourceName + "]");
}
final BeanFactory beanFactory = (BeanFactory) bean;
return new BeanFactoryReference() {
BeanFactory groupContextRef;
// constructor
{
this.groupContextRef = groupContext;
}
public BeanFactory getFactory() {
return beanFactory;
}
// Note that it's legal to call release more than once!
public void release() throws FatalBeanException {
synchronized (bfgInstancesByKey) {
BeanFactory savedRef = this.groupContextRef;
if (savedRef != null) {
this.groupContextRef = null;
BeanFactoryGroup bfg = (BeanFactoryGroup) bfgInstancesByObj.get(savedRef);
if (bfg != null) {
bfg.refCount--;
if (bfg.refCount == 0) {
destroyDefinition(savedRef, resourceName);
bfgInstancesByKey.remove(resourceName);
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 [" + resourceName + "]");
}
}
}
}
};
}
}
/**
* 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>This method should not instantiate any singletons. That function is performed
* by {@link #initializeDefinition initializeDefinition()}, which should also be
* overridden if this method is.
*/
protected BeanFactory createDefinition(String resourceName, String factoryKey) throws BeansException {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
try {
Resource[] configResources = resourcePatternResolver.getResources(resourceName);
if (configResources.length == 0) {
throw new FatalBeanException("Unable to find resource for specified definition. " +
"Group resource name [" + this.resourceName + "], factory key [" + factoryKey + "]");
}
reader.loadBeanDefinitions(configResources);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Error accessing bean definition resource [" + this.resourceName + "]", ex);
}
catch (BeanDefinitionStoreException ex) {
throw new FatalBeanException("Unable to load group definition: " +
"group resource name [" + this.resourceName + "], 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) throws BeansException {
if (groupDef instanceof ConfigurableListableBeanFactory) {
((ConfigurableListableBeanFactory) groupDef).preInstantiateSingletons();
}
}
/**
* Destroy definition in separate method so subclass may work with other definition types.
*/
protected void destroyDefinition(BeanFactory groupDef, String resourceName) throws BeansException {
if (groupDef instanceof ConfigurableBeanFactory) {
// debugging trace only
if (logger.isDebugEnabled()) {
logger.debug("Factory group with resource name '" + resourceName +
"' 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;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -