⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 singletonbeanfactorylocator.java

📁 Java/J2EE application framework based on [Expert One-on-One J2EE Design and Development] by Rod John
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
 *   <bean id="com.mycompany.myapp.mypackage"
 *         class="java.lang.String">
 *     <constructor-arg>
 *       <value>com.mycompany.myapp.services</value>
 *     </constructor-arg>
 *   </bean>
 * 
 * </beans>
 * </pre>
 *   
 * @author Colin Sampaleanu
 * @see org.springframework.context.access.DefaultLocatorFactory
 */
public class SingletonBeanFactoryLocator implements BeanFactoryLocator {

	public 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();


	// we map BeanFactoryGroup objects by String keys, and by the definition object
	private Map bfgInstancesByKey = new HashMap();

	private Map bfgInstancesByObj = new HashMap();

	private String resourceName;


	/**
	 * 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);
	}

	/**
	 * <p>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.</p> 
	 * 
	 * @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_URL_PREFIX + selector;
		
		synchronized (instances) {
			// debugging trace only
			//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;
		}
	}


	/**
	 * 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) {
				// debugging trace only
				//if (logger.isDebugEnabled()) {
				//	logger.debug("Factory group with resourceName '" + this.resourceName
				//			+ "' requested. Using existing instance.");
				//}
				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.");
				}
				
				BeanFactory groupContext = createDefinition(this.resourceName, factoryKey);

				bfg = new BeanFactoryGroup();
				bfg.definition = groupContext;
				bfg.refCount = 1;
				this.bfgInstancesByKey.put(this.resourceName, bfg);
				this.bfgInstancesByObj.put(groupContext, bfg);
			}

			final BeanFactory groupContext = bfg.definition;

			String lookupId = factoryKey;
			Object bean;
			try {
				bean = groupContext.getBean(lookupId);
			}
			catch (BeansException ex) {
				throw new FatalBeanException("Unable to return specified BeanFactory instance: factory key [" +
						factoryKey + "], from group with resource name [" + this.resourceName + "]", ex);
			}

			if (bean instanceof String) {
				// we have some indirection
				lookupId = (String) bean;
				try {
					bean = groupContext.getBean(lookupId);
				}
				catch (BeansException ex) {
					throw new FatalBeanException("Unable to return specified BeanFactory instance: lookup ID [" +
							lookupId + "], factory key [" + factoryKey + "], from group with resource name [" +
							this.resourceName + "]", ex);
				}
			}

			if (!(bean instanceof BeanFactory)) {
				throw new FatalBeanException("Returned bean is not BeanFactory or its subclass. lookup ID [" +
						lookupId + "], factory key [" + factoryKey + "], from group with resource name [" +
						this.resourceName + "]. Returned object class is [" + bean.getClass().getName() + "]");
			}

			final BeanFactory beanFactory = (BeanFactory) bean;

			return new BeanFactoryReference() {
				
				BeanFactory groupContextRef;
				
				// constructor
				{
					this.groupContextRef = groupContext;
				}
				
				public BeanFactory getFactory() {
					return beanFactory;
				}

				public void release() throws FatalBeanException {
					synchronized (bfgInstancesByKey) {
						BeanFactoryGroup bfg = (BeanFactoryGroup) bfgInstancesByObj.get(this.groupContextRef);
						if (bfg != null) {
							bfg.refCount--;
							if (bfg.refCount == 0) {
								destroyDefinition(this.groupContextRef, resourceName);
								bfgInstancesByKey.remove(resourceName);
								bfgInstancesByObj.remove(this.groupContextRef);
							}
						}
						else {
							logger.warn("Tried to release a SingletonBeanFactoryLocator (or subclass) 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).
	 */
	protected BeanFactory createDefinition(String resourceName, String factoryKey) throws BeansException {
		DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
		ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

		Resource[] configResources;
		try {
			configResources = resourcePatternResolver.getResources(resourceName);
		}
		catch (IOException e) {
			throw new BeanDefinitionStoreException("Error accessing bean definition resource", e);
		}

		if (configResources.length == 0)
			throw new FatalBeanException("Unable to find resource for specified definition. " +
					"Group resource name [" + this.resourceName + "], factory key [" + factoryKey + "]");

		try {
			for (int j = 0; j < configResources.length; j++)
				reader.loadBeanDefinitions(configResources[j]);
			
			factory.preInstantiateSingletons();
		}
		catch (BeansException e) {
			throw new FatalBeanException("Unable to load group definition. " +
					"Group resource name [" + this.resourceName + "], factory key [" + factoryKey + "]");
		}
		return factory;
	}
	
	/**
	 * 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 + -