Prechádzať zdrojové kódy

resolve #103 - rudimentary support for Spring DI

Ranides Atterwim 1 rok pred
rodič
commit
47424d3dbd

+ 26 - 0
assira.core/src/main/java/net/ranides/assira/system/HostContext.java

@@ -0,0 +1,26 @@
+package net.ranides.assira.system;
+
+import net.ranides.assira.reflection.IClass;
+import net.ranides.assira.system.impl.HostSpringContext;
+
+public interface HostContext {
+
+    Object getBean(String name);
+
+    <T> T getBean(Class<T> type);
+
+    boolean isPrototype(String name);
+
+    boolean isSingleton(String name);
+
+    static HostContext from(Object provider) {
+        if(provider == null) {
+            return null;
+        }
+        if(IClass.typefor(provider).name().contains("org.springframework")) {
+            return new HostSpringContext(provider);
+        }
+        throw new UnsupportedOperationException("Unsupported context provider: " + provider);
+    }
+
+}

+ 464 - 0
assira.core/src/main/java/net/ranides/assira/system/impl/HostSpringContext.java

@@ -0,0 +1,464 @@
+package net.ranides.assira.system.impl;
+
+import net.ranides.assira.reflection.AdapterFactory;
+import net.ranides.assira.system.HostContext;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.Set;
+
+public class HostSpringContext implements HostContext {
+
+    private final IApplicationContext delegate;
+
+    public HostSpringContext(Object provider) {
+        delegate = AdapterFactory.getSystem().cast(IApplicationContext.class, provider);
+    }
+
+    @Override
+    public Object getBean(String name) {
+        return delegate.getBean(name);
+    }
+
+    @Override
+    public <T> T getBean(Class<T> type) {
+        return delegate.getBean(type);
+    }
+
+    @Override
+    public boolean isPrototype(String name) {
+        return delegate.isPrototype(name);
+    }
+
+    @Override
+    public boolean isSingleton(String name) {
+        return delegate.isSingleton(name);
+    }
+
+    /**
+     *
+     * @noinspection JavadocReference
+     */
+    public interface IApplicationContext {
+
+        /**
+         * Return the names of beans matching the given type (including subclasses),
+         * judging from either bean definitions or the value of {@code getObjectType}
+         * in the case of FactoryBeans.
+         * <p><b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i>
+         * check nested beans which might match the specified type as well.
+         * <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
+         * will get initialized. If the object created by the FactoryBean doesn't match,
+         * the raw FactoryBean itself will be matched against the type.
+         * <p>Does not consider any hierarchy this factory may participate in.
+         * Use BeanFactoryUtils' {@code beanNamesForTypeIncludingAncestors}
+         * to include beans in ancestor factories too.
+         * <p>This version of {@code getBeanNamesForType} matches all kinds of beans,
+         * be it singletons, prototypes, or FactoryBeans. In most implementations, the
+         * result will be the same as for {@code getBeanNamesForType(type, true, true)}.
+         * <p>Bean names returned by this method should always return bean names <i>in the
+         * order of definition</i> in the backend configuration, as far as possible.
+         * @param type the class or interface to match, or {@code null} for all bean names
+         * @return the names of beans (or objects created by FactoryBeans) matching
+         * the given object type (including subclasses), or an empty array if none
+         * @see FactoryBean#getObjectType
+         * @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class)
+         */
+        String[] getBeanNamesForType(Class<?> type);
+
+        /**
+         * Return the names of beans matching the given type (including subclasses),
+         * judging from either bean definitions or the value of {@code getObjectType}
+         * in the case of FactoryBeans.
+         * <p><b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i>
+         * check nested beans which might match the specified type as well.
+         * <p>Does consider objects created by FactoryBeans if the "allowEagerInit" flag is set,
+         * which means that FactoryBeans will get initialized. If the object created by the
+         * FactoryBean doesn't match, the raw FactoryBean itself will be matched against the
+         * type. If "allowEagerInit" is not set, only raw FactoryBeans will be checked
+         * (which doesn't require initialization of each FactoryBean).
+         * <p>Does not consider any hierarchy this factory may participate in.
+         * Use BeanFactoryUtils' {@code beanNamesForTypeIncludingAncestors}
+         * to include beans in ancestor factories too.
+         * <p>Bean names returned by this method should always return bean names <i>in the
+         * order of definition</i> in the backend configuration, as far as possible.
+         * @param type the class or interface to match, or {@code null} for all bean names
+         * @param includeNonSingletons whether to include prototype or scoped beans too
+         * or just singletons (also applies to FactoryBeans)
+         * @param allowEagerInit whether to introspect <i>lazy-init singletons</i>
+         * and <i>objects created by FactoryBeans</i> - or by factory methods with a
+         * "factory-bean" reference - for the type check. Note that FactoryBeans need to be
+         * eagerly initialized to determine their type: So be aware that passing in "true"
+         * for this flag will initialize FactoryBeans and "factory-bean" references. Only
+         * actually necessary initialization for type checking purposes will be performed;
+         * constructor and method invocations will still be avoided as far as possible.
+         * @return the names of beans (or objects created by FactoryBeans) matching
+         * the given object type (including subclasses), or an empty array if none
+         * @see FactoryBean#getObjectType
+         * @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean)
+         */
+        String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
+
+        /**
+         * Return the bean instances that match the given object type (including
+         * subclasses), judging from either bean definitions or the value of
+         * {@code getObjectType} in the case of FactoryBeans.
+         * <p><b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i>
+         * check nested beans which might match the specified type as well. Also, it
+         * <b>suppresses exceptions for beans that are currently in creation in a circular
+         * reference scenario:</b> typically, references back to the caller of this method.
+         * <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
+         * will get initialized. If the object created by the FactoryBean doesn't match,
+         * the raw FactoryBean itself will be matched against the type.
+         * <p>Does not consider any hierarchy this factory may participate in.
+         * Use BeanFactoryUtils' {@code beansOfTypeIncludingAncestors}
+         * to include beans in ancestor factories too.
+         * <p>This version of getBeansOfType matches all kinds of beans, be it
+         * singletons, prototypes, or FactoryBeans. In most implementations, the
+         * result will be the same as for {@code getBeansOfType(type, true, true)}.
+         * <p>The Map returned by this method should always return bean names and
+         * corresponding bean instances <i>in the order of definition</i> in the
+         * backend configuration, as far as possible.
+         * <p><b>Consider {@link #getBeanNamesForType(Class)} with selective {@link #getBean}
+         * calls for specific bean names in preference to this Map-based retrieval method.</b>
+         * Aside from lazy instantiation benefits, this also avoids any exception suppression.
+         * @param type the class or interface to match, or {@code null} for all concrete beans
+         * @return a Map with the matching beans, containing the bean names as
+         * keys and the corresponding bean instances as values
+         * @throws BeansException if a bean could not be created
+         * @since 1.1.2
+         * @see FactoryBean#getObjectType
+         * @see BeanFactoryUtils#beansOfTypeIncludingAncestors(ListableBeanFactory, Class)
+         */
+        <T> Map<String, T> getBeansOfType(Class<T> type);
+
+        /**
+         * Return the bean instances that match the given object type (including
+         * subclasses), judging from either bean definitions or the value of
+         * {@code getObjectType} in the case of FactoryBeans.
+         * <p><b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i>
+         * check nested beans which might match the specified type as well. Also, it
+         * <b>suppresses exceptions for beans that are currently in creation in a circular
+         * reference scenario:</b> typically, references back to the caller of this method.
+         * <p>Does consider objects created by FactoryBeans if the "allowEagerInit" flag is set,
+         * which means that FactoryBeans will get initialized. If the object created by the
+         * FactoryBean doesn't match, the raw FactoryBean itself will be matched against the
+         * type. If "allowEagerInit" is not set, only raw FactoryBeans will be checked
+         * (which doesn't require initialization of each FactoryBean).
+         * <p>Does not consider any hierarchy this factory may participate in.
+         * Use BeanFactoryUtils' {@code beansOfTypeIncludingAncestors}
+         * to include beans in ancestor factories too.
+         * <p>The Map returned by this method should always return bean names and
+         * corresponding bean instances <i>in the order of definition</i> in the
+         * backend configuration, as far as possible.
+         * <p><b>Consider {@link #getBeanNamesForType(Class)} with selective {@link #getBean}
+         * calls for specific bean names in preference to this Map-based retrieval method.</b>
+         * Aside from lazy instantiation benefits, this also avoids any exception suppression.
+         * @param type the class or interface to match, or {@code null} for all concrete beans
+         * @param includeNonSingletons whether to include prototype or scoped beans too
+         * or just singletons (also applies to FactoryBeans)
+         * @param allowEagerInit whether to introspect <i>lazy-init singletons</i>
+         * and <i>objects created by FactoryBeans</i> - or by factory methods with a
+         * "factory-bean" reference - for the type check. Note that FactoryBeans need to be
+         * eagerly initialized to determine their type: So be aware that passing in "true"
+         * for this flag will initialize FactoryBeans and "factory-bean" references. Only
+         * actually necessary initialization for type checking purposes will be performed;
+         * constructor and method invocations will still be avoided as far as possible.
+         * @return a Map with the matching beans, containing the bean names as
+         * keys and the corresponding bean instances as values
+         * @throws BeansException if a bean could not be created
+         * @see FactoryBean#getObjectType
+         * @see BeanFactoryUtils#beansOfTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean)
+         */
+        <T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons, boolean allowEagerInit);
+
+        /**
+         * Find all names of beans which are annotated with the supplied {@link Annotation}
+         * type, without creating corresponding bean instances yet.
+         * <p>Note that this method considers objects created by FactoryBeans, which means
+         * that FactoryBeans will get initialized in order to determine their object type.
+         * @param annotationType the type of annotation to look for
+         * (at class, interface or factory method level of the specified bean)
+         * @return the names of all matching beans
+         * @since 4.0
+         * @see #getBeansWithAnnotation(Class)
+         * @see #findAnnotationOnBean(String, Class)
+         */
+        String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
+
+        /**
+         * Find all beans which are annotated with the supplied {@link Annotation} type,
+         * returning a Map of bean names with corresponding bean instances.
+         * <p>Note that this method considers objects created by FactoryBeans, which means
+         * that FactoryBeans will get initialized in order to determine their object type.
+         * @param annotationType the type of annotation to look for
+         * (at class, interface or factory method level of the specified bean)
+         * @return a Map with the matching beans, containing the bean names as
+         * keys and the corresponding bean instances as values
+         * @throws BeansException if a bean could not be created
+         * @since 3.0
+         * @see #findAnnotationOnBean(String, Class)
+         * @see #findAnnotationOnBean(String, Class, boolean)
+         * @see #findAllAnnotationsOnBean(String, Class, boolean)
+         */
+        Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType);
+
+        /**
+         * Find an {@link Annotation} of {@code annotationType} on the specified bean,
+         * traversing its interfaces and superclasses if no annotation can be found on
+         * the given class itself, as well as checking the bean's factory method (if any).
+         * @param beanName the name of the bean to look for annotations on
+         * @param annotationType the type of annotation to look for
+         * (at class, interface or factory method level of the specified bean)
+         * @return the annotation of the given type if found, or {@code null} otherwise
+         * @throws NoSuchBeanDefinitionException if there is no bean with the given name
+         * @since 3.0
+         * @see #findAnnotationOnBean(String, Class, boolean)
+         * @see #findAllAnnotationsOnBean(String, Class, boolean)
+         * @see #getBeanNamesForAnnotation(Class)
+         * @see #getBeansWithAnnotation(Class)
+         * @see #getType(String)
+         */
+        <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType);
+
+        /**
+         * Find an {@link Annotation} of {@code annotationType} on the specified bean,
+         * traversing its interfaces and superclasses if no annotation can be found on
+         * the given class itself, as well as checking the bean's factory method (if any).
+         * @param beanName the name of the bean to look for annotations on
+         * @param annotationType the type of annotation to look for
+         * (at class, interface or factory method level of the specified bean)
+         * @param allowFactoryBeanInit whether a {@code FactoryBean} may get initialized
+         * just for the purpose of determining its object type
+         * @return the annotation of the given type if found, or {@code null} otherwise
+         * @throws NoSuchBeanDefinitionException if there is no bean with the given name
+         * @since 5.3.14
+         * @see #findAnnotationOnBean(String, Class)
+         * @see #findAllAnnotationsOnBean(String, Class, boolean)
+         * @see #getBeanNamesForAnnotation(Class)
+         * @see #getBeansWithAnnotation(Class)
+         * @see #getType(String, boolean)
+         */
+        <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType, boolean allowFactoryBeanInit);
+
+        /**
+         * Find all {@link Annotation} instances of {@code annotationType} on the specified
+         * bean, traversing its interfaces and superclasses if no annotation can be found on
+         * the given class itself, as well as checking the bean's factory method (if any).
+         * @param beanName the name of the bean to look for annotations on
+         * @param annotationType the type of annotation to look for
+         * (at class, interface or factory method level of the specified bean)
+         * @param allowFactoryBeanInit whether a {@code FactoryBean} may get initialized
+         * just for the purpose of determining its object type
+         * @return the set of annotations of the given type found (potentially empty)
+         * @throws NoSuchBeanDefinitionException if there is no bean with the given name
+         * @since 6.0
+         * @see #getBeanNamesForAnnotation(Class)
+         * @see #findAnnotationOnBean(String, Class, boolean)
+         * @see #getType(String, boolean)
+         */
+        <A extends Annotation> Set<A> findAllAnnotationsOnBean(
+            String beanName, Class<A> annotationType, boolean allowFactoryBeanInit);
+
+        /**
+         * Return an instance, which may be shared or independent, of the specified bean.
+         * <p>This method allows a Spring BeanFactory to be used as a replacement for the
+         * Singleton or Prototype design pattern. Callers may retain references to
+         * returned objects in the case of Singleton beans.
+         * <p>Translates aliases back to the corresponding canonical bean name.
+         * <p>Will ask the parent factory if the bean cannot be found in this factory instance.
+         * @param name the name of the bean to retrieve
+         * @return an instance of the bean.
+         * Note that the return value will never be {@code null} but possibly a stub for
+         * {@code null} returned from a factory method, to be checked via {@code equals(null)}.
+         * Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies.
+         * @throws NoSuchBeanDefinitionException if there is no bean with the specified name
+         * @throws BeansException if the bean could not be obtained
+         */
+        Object getBean(String name);
+
+        /**
+         * Return an instance, which may be shared or independent, of the specified bean.
+         * <p>Behaves the same as {@link #getBean(String)}, but provides a measure of type
+         * safety by throwing a BeanNotOfRequiredTypeException if the bean is not of the
+         * required type. This means that ClassCastException can't be thrown on casting
+         * the result correctly, as can happen with {@link #getBean(String)}.
+         * <p>Translates aliases back to the corresponding canonical bean name.
+         * <p>Will ask the parent factory if the bean cannot be found in this factory instance.
+         * @param name the name of the bean to retrieve
+         * @param requiredType type the bean must match; can be an interface or superclass
+         * @return an instance of the bean.
+         * Note that the return value will never be {@code null}. In case of a stub for
+         * {@code null} from a factory method having been resolved for the requested bean, a
+         * {@code BeanNotOfRequiredTypeException} against the NullBean stub will be raised.
+         * Consider using {@link #getBeanProvider(Class)} for resolving optional dependencies.
+         * @throws NoSuchBeanDefinitionException if there is no such bean definition
+         * @throws BeanNotOfRequiredTypeException if the bean is not of the required type
+         * @throws BeansException if the bean could not be created
+         */
+        <T> T getBean(String name, Class<T> requiredType);
+
+        /**
+         * Return an instance, which may be shared or independent, of the specified bean.
+         * <p>Allows for specifying explicit constructor arguments / factory method arguments,
+         * overriding the specified default arguments (if any) in the bean definition.
+         * Note that the provided arguments need to match a specific candidate constructor /
+         * factory method in the order of declared parameters.
+         * @param name the name of the bean to retrieve
+         * @param args arguments to use when creating a bean instance using explicit arguments
+         * (only applied when creating a new instance as opposed to retrieving an existing one)
+         * @return an instance of the bean
+         * @throws NoSuchBeanDefinitionException if there is no such bean definition
+         * @throws BeanDefinitionStoreException if arguments have been given but
+         * the affected bean isn't a prototype
+         * @throws BeansException if the bean could not be created
+         * @since 2.5
+         */
+        Object getBean(String name, Object... args);
+
+        /**
+         * Return the bean instance that uniquely matches the given object type, if any.
+         * <p>This method goes into {@link ListableBeanFactory} by-type lookup territory
+         * but may also be translated into a conventional by-name lookup based on the name
+         * of the given type. For more extensive retrieval operations across sets of beans,
+         * use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}.
+         * @param requiredType type the bean must match; can be an interface or superclass
+         * @return an instance of the single bean matching the required type
+         * @throws NoSuchBeanDefinitionException if no bean of the given type was found
+         * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
+         * @throws BeansException if the bean could not be created
+         * @since 3.0
+         * @see ListableBeanFactory
+         */
+        <T> T getBean(Class<T> requiredType);
+
+        /**
+         * Return an instance, which may be shared or independent, of the specified bean.
+         * <p>Allows for specifying explicit constructor arguments / factory method arguments,
+         * overriding the specified default arguments (if any) in the bean definition.
+         * Note that the provided arguments need to match a specific candidate constructor /
+         * factory method in the order of declared parameters.
+         * <p>This method goes into {@link ListableBeanFactory} by-type lookup territory
+         * but may also be translated into a conventional by-name lookup based on the name
+         * of the given type. For more extensive retrieval operations across sets of beans,
+         * use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}.
+         * @param requiredType type the bean must match; can be an interface or superclass
+         * @param args arguments to use when creating a bean instance using explicit arguments
+         * (only applied when creating a new instance as opposed to retrieving an existing one)
+         * @return an instance of the bean
+         * @throws NoSuchBeanDefinitionException if there is no such bean definition
+         * @throws BeanDefinitionStoreException if arguments have been given but
+         * the affected bean isn't a prototype
+         * @throws BeansException if the bean could not be created
+         * @since 4.1
+         */
+        <T> T getBean(Class<T> requiredType, Object... args);
+
+        /**
+         * Does this bean factory contain a bean definition or externally registered singleton
+         * instance with the given name?
+         * <p>If the given name is an alias, it will be translated back to the corresponding
+         * canonical bean name.
+         * <p>If this factory is hierarchical, will ask any parent factory if the bean cannot
+         * be found in this factory instance.
+         * <p>If a bean definition or singleton instance matching the given name is found,
+         * this method will return {@code true} whether the named bean definition is concrete
+         * or abstract, lazy or eager, in scope or not. Therefore, note that a {@code true}
+         * return value from this method does not necessarily indicate that {@link #getBean}
+         * will be able to obtain an instance for the same name.
+         * @param name the name of the bean to query
+         * @return whether a bean with the given name is present
+         */
+        boolean containsBean(String name);
+
+        /**
+         * Is this bean a shared singleton? That is, will {@link #getBean} always
+         * return the same instance?
+         * <p>Note: This method returning {@code false} does not clearly indicate
+         * independent instances. It indicates non-singleton instances, which may correspond
+         * to a scoped bean as well. Use the {@link #isPrototype} operation to explicitly
+         * check for independent instances.
+         * <p>Translates aliases back to the corresponding canonical bean name.
+         * <p>Will ask the parent factory if the bean cannot be found in this factory instance.
+         * @param name the name of the bean to query
+         * @return whether this bean corresponds to a singleton instance
+         * @throws NoSuchBeanDefinitionException if there is no bean with the given name
+         * @see #getBean
+         * @see #isPrototype
+         */
+        boolean isSingleton(String name);
+
+        /**
+         * Is this bean a prototype? That is, will {@link #getBean} always return
+         * independent instances?
+         * <p>Note: This method returning {@code false} does not clearly indicate
+         * a singleton object. It indicates non-independent instances, which may correspond
+         * to a scoped bean as well. Use the {@link #isSingleton} operation to explicitly
+         * check for a shared singleton instance.
+         * <p>Translates aliases back to the corresponding canonical bean name.
+         * <p>Will ask the parent factory if the bean cannot be found in this factory instance.
+         * @param name the name of the bean to query
+         * @return whether this bean will always deliver independent instances
+         * @throws NoSuchBeanDefinitionException if there is no bean with the given name
+         * @since 2.0.3
+         * @see #getBean
+         * @see #isSingleton
+         */
+        boolean isPrototype(String name);
+
+        /**
+         * Check whether the bean with the given name matches the specified type.
+         * More specifically, check whether a {@link #getBean} call for the given name
+         * would return an object that is assignable to the specified target type.
+         * <p>Translates aliases back to the corresponding canonical bean name.
+         * <p>Will ask the parent factory if the bean cannot be found in this factory instance.
+         * @param name the name of the bean to query
+         * @param typeToMatch the type to match against (as a {@code Class})
+         * @return {@code true} if the bean type matches,
+         * {@code false} if it doesn't match or cannot be determined yet
+         * @throws NoSuchBeanDefinitionException if there is no bean with the given name
+         * @since 2.0.1
+         * @see #getBean
+         * @see #getType
+         */
+        boolean isTypeMatch(String name, Class<?> typeToMatch);
+
+        /**
+         * Determine the type of the bean with the given name. More specifically,
+         * determine the type of object that {@link #getBean} would return for the given name.
+         * <p>For a {@link FactoryBean}, return the type of object that the FactoryBean creates,
+         * as exposed by {@link FactoryBean#getObjectType()}. This may lead to the initialization
+         * of a previously uninitialized {@code FactoryBean} (see {@link #getType(String, boolean)}).
+         * <p>Translates aliases back to the corresponding canonical bean name.
+         * <p>Will ask the parent factory if the bean cannot be found in this factory instance.
+         * @param name the name of the bean to query
+         * @return the type of the bean, or {@code null} if not determinable
+         * @throws NoSuchBeanDefinitionException if there is no bean with the given name
+         * @since 1.1.2
+         * @see #getBean
+         * @see #isTypeMatch
+         */
+        Class<?> getType(String name);
+
+        /**
+         * Determine the type of the bean with the given name. More specifically,
+         * determine the type of object that {@link #getBean} would return for the given name.
+         * <p>For a {@link FactoryBean}, return the type of object that the FactoryBean creates,
+         * as exposed by {@link FactoryBean#getObjectType()}. Depending on the
+         * {@code allowFactoryBeanInit} flag, this may lead to the initialization of a previously
+         * uninitialized {@code FactoryBean} if no early type information is available.
+         * <p>Translates aliases back to the corresponding canonical bean name.
+         * <p>Will ask the parent factory if the bean cannot be found in this factory instance.
+         * @param name the name of the bean to query
+         * @param allowFactoryBeanInit whether a {@code FactoryBean} may get initialized
+         * just for the purpose of determining its object type
+         * @return the type of the bean, or {@code null} if not determinable
+         * @throws NoSuchBeanDefinitionException if there is no bean with the given name
+         * @since 5.2
+         * @see #getBean
+         * @see #isTypeMatch
+         */
+        Class<?> getType(String name, boolean allowFactoryBeanInit);
+
+    }
+}