001/* 002 * Copyright 2002-2012 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.context.access; 018 019import java.util.HashMap; 020import java.util.Map; 021 022import org.springframework.beans.BeansException; 023import org.springframework.beans.factory.BeanFactory; 024import org.springframework.beans.factory.access.BeanFactoryLocator; 025import org.springframework.beans.factory.access.SingletonBeanFactoryLocator; 026import org.springframework.context.ConfigurableApplicationContext; 027import org.springframework.context.support.ClassPathXmlApplicationContext; 028import org.springframework.core.io.support.ResourcePatternResolver; 029import org.springframework.core.io.support.ResourcePatternUtils; 030 031/** 032 * <p>Variant of {@link org.springframework.beans.factory.access.SingletonBeanFactoryLocator} 033 * which creates its internal bean factory reference as an 034 * {@link org.springframework.context.ApplicationContext} instead of 035 * SingletonBeanFactoryLocator's simple BeanFactory. For almost all usage scenarios, 036 * this will not make a difference, since within that ApplicationContext or BeanFactory 037 * you are still free to define either BeanFactory or ApplicationContext instances. 038 * The main reason one would need to use this class is if bean post-processing 039 * (or other ApplicationContext specific features are needed in the bean reference 040 * definition itself). 041 * 042 * <p><strong>Note:</strong> This class uses <strong>classpath*:beanRefContext.xml</strong> 043 * as the default resource location for the bean factory reference definition files. 044 * It is not possible nor legal to share definitions with SingletonBeanFactoryLocator 045 * at the same time. 046 * 047 * @author Colin Sampaleanu 048 * @author Juergen Hoeller 049 * @see org.springframework.beans.factory.access.SingletonBeanFactoryLocator 050 * @see org.springframework.context.access.DefaultLocatorFactory 051 */ 052public class ContextSingletonBeanFactoryLocator extends SingletonBeanFactoryLocator { 053 054 private static final String DEFAULT_RESOURCE_LOCATION = "classpath*:beanRefContext.xml"; 055 056 /** The keyed singleton instances */ 057 private static final Map<String, BeanFactoryLocator> instances = new HashMap<String, BeanFactoryLocator>(); 058 059 060 /** 061 * Returns an instance which uses the default "classpath*:beanRefContext.xml", as 062 * the name of the definition file(s). All resources returned by the current 063 * thread's context class loader's {@code getResources} method with this 064 * name will be combined to create a definition, which is just a BeanFactory. 065 * @return the corresponding BeanFactoryLocator instance 066 * @throws BeansException in case of factory loading failure 067 */ 068 public static BeanFactoryLocator getInstance() throws BeansException { 069 return getInstance(null); 070 } 071 072 /** 073 * Returns an instance which uses the specified selector, as the name of the 074 * definition file(s). In the case of a name with a Spring "classpath*:" prefix, 075 * or with no prefix, which is treated the same, the current thread's context class 076 * loader's {@code getResources} method will be called with this value to get 077 * all resources having that name. These resources will then be combined to form a 078 * definition. In the case where the name uses a Spring "classpath:" prefix, or 079 * a standard URL prefix, then only one resource file will be loaded as the 080 * definition. 081 * @param selector the location of the resource(s) which will be read and 082 * combined to form the definition for the BeanFactoryLocator instance. 083 * Any such files must form a valid ApplicationContext definition. 084 * @return the corresponding BeanFactoryLocator instance 085 * @throws BeansException in case of factory loading failure 086 */ 087 public static BeanFactoryLocator getInstance(String selector) throws BeansException { 088 String resourceLocation = selector; 089 if (resourceLocation == null) { 090 resourceLocation = DEFAULT_RESOURCE_LOCATION; 091 } 092 093 // For backwards compatibility, we prepend "classpath*:" to the selector name if there 094 // is no other prefix (i.e. "classpath*:", "classpath:", or some URL prefix). 095 if (!ResourcePatternUtils.isUrl(resourceLocation)) { 096 resourceLocation = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resourceLocation; 097 } 098 099 synchronized (instances) { 100 if (logger.isTraceEnabled()) { 101 logger.trace("ContextSingletonBeanFactoryLocator.getInstance(): instances.hashCode=" + 102 instances.hashCode() + ", instances=" + instances); 103 } 104 BeanFactoryLocator bfl = instances.get(resourceLocation); 105 if (bfl == null) { 106 bfl = new ContextSingletonBeanFactoryLocator(resourceLocation); 107 instances.put(resourceLocation, bfl); 108 } 109 return bfl; 110 } 111 } 112 113 114 /** 115 * Constructor which uses the specified name as the resource name 116 * of the definition file(s). 117 * @param resourceLocation the Spring resource location to use 118 * (either a URL or a "classpath:" / "classpath*:" pseudo URL) 119 */ 120 protected ContextSingletonBeanFactoryLocator(String resourceLocation) { 121 super(resourceLocation); 122 } 123 124 /** 125 * Overrides the default method to create definition object as an ApplicationContext 126 * instead of the default BeanFactory. This does not affect what can actually 127 * be loaded by that definition. 128 * <p>The default implementation simply builds a 129 * {@link org.springframework.context.support.ClassPathXmlApplicationContext}. 130 */ 131 @Override 132 protected BeanFactory createDefinition(String resourceLocation, String factoryKey) { 133 return new ClassPathXmlApplicationContext(new String[] {resourceLocation}, false); 134 } 135 136 /** 137 * Overrides the default method to refresh the ApplicationContext, invoking 138 * {@link ConfigurableApplicationContext#refresh ConfigurableApplicationContext.refresh()}. 139 */ 140 @Override 141 protected void initializeDefinition(BeanFactory groupDef) { 142 if (groupDef instanceof ConfigurableApplicationContext) { 143 ((ConfigurableApplicationContext) groupDef).refresh(); 144 } 145 } 146 147 /** 148 * Overrides the default method to operate on an ApplicationContext, invoking 149 * {@link ConfigurableApplicationContext#refresh ConfigurableApplicationContext.close()}. 150 */ 151 @Override 152 protected void destroyDefinition(BeanFactory groupDef, String selector) { 153 if (groupDef instanceof ConfigurableApplicationContext) { 154 if (logger.isTraceEnabled()) { 155 logger.trace("Context group with selector '" + selector + 156 "' being released, as there are no more references to it"); 157 } 158 ((ConfigurableApplicationContext) groupDef).close(); 159 } 160 } 161 162}