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 java.io.Serializable;
020
021import javax.inject.Provider;
022
023import org.springframework.beans.BeansException;
024import org.springframework.beans.factory.BeanFactory;
025import org.springframework.lang.Nullable;
026import org.springframework.util.Assert;
027
028/**
029 * A {@link org.springframework.beans.factory.FactoryBean} implementation that
030 * returns a value which is a JSR-330 {@link javax.inject.Provider} that in turn
031 * returns a bean sourced from a {@link org.springframework.beans.factory.BeanFactory}.
032 *
033 * <p>This is basically a JSR-330 compliant variant of Spring's good old
034 * {@link ObjectFactoryCreatingFactoryBean}. It can be used for traditional
035 * external dependency injection configuration that targets a property or
036 * constructor argument of type {@code javax.inject.Provider}, as an
037 * alternative to JSR-330's {@code @Inject} annotation-driven approach.
038 *
039 * @author Juergen Hoeller
040 * @since 3.0.2
041 * @see javax.inject.Provider
042 * @see ObjectFactoryCreatingFactoryBean
043 */
044public class ProviderCreatingFactoryBean extends AbstractFactoryBean<Provider<Object>> {
045
046        @Nullable
047        private String targetBeanName;
048
049
050        /**
051         * Set the name of the target bean.
052         * <p>The target does not <i>have</i> to be a non-singleton bean, but realistically
053         * always will be (because if the target bean were a singleton, then said singleton
054         * bean could simply be injected straight into the dependent object, thus obviating
055         * the need for the extra level of indirection afforded by this factory approach).
056         */
057        public void setTargetBeanName(String targetBeanName) {
058                this.targetBeanName = targetBeanName;
059        }
060
061        @Override
062        public void afterPropertiesSet() throws Exception {
063                Assert.hasText(this.targetBeanName, "Property 'targetBeanName' is required");
064                super.afterPropertiesSet();
065        }
066
067
068        @Override
069        public Class<?> getObjectType() {
070                return Provider.class;
071        }
072
073        @Override
074        protected Provider<Object> createInstance() {
075                BeanFactory beanFactory = getBeanFactory();
076                Assert.state(beanFactory != null, "No BeanFactory available");
077                Assert.state(this.targetBeanName != null, "No target bean name specified");
078                return new TargetBeanProvider(beanFactory, this.targetBeanName);
079        }
080
081
082        /**
083         * Independent inner class - for serialization purposes.
084         */
085        @SuppressWarnings("serial")
086        private static class TargetBeanProvider implements Provider<Object>, Serializable {
087
088                private final BeanFactory beanFactory;
089
090                private final String targetBeanName;
091
092                public TargetBeanProvider(BeanFactory beanFactory, String targetBeanName) {
093                        this.beanFactory = beanFactory;
094                        this.targetBeanName = targetBeanName;
095                }
096
097                @Override
098                public Object get() throws BeansException {
099                        return this.beanFactory.getBean(this.targetBeanName);
100                }
101        }
102
103}