|
From: Trevor B. <tre...@gm...> - 2005-09-26 23:33:26
|
Hi all,
I wanted to draw attention to a behavior in Spring that I was
already aware of, but which bit me unexpectedly in a project I was
called in to help. (it cropped up due to some 'mis-use' of Spring).
I found this with Spring 1.2.3. When I went back to Spring 1.0.2 I
found that it exhibited different behavior, which I consider more
correct. I found the JIRA issue that relates to this change:
http://opensource2.atlassian.com/projects/spring/browse/SPR-174
Let me explain the scenario I encountered.
Spring can call private constructors for beans (since 1.1RC1). Take
this simple Java class:
public class MySingletonBean {
private static MySingletonBean s_instance =3D new MySingletonBean();
private MySingletonBean() {
System.out.println("Creating MySingletonBean");
}
public static MySingletonBean getInstance() {
return s_instance;
}
}
With a spring bean definition like so:
<bean id=3D"MyBean"
class=3D"com.example.MySingletonBean">
</bean>
I find that when I load an ApplicationContext, Spring successfully
calls the private constructor (by calling setAccessible(true) on the
java.lang.Constructor object).
When I think about it, I guess that I would have expected that Spring
would have flagged this usage as an error or at least a warning - e.g.
"No public constructor in class [class com.example.MySingletonBean]".
I came across this feature when I was called in to help out an
application that was having a problem which turned out to be multiple
singletons within a single classloader. A class was defined as a
singleton using the typical pattern (i.e. private constructor, static
getInstance() method), but was also defined in a Spring context using
a bean definiton like so:
<bean id=3D"MyBean"
class=3D"com.example.MySingletonBean">
</bean>
Of course to be correct, the bean definition should have been:
<bean id=3D"MyBean"
class=3D"com.example.MySingletonBean"
factory-method=3D"getInstance">
</bean>
Different places in the application code were accessing the same
object in different ways - some were using Spring, others were calling
getInstance(). Of course, we had two instances of the singleton within
the application - so the code was not behaving correctly.
While the design of this part of the app was not exactly correct/good
practice (should really just be using one mechanism to access the
object) - it still exposed a behavior in Spring that I think may not
be wholly intuitive. I have since reworked this to something I think
is more suitable, removing the singleton pattern implementation and
simply using Spring to wire up the singleton object to all objects
that need it.
With Spring 1.0.2 - we do not have this problem. Spring throws an error:
Exception in thread "main"
org.springframework.beans.factory.BeanDefinitionStoreException: Error
registering bean with name 'MyBean' defined in class path resource
[applicationContext.xml]: Validation of bean definition with name
failed; nested exception is
org.springframework.beans.factory.support.BeanDefinitionValidationException=
:
No public constructor in class [class com.example.MySingletonBean]
I was just curious as to the development team's thoughts on this
issue. I understand there are scenarios where we may want to
instantiate beans that only have private constructors. But it does
open up the possibility for miss-use - as can be seen from my concrete
example above.
Regards,
Trevor
|
|
From: Eugene K. <eu...@md...> - 2005-09-27 17:51:05
Attachments:
SAXBeanDefinitionParser.java
|
Folks, I've implemented a sax-based parser for Spring's XML bean definition and wonder if you'd be interested to integrate this into Spring framework. this class can be used in place of the current DOM-based parser as well as an lightweight adapter for runtime app context generators such as xslt-based tools or tools like Jacn (even so I believe Jacn should directly generate Spring factories without intermediate XML representation). I run all existing tests from xml package and they all seem working just fine. That however required to have copies of XmlBeanDefinitionReaderand and XmlBeanFactory patched to work with my parser. In my tests this parser is little bit faster then current DOM-based parser and in my opinion it is structured little better then DOM-based parser. Basically is is sort-of stripped-down Digester (stack-based state machine) with declarative definitions for parsing rules. Interestingly the same rules could be used with StAX-based parser with very minimal changes or even without changes using adapter for SAX Attributes instance used in rule params. Also note that current XmlBeanDefinitionReader class is tight to DOM-based API and does not allow to hookup abstract XML parsers (e.g. SAX or StAX). This make impossible to substiture custom non-DOM XML parser to XmlBeanFactory. regards, Eugene |
|
From: Colin S. <col...@ex...> - 2005-10-17 01:51:45
|
I managed to miss this when it was initially posted. We may want to
consider this for 1.3.
Eugene Kuleshov wrote:
> Folks,
>
> I've implemented a sax-based parser for Spring's XML bean definition
> and wonder if you'd be interested to integrate this into Spring
> framework. this class can be used in place of the current DOM-based
> parser as well as an lightweight adapter for runtime app context
> generators such as xslt-based tools or tools like Jacn (even so I
> believe Jacn should directly generate Spring factories without
> intermediate XML representation).
>
> I run all existing tests from xml package and they all seem working
> just fine. That however required to have copies of
> XmlBeanDefinitionReaderand and XmlBeanFactory patched to work with my
> parser.
>
> In my tests this parser is little bit faster then current DOM-based
> parser and in my opinion it is structured little better then DOM-based
> parser. Basically is is sort-of stripped-down Digester (stack-based
> state machine) with declarative definitions for parsing rules.
> Interestingly the same rules could be used with StAX-based parser with
> very minimal changes or even without changes using adapter for SAX
> Attributes instance used in rule params.
>
> Also note that current XmlBeanDefinitionReader class is tight to
> DOM-based API and does not allow to hookup abstract XML parsers (e.g.
> SAX or StAX). This make impossible to substiture custom non-DOM XML
> parser to XmlBeanFactory.
>
> regards,
> Eugene
>
>
>------------------------------------------------------------------------
>
>
>package com.cibcwm.go.otis.dasl.util;
>
>import java.io.IOException;
>import java.io.InputStream;
>import java.util.ArrayList;
>import java.util.Arrays;
>import java.util.HashMap;
>import java.util.Iterator;
>import java.util.List;
>import java.util.Map;
>import java.util.Properties;
>
>import javax.xml.parsers.ParserConfigurationException;
>import javax.xml.parsers.SAXParser;
>import javax.xml.parsers.SAXParserFactory;
>
>import org.apache.commons.logging.Log;
>import org.apache.commons.logging.LogFactory;
>import org.springframework.beans.MutablePropertyValues;
>import org.springframework.beans.factory.BeanDefinitionStoreException;
>import org.springframework.beans.factory.config.BeanDefinition;
>import org.springframework.beans.factory.config.BeanDefinitionHolder;
>import org.springframework.beans.factory.config.ConstructorArgumentValues;
>import org.springframework.beans.factory.config.RuntimeBeanReference;
>import org.springframework.beans.factory.config.TypedStringValue;
>import org.springframework.beans.factory.support.AbstractBeanDefinition;
>import org.springframework.beans.factory.support.BeanDefinitionReader;
>import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
>import org.springframework.beans.factory.support.LookupOverride;
>import org.springframework.beans.factory.support.ManagedList;
>import org.springframework.beans.factory.support.ManagedMap;
>import org.springframework.beans.factory.support.ManagedSet;
>import org.springframework.beans.factory.support.ReplaceOverride;
>import org.springframework.core.io.ClassPathResource;
>import org.springframework.core.io.Resource;
>import org.springframework.core.io.support.ResourcePatternUtils;
>import org.springframework.util.ClassUtils;
>import org.springframework.util.StringUtils;
>
>import org.xml.sax.Attributes;
>import org.xml.sax.InputSource;
>import org.xml.sax.SAXException;
>import org.xml.sax.SAXParseException;
>import org.xml.sax.helpers.DefaultHandler;
>
>
>/**
> * Default implementation of the XmlBeanDefinitionParser interface.
> * Parses bean definitions according to the "spring-beans" DTD,
> * that is, Spring's default XML bean definition format.
> *
> * <p>The structure, elements and attribute names of the required XML document
> * are hard-coded in this class. (Of course a transform could be run if necessary
> * to produce this format). "beans" doesn't need to be the root element of the XML
> * document: This class will parse all bean definition elements in the XML file.
> *
> * @author Rod Johnson
> * @author Juergen Hoeller
> * @author Rob Harrop
> * @since 18.12.2003
> */
>public class SAXBeanDefinitionParser extends DefaultHandler {
>
> /**
> * Stack of the intermediate processing contexts.
> */
> List stack = new ArrayList();
> /**
> * Complete name of the current element.
> */
> private String match = "";
> private StringBuffer text = new StringBuffer();
>
>
> private final RuleSet RULES = new RuleSet();
> {
> RULES.add("*/description", null);
>
> RULES.add("beans/import", new BeansImportRule());
> RULES.add("beans/alias", new BeansAliasRule());
> RULES.add("beans", new BeansRule()); // description?, (import | alias | bean)*
>
> RULES.add("*/bean/replaced-method/arg-type", new BeanReplacedMethodArgTypeRule());
> RULES.add("*/bean/replaced-method", new BeanReplacedMethodRule()); // (arg-type)*
> RULES.add("*/bean/property", new BeanPropertyRule()); // description?, (bean | ref | idref | value | null | list | set | map | props)?
> RULES.add("*/bean/lookup-method", new BeanLookupMethodRule());
> RULES.add("*/bean/constructor-arg", new BeanConstructorArgRule()); // description?, (bean | ref | idref | value | null | list | set | map | props)?
> RULES.add("*/bean", new BeanRule()); // description?, (constructor-arg | property | lookup-method | replaced-method)*
>
> RULES.add("*/ref", new RefRule());
> RULES.add("*/idref", new IdRefRule());
> RULES.add("*/value", new ValueRule());
> RULES.add("*/null", new NullRule());
>
> RULES.add("*/list", new ListRule()); // (bean | ref | idref | value | null | list | set | map | props)*
>
> RULES.add("*/set", new SetType()); // (bean | ref | idref | value | null | list | set | map | props)*
>
> RULES.add("*/map/entry/key", new MapEntryKeyRule()); // (bean | ref | idref | value | null | list | set | map | props)
> RULES.add("*/map/entry", new MapEntryRule()); // key?, (bean | ref | idref | value | null | list | set | map | props)?
> RULES.add("*/map", new MapRule()); // (entry)*
>
> RULES.add("*/props/prop", new PropsPropRule());
> RULES.add("*/props", new PropsRule()); // (prop)*
> }
>
> static private final String BEAN_NAME_DELIMITERS = ",; ";
>
> /**
> * Value of a T/F attribute that represents true.
> * Anything else represents false. Case seNsItive.
> */
> static private final String TRUE_VALUE = "true";
> static private final String DEFAULT_VALUE = "default";
>
> static private final String AUTOWIRE_BY_NAME_VALUE = "byName";
> static private final String AUTOWIRE_BY_TYPE_VALUE = "byType";
> static private final String AUTOWIRE_CONSTRUCTOR_VALUE = "constructor";
> static private final String AUTOWIRE_AUTODETECT_VALUE = "autodetect";
>
> static private final String DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE = "all";
> static private final String DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE = "simple";
> static private final String DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE = "objects";
>
> static private final String DEFAULT_LAZY_INIT_ATTRIBUTE = "default-lazy-init";
> static private final String DEFAULT_AUTOWIRE_ATTRIBUTE = "default-autowire";
> static private final String DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE = "default-dependency-check";
>
> static private final String RESOURCE_ATTRIBUTE = "resource";
>
> static private final String NAME_ATTRIBUTE = "name";
> static private final String ALIAS_ATTRIBUTE = "alias";
>
> static private final String BEAN_ELEMENT = "bean";
> static private final String ID_ATTRIBUTE = "id";
> static private final String PARENT_ATTRIBUTE = "parent";
>
> static private final String CLASS_ATTRIBUTE = "class";
> static private final String ABSTRACT_ATTRIBUTE = "abstract";
> static private final String SINGLETON_ATTRIBUTE = "singleton";
> static private final String LAZY_INIT_ATTRIBUTE = "lazy-init";
> static private final String AUTOWIRE_ATTRIBUTE = "autowire";
> static private final String DEPENDENCY_CHECK_ATTRIBUTE = "dependency-check";
> static private final String DEPENDS_ON_ATTRIBUTE = "depends-on";
> static private final String INIT_METHOD_ATTRIBUTE = "init-method";
> static private final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";
> static private final String FACTORY_METHOD_ATTRIBUTE = "factory-method";
> static private final String FACTORY_BEAN_ATTRIBUTE = "factory-bean";
>
> static private final String INDEX_ATTRIBUTE = "index";
> static private final String TYPE_ATTRIBUTE = "type";
> static private final String REF_ATTRIBUTE = "ref";
> static private final String VALUE_ATTRIBUTE = "value";
>
> static private final String REPLACER_ATTRIBUTE = "replacer";
> static private final String ARG_TYPE_MATCH_ATTRIBUTE = "match";
>
> static private final String BEAN_REF_ATTRIBUTE = "bean";
> static private final String LOCAL_REF_ATTRIBUTE = "local";
> static private final String PARENT_REF_ATTRIBUTE = "parent";
>
> static private final String KEY_ATTRIBUTE = "key";
> static private final String KEY_REF_ATTRIBUTE = "key-ref";
> static private final String VALUE_REF_ATTRIBUTE = "value-ref";
>
>
> protected final Log logger = LogFactory.getLog(getClass());
>
> BeanDefinitionReader beanDefinitionReader;
>
> Resource resource;
>
> String defaultLazyInit;
>
> String defaultAutowire;
>
> String defaultDependencyCheck;
>
> int beanDefinitionCount = 0;
> private boolean validating = true;
>
>
> public SAXBeanDefinitionParser( boolean validating) {
> this.validating = validating;
> }
>
> public int registerBeanDefinitions(BeanDefinitionReader reader, Resource resource, InputStream inputStream)
> throws BeanDefinitionStoreException {
> this.beanDefinitionReader = reader;
> this.resource = resource;
>
> SAXParserFactory factory = SAXParserFactory.newInstance();
> factory.setValidating(validating);
> SAXParser parser;
> try {
> parser = factory.newSAXParser();
> logger.debug("Loading bean definitions");
> parser.parse(inputStream, this);
>
> } catch( ParserConfigurationException ex) {
> logger.error( ex.getMessage(), ex);
> throw new BeanDefinitionStoreException( ex.getMessage(), ex);
>
> } catch( SAXException ex) {
> logger.error( ex.getMessage(), ex);
> throw new BeanDefinitionStoreException( ex.getMessage(), ex);
>
> } catch( IOException ex) {
> logger.error( ex.getMessage(), ex);
> throw new BeanDefinitionStoreException( ex.getMessage(), ex);
>
> }
>
> if (logger.isDebugEnabled()) {
> logger.debug("Found " + beanDefinitionCount + " <bean> elements in " + resource);
> }
> return beanDefinitionCount;
> }
>
> /**
> * Return the BeanDefinitionReader that this parser has been called from.
> */
> protected final BeanDefinitionReader getBeanDefinitionReader() {
> return beanDefinitionReader;
> }
>
> /**
> * Return the descriptor for the XML resource that this parser works on.
> */
> protected final Resource getResource() {
> return resource;
> }
>
> /**
> * Return the default lazy-init flag for the document that's currently parsed.
> */
> protected final String getDefaultLazyInit() {
> return defaultLazyInit;
> }
>
> /**
> * Return the default autowire setting for the document that's currently parsed.
> */
> protected final String getDefaultAutowire() {
> return defaultAutowire;
> }
>
> /**
> * Return the default dependency-check setting for the document that's currently parsed.
> */
> protected final String getDefaultDependencyCheck() {
> return defaultDependencyCheck;
> }
>
>
> /**
> * Process notification of the start of an XML element being reached.
> *
> * @param ns - The Namespace URI, or the empty string if the element has no
> * Namespace URI or if Namespace processing is not being performed.
> * @param localName - The local name (without prefix), or the empty string
> * if Namespace processing is not being performed.
> * @param qName - The qualified name (with prefix), or the empty string if
> * qualified names are not available.
> * @param list - The attributes attached to the element. If there are no
> * attributes, it shall be an empty Attributes object.
> * @exception SAXException if a parsing error is to be reported
> */
> public final void startElement( String ns, String localName, String qName, Attributes list) {
> // the actual element name is either in localName or qName, depending
> // on whether the parser is namespace aware
> String name = localName;
> if (name == null || name.length() < 1) {
> name = qName;
> }
>
> // Compute the current matching rule
> StringBuffer sb = new StringBuffer(match);
> if (match.length() > 0) {
> sb.append('/');
> }
> sb.append(name);
> match = sb.toString();
>
> // Fire "begin" event for relevant rule
> Rule r = (Rule) RULES.match(match);
> if (r != null) {
> r.begin(ns, name, list);
> }
>
> text = new StringBuffer(); // TODO optimize this
> }
>
> /**
> * Process notification of the end of an XML element being reached.
> *
> * @param ns - The Namespace URI, or the empty string if the element has no
> * Namespace URI or if Namespace processing is not being performed.
> * @param localName - The local name (without prefix), or the empty string
> * if Namespace processing is not being performed.
> * @param qName - The qualified XML 1.0 name (with prefix), or the empty
> * string if qualified names are not available.
> *
> * @exception SAXException if a parsing error is to be reported
> */
> public final void endElement(String ns, String localName, String qName) {
> // the actual element name is either in localName or qName, depending
> // on whether the parser is namespace aware
> String name = localName;
> if (name == null || name.length() < 1) {
> name = qName;
> }
>
> // Fire "end" event for relevant rule
> Rule r = (Rule) RULES.match(match);
> if (r != null) {
> r.text(ns, name, text.toString());
> r.end(ns, name);
> }
>
> // Recover the previous match expression
> int slash = match.lastIndexOf('/');
> if (slash >= 0) {
> match = match.substring(0, slash);
> } else {
> match = "";
> }
> }
>
> public void characters( char[] ch, int start, int length) {
> text.append( ch, start, length);
> }
>
>
> public InputSource resolveEntity( String publicId, String systemId) throws SAXException {
> if("-//SPRING//DTD BEAN//EN".equals(publicId) &&
> "http://www.springframework.org/dtd/spring-beans.dtd".equals(systemId)) {
> Resource resource = new ClassPathResource("/org/springframework/beans/factory/xml/spring-beans.dtd", getClass());
> try {
> InputSource source = new InputSource( resource.getInputStream());
> source.setPublicId( publicId);
> source.setSystemId( systemId);
> if (logger.isDebugEnabled()) {
> logger.debug("Found beans DTD [" + systemId + "] in classpath");
> }
> return source;
> } catch( IOException e) {
> throw new SAXException(e);
> }
> }
> return null;
> }
>
> public void error( SAXParseException e) throws SAXException {
> System.err.println( "ERROR "+e.toString());
> throw e;
> }
>
> public void warning( SAXParseException e) throws SAXException {
> System.err.println( "WARNING "+e.toString());
> throw e;
> }
>
> public void fatalError( SAXParseException e) throws SAXException {
> System.err.println( "FATAL "+e.toString());
> throw e;
> }
>
> private static final class RuleSet {
> private Map rules = new HashMap();
> private List lpatterns = new ArrayList();
> private List rpatterns = new ArrayList();
>
> public void add(String path, Rule rule) {
> String pattern = path;
> if (path.startsWith("*/")) {
> pattern = path.substring(1);
> lpatterns.add(pattern);
> } else if (path.endsWith("/*")) {
> pattern = path.substring(0, path.length() - 1);
> rpatterns.add(pattern);
> }
> rules.put(pattern, rule);
> }
>
> public Object match(String path) {
> if (rules.containsKey(path)) {
> return rules.get(path);
> }
>
> for (Iterator it = lpatterns.iterator(); it.hasNext();) {
> String pattern = (String) it.next();
> if (path.endsWith(pattern)) {
> return rules.get(pattern);
> }
> }
>
> for (Iterator it = rpatterns.iterator(); it.hasNext();) {
> String pattern = (String) it.next();
> if (path.startsWith(pattern)) {
> return rules.get(pattern);
> }
> }
>
> return null;
> }
>
> }
>
> final Object peek() {
> return stack.size() == 0 ? null : stack.get(stack.size() - 1);
> }
>
> final Object peek(int n) {
> return stack.size() < (n + 1) ? null : stack.get(n);
> }
>
> final Object pop() {
> return stack.size() == 0 ? null : stack.remove(stack.size() - 1);
> }
>
> final void push(Object object) {
> stack.add(object);
> }
>
>
> protected abstract class Rule {
>
> public void begin( String ns, String name, Attributes attrs) {
> }
>
> public void end( String ns, String name) {
> }
>
> public void text( String ns, String name, String text) {
> }
>
> protected BeanDefinitionHolder getBeanDefinitionHolder() {
> for( int n = stack.size()-1; n>=0; n--) {
> Object o = stack.get(n);
> if( o instanceof BeanDefinitionHolder) {
> return ( BeanDefinitionHolder) o;
> }
> }
> throw new BeanDefinitionStoreException( "Unable to find BeanDefinitionHolder");
> }
>
> protected AbstractBeanDefinition getBeanDefinition() {
> return ( AbstractBeanDefinition) getBeanDefinitionHolder().getBeanDefinition();
> }
>
> protected Object getValue(String elementName, Attributes atts) {
> String ref = atts.getValue(REF_ATTRIBUTE);
> String value = atts.getValue(VALUE_ATTRIBUTE);
> if(ref!=null && value!=null) {
> throw new BeanDefinitionStoreException( getResource(), getBeanDefinitionHolder().getBeanName(), elementName +
> " is only allowed to contain either a 'ref' attribute OR a 'value' attribute OR a sub-element");
> }
>
> if (ref!=null) {
> return new RuntimeBeanReference(ref);
> } else if (value!=null) {
> return value;
> }
>
> return null;
> }
>
> public void setValue( Object value) {
> Object target = peek();
>
> // introspect type and set an appropriate property
> // TODO add assertion for already set properties
> if( target instanceof ConstructorArgumentValuesHolder) { // constructor-arg/...
> ((ConstructorArgumentValuesHolder) target).setValue(value);
>
> } else if( target instanceof PropertyValueHolder) { // property/...
> ((PropertyValueHolder) target).setValue(value);
>
> } else if( target instanceof ManagedList) { // list/...
> ((ManagedList) target).add(value);
>
> } else if( target instanceof ManagedSet) { // set/...
> ((ManagedSet) target).add(value);
>
> } else if( target instanceof MapEntryHolder) { // map/entry/...
> ((MapEntryHolder) target).setValue(value);
>
> } else if( target instanceof MapEntryKeyHolder) { // map/entry/key/...
> ((MapEntryKeyHolder) target).setKey(value);
>
> }
> }
> }
>
>
> /**
> * description?, (import | alias | bean)*
> */
> private final class BeansRule extends Rule {
> public void begin( String ns, String name, Attributes atts) {
> defaultLazyInit = atts.getValue(DEFAULT_LAZY_INIT_ATTRIBUTE);
> defaultAutowire = atts.getValue(DEFAULT_AUTOWIRE_ATTRIBUTE);
> defaultDependencyCheck = atts.getValue(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE);
>
> if (logger.isDebugEnabled()) {
> logger.debug("Default lazy init '" + defaultLazyInit + "'");
> logger.debug("Default autowire '" + defaultAutowire + "'");
> logger.debug("Default dependency check '" + defaultDependencyCheck + "'");
> }
> }
> }
>
>
> public class BeansImportRule extends Rule {
> public void begin( String ns, String name, Attributes atts) {
> String location = atts.getValue(RESOURCE_ATTRIBUTE);
>
> if (ResourcePatternUtils.isUrl(location)) {
> int importCount = getBeanDefinitionReader().loadBeanDefinitions(location);
> if (logger.isDebugEnabled()) {
> logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
> }
> } else {
> // No URL -> considering resource location as relative to the current file.
> try {
> Resource relativeResource = getResource().createRelative(location);
> int importCount = getBeanDefinitionReader().loadBeanDefinitions(relativeResource);
> if (logger.isDebugEnabled()) {
> logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
> }
> } catch (IOException ex) {
> throw new BeanDefinitionStoreException(
> "Invalid relative resource location [" + location + "] to import bean definitions from", ex);
> }
> }
> }
> }
>
>
> public class BeansAliasRule extends Rule {
> public void begin( String ns, String name, Attributes atts) {
> String beanName = atts.getValue(NAME_ATTRIBUTE);
> String alias = atts.getValue(ALIAS_ATTRIBUTE);
> beanDefinitionReader.getBeanFactory().registerAlias(beanName, alias);
> }
> }
>
>
> /**
> * description?, (constructor-arg | property | lookup-method | replaced-method)*
> */
> public class BeanRule extends Rule {
> public void begin( String ns, String name, Attributes atts) {
> beanDefinitionCount++;
> // BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele, false);
>
> String id = atts.getValue(ID_ATTRIBUTE);
> String nameAttr = atts.getValue(NAME_ATTRIBUTE);
>
> List aliases = new ArrayList();
> if (StringUtils.hasLength(nameAttr)) {
> String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
> aliases.addAll(Arrays.asList(nameArr));
> }
>
> String beanName = id;
> if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
> beanName = (String) aliases.remove(0);
> if (logger.isDebugEnabled()) {
> logger.debug("No XML 'id' specified - using '" + beanName +
> "' as bean name and " + aliases + " as aliases");
> }
> }
>
> BeanDefinition beanDefinition = getBeanDefinition(atts, beanName);
>
> if (!StringUtils.hasText(beanName) && beanDefinition instanceof AbstractBeanDefinition) {
> boolean isInnerBean = stack.size()>0;
> beanName = BeanDefinitionReaderUtils.generateBeanName(
> (AbstractBeanDefinition) beanDefinition, beanDefinitionReader.getBeanFactory(), isInnerBean);
> if (logger.isDebugEnabled()) {
> logger.debug("Neither XML 'id' nor 'name' specified - " +
> "using generated bean name [" + beanName + "]");
> }
> }
>
> String[] aliasesArray = (String[]) aliases.toArray(new String[aliases.size()]);
>
> push( new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray));
> }
>
> public void end( String ns, String name) {
> BeanDefinitionHolder bh = ( BeanDefinitionHolder) pop(); // don't inline
> if(stack.size()==0) {
> BeanDefinitionReaderUtils.registerBeanDefinition(bh, beanDefinitionReader.getBeanFactory());
> } else {
> setValue(bh);
> }
> }
>
> protected BeanDefinition getBeanDefinition(Attributes atts, String beanName)
> throws BeanDefinitionStoreException {
>
> String className = atts.getValue(CLASS_ATTRIBUTE);
> String parent = atts.getValue(PARENT_ATTRIBUTE);
>
> try {
> ConstructorArgumentValues cargs = new ConstructorArgumentValues();
> MutablePropertyValues pvs = new MutablePropertyValues();
>
> AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition(
> className, parent, cargs, pvs, getBeanDefinitionReader().getBeanClassLoader());
>
> String dependsOn = atts.getValue(DEPENDS_ON_ATTRIBUTE);
> if (dependsOn!=null) {
> bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, BEAN_NAME_DELIMITERS));
> }
>
> String factoryMethod = atts.getValue(FACTORY_METHOD_ATTRIBUTE);
> if (factoryMethod!=null) {
> bd.setFactoryMethodName(factoryMethod);
> }
> String factoryBeanName = atts.getValue(FACTORY_BEAN_ATTRIBUTE);
> if (factoryBeanName!=null) {
> bd.setFactoryBeanName(factoryBeanName);
> }
>
> String dependencyCheck = atts.getValue(DEPENDENCY_CHECK_ATTRIBUTE);
> if (DEFAULT_VALUE.equals(dependencyCheck)) {
> dependencyCheck = getDefaultDependencyCheck();
> }
> bd.setDependencyCheck(getDependencyCheck(dependencyCheck));
>
> String autowire = atts.getValue(AUTOWIRE_ATTRIBUTE);
> if (DEFAULT_VALUE.equals(autowire)) {
> autowire = getDefaultAutowire();
> }
> bd.setAutowireMode(getAutowireMode(autowire));
>
> String initMethodName = atts.getValue(INIT_METHOD_ATTRIBUTE);
> if (initMethodName!=null && !initMethodName.equals("")) {
> bd.setInitMethodName(initMethodName);
> }
> String destroyMethodName = atts.getValue(DESTROY_METHOD_ATTRIBUTE);
> if (destroyMethodName!=null && !destroyMethodName.equals("")) {
> bd.setDestroyMethodName(destroyMethodName);
> }
>
> bd.setResourceDescription(getResource().getDescription());
>
> String isAbstract = atts.getValue(ABSTRACT_ATTRIBUTE);
> if (isAbstract!=null) {
> bd.setAbstract(TRUE_VALUE.equals(isAbstract));
> }
>
> String isSingleton = atts.getValue(SINGLETON_ATTRIBUTE);
> if (isSingleton!=null) {
> bd.setSingleton(TRUE_VALUE.equals(isSingleton));
> }
>
> String lazyInit = atts.getValue(LAZY_INIT_ATTRIBUTE);
> if (DEFAULT_VALUE.equals(lazyInit) && bd.isSingleton()) {
> // Just apply default to singletons, as lazy-init has no meaning for prototypes.
> lazyInit = defaultLazyInit;
> }
> bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
>
> return bd;
> }
> catch (ClassNotFoundException ex) {
> throw new BeanDefinitionStoreException(
> resource, beanName, "Bean class [" + className + "] not found", ex);
> }
> catch (NoClassDefFoundError err) {
> throw new BeanDefinitionStoreException(
> resource, beanName, "Class that bean class [" + className + "] depends on not found", err);
> }
> }
>
> protected int getDependencyCheck(String att) {
> int dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_NONE;
> if (DEPENDENCY_CHECK_ALL_ATTRIBUTE_VALUE.equals(att)) {
> dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_ALL;
> }
> else if (DEPENDENCY_CHECK_SIMPLE_ATTRIBUTE_VALUE.equals(att)) {
> dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_SIMPLE;
> }
> else if (DEPENDENCY_CHECK_OBJECTS_ATTRIBUTE_VALUE.equals(att)) {
> dependencyCheckCode = AbstractBeanDefinition.DEPENDENCY_CHECK_OBJECTS;
> }
> // Else leave default value.
> return dependencyCheckCode;
> }
>
> protected int getAutowireMode(String att) {
> int autowire = AbstractBeanDefinition.AUTOWIRE_NO;
> if (AUTOWIRE_BY_NAME_VALUE.equals(att)) {
> autowire = AbstractBeanDefinition.AUTOWIRE_BY_NAME;
> }
> else if (AUTOWIRE_BY_TYPE_VALUE.equals(att)) {
> autowire = Abstra...
[truncated message content] |
|
From: Eugene K. <eu...@ja...> - 2005-10-17 01:59:39
|
Hi Colin, Thanks for looking into this. Actually I've created a JIRA, which is not assigned to Jurgen. http://opensource2.atlassian.com/projects/spring/browse/SPR-1333 regards, Eugene Colin Sampaleanu wrote: > I managed to miss this when it was initially posted. We may want to > consider this for 1.3. > > > Eugene Kuleshov wrote: > >> Folks, >> >> I've implemented a sax-based parser for Spring's XML bean definition >> and wonder if you'd be interested to integrate this into Spring >> framework. this class can be used in place of the current DOM-based >> parser as well as an lightweight adapter for runtime app context >> generators such as xslt-based tools or tools like Jacn (even so I >> believe Jacn should directly generate Spring factories without >> intermediate XML representation). >> >> I run all existing tests from xml package and they all seem working >> just fine. That however required to have copies of >> XmlBeanDefinitionReaderand and XmlBeanFactory patched to work with my >> parser. >> >> In my tests this parser is little bit faster then current DOM-based >> parser and in my opinion it is structured little better then DOM-based >> parser. Basically is is sort-of stripped-down Digester (stack-based >> state machine) with declarative definitions for parsing rules. >> Interestingly the same rules could be used with StAX-based parser with >> very minimal changes or even without changes using adapter for SAX >> Attributes instance used in rule params. >> >> Also note that current XmlBeanDefinitionReader class is tight to >> DOM-based API and does not allow to hookup abstract XML parsers (e.g. >> SAX or StAX). This make impossible to substiture custom non-DOM XML >> parser to XmlBeanFactory. >> >> regards, >> Eugene |