All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.beans.factory.support.PropertiesBeanDefinitionReader Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2020 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
 *
 *      https://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.support;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;

import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyAccessor;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.CannotLoadBeanClassException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.ResourcePropertiesPersister;
import org.springframework.lang.Nullable;
import org.springframework.util.PropertiesPersister;
import org.springframework.util.StringUtils;

/**
 * Bean definition reader for a simple properties format.
 *
 * 

Provides bean definition registration methods for Map/Properties and * ResourceBundle. Typically applied to a DefaultListableBeanFactory. * *

Example: * *

 * employee.(class)=MyClass       // bean is of class MyClass
 * employee.(abstract)=true       // this bean can't be instantiated directly
 * employee.group=Insurance       // real property
 * employee.usesDialUp=false      // real property (potentially overridden)
 *
 * salesrep.(parent)=employee     // derives from "employee" bean definition
 * salesrep.(lazy-init)=true      // lazily initialize this singleton bean
 * salesrep.manager(ref)=tony     // reference to another bean
 * salesrep.department=Sales      // real property
 *
 * techie.(parent)=employee       // derives from "employee" bean definition
 * techie.(scope)=prototype       // bean is a prototype (not a shared instance)
 * techie.manager(ref)=jeff       // reference to another bean
 * techie.department=Engineering  // real property
 * techie.usesDialUp=true         // real property (overriding parent value)
 *
 * ceo.$0(ref)=secretary          // inject 'secretary' bean as 0th constructor arg
 * ceo.$1=1000000                 // inject value '1000000' at 1st constructor arg
 * 
* * @author Rod Johnson * @author Juergen Hoeller * @author Rob Harrop * @since 26.11.2003 * @see DefaultListableBeanFactory * @deprecated as of 5.3, in favor of Spring's common bean definition formats * and/or custom reader implementations */ @Deprecated public class PropertiesBeanDefinitionReader extends AbstractBeanDefinitionReader { /** * Value of a T/F attribute that represents true. * Anything else represents false. Case seNsItive. */ public static final String TRUE_VALUE = "true"; /** * Separator between bean name and property name. * We follow normal Java conventions. */ public static final String SEPARATOR = "."; /** * Special key to distinguish {@code owner.(class)=com.myapp.MyClass}. */ public static final String CLASS_KEY = "(class)"; /** * Special key to distinguish {@code owner.(parent)=parentBeanName}. */ public static final String PARENT_KEY = "(parent)"; /** * Special key to distinguish {@code owner.(scope)=prototype}. * Default is "true". */ public static final String SCOPE_KEY = "(scope)"; /** * Special key to distinguish {@code owner.(singleton)=false}. * Default is "true". */ public static final String SINGLETON_KEY = "(singleton)"; /** * Special key to distinguish {@code owner.(abstract)=true} * Default is "false". */ public static final String ABSTRACT_KEY = "(abstract)"; /** * Special key to distinguish {@code owner.(lazy-init)=true} * Default is "false". */ public static final String LAZY_INIT_KEY = "(lazy-init)"; /** * Property suffix for references to other beans in the current * BeanFactory: e.g. {@code owner.dog(ref)=fido}. * Whether this is a reference to a singleton or a prototype * will depend on the definition of the target bean. */ public static final String REF_SUFFIX = "(ref)"; /** * Prefix before values referencing other beans. */ public static final String REF_PREFIX = "*"; /** * Prefix used to denote a constructor argument definition. */ public static final String CONSTRUCTOR_ARG_PREFIX = "$"; @Nullable private String defaultParentBean; private PropertiesPersister propertiesPersister = ResourcePropertiesPersister.INSTANCE; /** * Create new PropertiesBeanDefinitionReader for the given bean factory. * @param registry the BeanFactory to load bean definitions into, * in the form of a BeanDefinitionRegistry */ public PropertiesBeanDefinitionReader(BeanDefinitionRegistry registry) { super(registry); } /** * Set the default parent bean for this bean factory. * If a child bean definition handled by this factory provides neither * a parent nor a class attribute, this default value gets used. *

Can be used e.g. for view definition files, to define a parent * with a default view class and common attributes for all views. * View definitions that define their own parent or carry their own * class can still override this. *

Strictly speaking, the rule that a default parent setting does * not apply to a bean definition that carries a class is there for * backwards compatibility reasons. It still matches the typical use case. */ public void setDefaultParentBean(@Nullable String defaultParentBean) { this.defaultParentBean = defaultParentBean; } /** * Return the default parent bean for this bean factory. */ @Nullable public String getDefaultParentBean() { return this.defaultParentBean; } /** * Set the PropertiesPersister to use for parsing properties files. * The default is ResourcePropertiesPersister. * @see ResourcePropertiesPersister#INSTANCE */ public void setPropertiesPersister(@Nullable PropertiesPersister propertiesPersister) { this.propertiesPersister = (propertiesPersister != null ? propertiesPersister : ResourcePropertiesPersister.INSTANCE); } /** * Return the PropertiesPersister to use for parsing properties files. */ public PropertiesPersister getPropertiesPersister() { return this.propertiesPersister; } /** * Load bean definitions from the specified properties file, * using all property keys (i.e. not filtering by prefix). * @param resource the resource descriptor for the properties file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors * @see #loadBeanDefinitions(org.springframework.core.io.Resource, String) */ @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource), null); } /** * Load bean definitions from the specified properties file. * @param resource the resource descriptor for the properties file * @param prefix a filter within the keys in the map: e.g. 'beans.' * (can be empty or {@code null}) * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ public int loadBeanDefinitions(Resource resource, @Nullable String prefix) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource), prefix); } /** * Load bean definitions from the specified properties file. * @param encodedResource the resource descriptor for the properties file, * allowing to specify an encoding to use for parsing the file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { return loadBeanDefinitions(encodedResource, null); } /** * Load bean definitions from the specified properties file. * @param encodedResource the resource descriptor for the properties file, * allowing to specify an encoding to use for parsing the file * @param prefix a filter within the keys in the map: e.g. 'beans.' * (can be empty or {@code null}) * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ public int loadBeanDefinitions(EncodedResource encodedResource, @Nullable String prefix) throws BeanDefinitionStoreException { if (logger.isTraceEnabled()) { logger.trace("Loading properties bean definitions from " + encodedResource); } Properties props = new Properties(); try { try (InputStream is = encodedResource.getResource().getInputStream()) { if (encodedResource.getEncoding() != null) { getPropertiesPersister().load(props, new InputStreamReader(is, encodedResource.getEncoding())); } else { getPropertiesPersister().load(props, is); } } int count = registerBeanDefinitions(props, prefix, encodedResource.getResource().getDescription()); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + encodedResource); } return count; } catch (IOException ex) { throw new BeanDefinitionStoreException("Could not parse properties from " + encodedResource.getResource(), ex); } } /** * Register bean definitions contained in a resource bundle, * using all property keys (i.e. not filtering by prefix). * @param rb the ResourceBundle to load from * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors * @see #registerBeanDefinitions(java.util.ResourceBundle, String) */ public int registerBeanDefinitions(ResourceBundle rb) throws BeanDefinitionStoreException { return registerBeanDefinitions(rb, null); } /** * Register bean definitions contained in a ResourceBundle. *

Similar syntax as for a Map. This method is useful to enable * standard Java internationalization support. * @param rb the ResourceBundle to load from * @param prefix a filter within the keys in the map: e.g. 'beans.' * (can be empty or {@code null}) * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ public int registerBeanDefinitions(ResourceBundle rb, @Nullable String prefix) throws BeanDefinitionStoreException { // Simply create a map and call overloaded method. Map map = new HashMap<>(); Enumeration keys = rb.getKeys(); while (keys.hasMoreElements()) { String key = keys.nextElement(); map.put(key, rb.getObject(key)); } return registerBeanDefinitions(map, prefix); } /** * Register bean definitions contained in a Map, using all property keys (i.e. not * filtering by prefix). * @param map a map of {@code name} to {@code property} (String or Object). Property * values will be strings if coming from a Properties file etc. Property names * (keys) must be Strings. Class keys must be Strings. * @return the number of bean definitions found * @throws BeansException in case of loading or parsing errors * @see #registerBeanDefinitions(java.util.Map, String, String) */ public int registerBeanDefinitions(Map map) throws BeansException { return registerBeanDefinitions(map, null); } /** * Register bean definitions contained in a Map. * Ignore ineligible properties. * @param map a map of {@code name} to {@code property} (String or Object). Property * values will be strings if coming from a Properties file etc. Property names * (keys) must be Strings. Class keys must be Strings. * @param prefix a filter within the keys in the map: e.g. 'beans.' * (can be empty or {@code null}) * @return the number of bean definitions found * @throws BeansException in case of loading or parsing errors */ public int registerBeanDefinitions(Map map, @Nullable String prefix) throws BeansException { return registerBeanDefinitions(map, prefix, "Map " + map); } /** * Register bean definitions contained in a Map. * Ignore ineligible properties. * @param map a map of {@code name} to {@code property} (String or Object). Property * values will be strings if coming from a Properties file etc. Property names * (keys) must be Strings. Class keys must be Strings. * @param prefix a filter within the keys in the map: e.g. 'beans.' * (can be empty or {@code null}) * @param resourceDescription description of the resource that the * Map came from (for logging purposes) * @return the number of bean definitions found * @throws BeansException in case of loading or parsing errors * @see #registerBeanDefinitions(Map, String) */ public int registerBeanDefinitions(Map map, @Nullable String prefix, String resourceDescription) throws BeansException { if (prefix == null) { prefix = ""; } int beanCount = 0; for (Object key : map.keySet()) { if (!(key instanceof String)) { throw new IllegalArgumentException("Illegal key [" + key + "]: only Strings allowed"); } String keyString = (String) key; if (keyString.startsWith(prefix)) { // Key is of form: prefix.property String nameAndProperty = keyString.substring(prefix.length()); // Find dot before property name, ignoring dots in property keys. int sepIdx ; int propKeyIdx = nameAndProperty.indexOf(PropertyAccessor.PROPERTY_KEY_PREFIX); if (propKeyIdx != -1) { sepIdx = nameAndProperty.lastIndexOf(SEPARATOR, propKeyIdx); } else { sepIdx = nameAndProperty.lastIndexOf(SEPARATOR); } if (sepIdx != -1) { String beanName = nameAndProperty.substring(0, sepIdx); if (logger.isTraceEnabled()) { logger.trace("Found bean name '" + beanName + "'"); } if (!getRegistry().containsBeanDefinition(beanName)) { // If we haven't already registered it... registerBeanDefinition(beanName, map, prefix + beanName, resourceDescription); ++beanCount; } } else { // Ignore it: It wasn't a valid bean name and property, // although it did start with the required prefix. if (logger.isDebugEnabled()) { logger.debug("Invalid bean name and property [" + nameAndProperty + "]"); } } } } return beanCount; } /** * Get all property values, given a prefix (which will be stripped) * and add the bean they define to the factory with the given name. * @param beanName name of the bean to define * @param map a Map containing string pairs * @param prefix prefix of each entry, which will be stripped * @param resourceDescription description of the resource that the * Map came from (for logging purposes) * @throws BeansException if the bean definition could not be parsed or registered */ protected void registerBeanDefinition(String beanName, Map map, String prefix, String resourceDescription) throws BeansException { String className = null; String parent = null; String scope = BeanDefinition.SCOPE_SINGLETON; boolean isAbstract = false; boolean lazyInit = false; ConstructorArgumentValues cas = new ConstructorArgumentValues(); MutablePropertyValues pvs = new MutablePropertyValues(); String prefixWithSep = prefix + SEPARATOR; int beginIndex = prefixWithSep.length(); for (Map.Entry entry : map.entrySet()) { String key = StringUtils.trimWhitespace((String) entry.getKey()); if (key.startsWith(prefixWithSep)) { String property = key.substring(beginIndex); if (CLASS_KEY.equals(property)) { className = StringUtils.trimWhitespace((String) entry.getValue()); } else if (PARENT_KEY.equals(property)) { parent = StringUtils.trimWhitespace((String) entry.getValue()); } else if (ABSTRACT_KEY.equals(property)) { String val = StringUtils.trimWhitespace((String) entry.getValue()); isAbstract = TRUE_VALUE.equals(val); } else if (SCOPE_KEY.equals(property)) { // Spring 2.0 style scope = StringUtils.trimWhitespace((String) entry.getValue()); } else if (SINGLETON_KEY.equals(property)) { // Spring 1.2 style String val = StringUtils.trimWhitespace((String) entry.getValue()); scope = (!StringUtils.hasLength(val) || TRUE_VALUE.equals(val) ? BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE); } else if (LAZY_INIT_KEY.equals(property)) { String val = StringUtils.trimWhitespace((String) entry.getValue()); lazyInit = TRUE_VALUE.equals(val); } else if (property.startsWith(CONSTRUCTOR_ARG_PREFIX)) { if (property.endsWith(REF_SUFFIX)) { int index = Integer.parseInt(property.substring(1, property.length() - REF_SUFFIX.length())); cas.addIndexedArgumentValue(index, new RuntimeBeanReference(entry.getValue().toString())); } else { int index = Integer.parseInt(property.substring(1)); cas.addIndexedArgumentValue(index, readValue(entry)); } } else if (property.endsWith(REF_SUFFIX)) { // This isn't a real property, but a reference to another prototype // Extract property name: property is of form dog(ref) property = property.substring(0, property.length() - REF_SUFFIX.length()); String ref = StringUtils.trimWhitespace((String) entry.getValue()); // It doesn't matter if the referenced bean hasn't yet been registered: // this will ensure that the reference is resolved at runtime. Object val = new RuntimeBeanReference(ref); pvs.add(property, val); } else { // It's a normal bean property. pvs.add(property, readValue(entry)); } } } if (logger.isTraceEnabled()) { logger.trace("Registering bean definition for bean name '" + beanName + "' with " + pvs); } // Just use default parent if we're not dealing with the parent itself, // and if there's no class name specified. The latter has to happen for // backwards compatibility reasons. if (parent == null && className == null && !beanName.equals(this.defaultParentBean)) { parent = this.defaultParentBean; } try { AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition( parent, className, getBeanClassLoader()); bd.setScope(scope); bd.setAbstract(isAbstract); bd.setLazyInit(lazyInit); bd.setConstructorArgumentValues(cas); bd.setPropertyValues(pvs); getRegistry().registerBeanDefinition(beanName, bd); } catch (ClassNotFoundException ex) { throw new CannotLoadBeanClassException(resourceDescription, beanName, className, ex); } catch (LinkageError err) { throw new CannotLoadBeanClassException(resourceDescription, beanName, className, err); } } /** * Reads the value of the entry. Correctly interprets bean references for * values that are prefixed with an asterisk. */ private Object readValue(Map.Entry entry) { Object val = entry.getValue(); if (val instanceof String) { String strVal = (String) val; // If it starts with a reference prefix... if (strVal.startsWith(REF_PREFIX)) { // Expand the reference. String targetName = strVal.substring(1); if (targetName.startsWith(REF_PREFIX)) { // Escaped prefix -> use plain value. val = targetName; } else { val = new RuntimeBeanReference(targetName); } } } return val; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy