001/*
002 * Copyright 2002-2020 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.context.annotation;
018
019import java.lang.annotation.Annotation;
020import java.lang.reflect.Method;
021import java.util.Collection;
022import java.util.Collections;
023import java.util.LinkedHashSet;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027
028import org.springframework.aop.TargetSource;
029import org.springframework.aop.framework.ProxyFactory;
030import org.springframework.beans.factory.BeanFactory;
031import org.springframework.beans.factory.NoSuchBeanDefinitionException;
032import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
033import org.springframework.beans.factory.config.DependencyDescriptor;
034import org.springframework.beans.factory.support.DefaultListableBeanFactory;
035import org.springframework.core.MethodParameter;
036import org.springframework.core.annotation.AnnotationUtils;
037import org.springframework.lang.Nullable;
038import org.springframework.util.Assert;
039
040/**
041 * Complete implementation of the
042 * {@link org.springframework.beans.factory.support.AutowireCandidateResolver} strategy
043 * interface, providing support for qualifier annotations as well as for lazy resolution
044 * driven by the {@link Lazy} annotation in the {@code context.annotation} package.
045 *
046 * @author Juergen Hoeller
047 * @since 4.0
048 */
049public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver {
050
051        @Override
052        @Nullable
053        public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
054                return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
055        }
056
057        protected boolean isLazy(DependencyDescriptor descriptor) {
058                for (Annotation ann : descriptor.getAnnotations()) {
059                        Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
060                        if (lazy != null && lazy.value()) {
061                                return true;
062                        }
063                }
064                MethodParameter methodParam = descriptor.getMethodParameter();
065                if (methodParam != null) {
066                        Method method = methodParam.getMethod();
067                        if (method == null || void.class == method.getReturnType()) {
068                                Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class);
069                                if (lazy != null && lazy.value()) {
070                                        return true;
071                                }
072                        }
073                }
074                return false;
075        }
076
077        protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
078                BeanFactory beanFactory = getBeanFactory();
079                Assert.state(beanFactory instanceof DefaultListableBeanFactory,
080                                "BeanFactory needs to be a DefaultListableBeanFactory");
081                final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;
082
083                TargetSource ts = new TargetSource() {
084                        @Override
085                        public Class<?> getTargetClass() {
086                                return descriptor.getDependencyType();
087                        }
088                        @Override
089                        public boolean isStatic() {
090                                return false;
091                        }
092                        @Override
093                        public Object getTarget() {
094                                Set<String> autowiredBeanNames = (beanName != null ? new LinkedHashSet<>(1) : null);
095                                Object target = dlbf.doResolveDependency(descriptor, beanName, autowiredBeanNames, null);
096                                if (target == null) {
097                                        Class<?> type = getTargetClass();
098                                        if (Map.class == type) {
099                                                return Collections.emptyMap();
100                                        }
101                                        else if (List.class == type) {
102                                                return Collections.emptyList();
103                                        }
104                                        else if (Set.class == type || Collection.class == type) {
105                                                return Collections.emptySet();
106                                        }
107                                        throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
108                                                        "Optional dependency not present for lazy injection point");
109                                }
110                                if (autowiredBeanNames != null) {
111                                        for (String autowiredBeanName : autowiredBeanNames) {
112                                                if (dlbf.containsBean(autowiredBeanName)) {
113                                                        dlbf.registerDependentBean(autowiredBeanName, beanName);
114                                                }
115                                        }
116                                }
117                                return target;
118                        }
119                        @Override
120                        public void releaseTarget(Object target) {
121                        }
122                };
123
124                ProxyFactory pf = new ProxyFactory();
125                pf.setTargetSource(ts);
126                Class<?> dependencyType = descriptor.getDependencyType();
127                if (dependencyType.isInterface()) {
128                        pf.addInterface(dependencyType);
129                }
130                return pf.getProxy(dlbf.getBeanClassLoader());
131        }
132
133}