001/*
002 * Copyright 2002-2017 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.beans.factory.config;
018
019import org.springframework.beans.factory.FactoryBean;
020import org.springframework.beans.factory.FactoryBeanNotInitializedException;
021
022/**
023 * {@link FactoryBean} which returns a value which is the result of a static or instance
024 * method invocation. For most use cases it is better to just use the container's
025 * built-in factory method support for the same purpose, since that is smarter at
026 * converting arguments. This factory bean is still useful though when you need to
027 * call a method which doesn't return any value (for example, a static class method
028 * to force some sort of initialization to happen). This use case is not supported
029 * by factory methods, since a return value is needed to obtain the bean instance.
030 *
031 * <p>Note that as it is expected to be used mostly for accessing factory methods,
032 * this factory by default operates in a <b>singleton</b> fashion. The first request
033 * to {@link #getObject} by the owning bean factory will cause a method invocation,
034 * whose return value will be cached for subsequent requests. An internal
035 * {@link #setSingleton singleton} property may be set to "false", to cause this
036 * factory to invoke the target method each time it is asked for an object.
037 *
038 * <p><b>NOTE: If your target method does not produce a result to expose, consider
039 * {@link MethodInvokingBean} instead, which avoids the type determination and
040 * lifecycle limitations that this {@link MethodInvokingFactoryBean} comes with.</b>
041 *
042 * <p>This invoker supports any kind of target method. A static method may be specified
043 * by setting the {@link #setTargetMethod targetMethod} property to a String representing
044 * the static method name, with {@link #setTargetClass targetClass} specifying the Class
045 * that the static method is defined on. Alternatively, a target instance method may be
046 * specified, by setting the {@link #setTargetObject targetObject} property as the target
047 * object, and the {@link #setTargetMethod targetMethod} property as the name of the
048 * method to call on that target object. Arguments for the method invocation may be
049 * specified by setting the {@link #setArguments arguments} property.
050 *
051 * <p>This class depends on {@link #afterPropertiesSet()} being called once
052 * all properties have been set, as per the InitializingBean contract.
053 *
054 * <p>An example (in an XML based bean factory definition) of a bean definition
055 * which uses this class to call a static factory method:
056 *
057 * <pre class="code">
058 * &lt;bean id="myObject" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
059 *   &lt;property name="staticMethod" value="com.whatever.MyClassFactory.getInstance"/>
060 * &lt;/bean></pre>
061 *
062 * <p>An example of calling a static method then an instance method to get at a
063 * Java system property. Somewhat verbose, but it works.
064 *
065 * <pre class="code">
066 * &lt;bean id="sysProps" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
067 *   &lt;property name="targetClass" value="java.lang.System"/>
068 *   &lt;property name="targetMethod" value="getProperties"/>
069 * &lt;/bean>
070 *
071 * &lt;bean id="javaVersion" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
072 *   &lt;property name="targetObject" ref="sysProps"/>
073 *   &lt;property name="targetMethod" value="getProperty"/>
074 *   &lt;property name="arguments" value="java.version"/>
075 * &lt;/bean></pre>
076 *
077 * @author Colin Sampaleanu
078 * @author Juergen Hoeller
079 * @since 21.11.2003
080 * @see MethodInvokingBean
081 * @see org.springframework.util.MethodInvoker
082 */
083public class MethodInvokingFactoryBean extends MethodInvokingBean implements FactoryBean<Object> {
084
085        private boolean singleton = true;
086
087        private boolean initialized = false;
088
089        /** Method call result in the singleton case */
090        private Object singletonObject;
091
092
093        /**
094         * Set if a singleton should be created, or a new object on each
095         * {@link #getObject()} request otherwise. Default is "true".
096         */
097        public void setSingleton(boolean singleton) {
098                this.singleton = singleton;
099        }
100
101        @Override
102        public void afterPropertiesSet() throws Exception {
103                prepare();
104                if (this.singleton) {
105                        this.initialized = true;
106                        this.singletonObject = invokeWithTargetException();
107                }
108        }
109
110
111        /**
112         * Returns the same value each time if the singleton property is set
113         * to "true", otherwise returns the value returned from invoking the
114         * specified method on the fly.
115         */
116        @Override
117        public Object getObject() throws Exception {
118                if (this.singleton) {
119                        if (!this.initialized) {
120                                throw new FactoryBeanNotInitializedException();
121                        }
122                        // Singleton: return shared object.
123                        return this.singletonObject;
124                }
125                else {
126                        // Prototype: new object on each call.
127                        return invokeWithTargetException();
128                }
129        }
130
131        /**
132         * Return the type of object that this FactoryBean creates,
133         * or {@code null} if not known in advance.
134         */
135        @Override
136        public Class<?> getObjectType() {
137                if (!isPrepared()) {
138                        // Not fully initialized yet -> return null to indicate "not known yet".
139                        return null;
140                }
141                return getPreparedMethod().getReturnType();
142        }
143
144        @Override
145        public boolean isSingleton() {
146                return this.singleton;
147        }
148
149}