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