001/*
002 * Copyright 2002-2012 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.util.HashMap;
020import java.util.Map;
021import java.util.Set;
022
023import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
024import org.springframework.beans.factory.config.BeanDefinition;
025
026/**
027 * Simple {@link ScopeMetadataResolver} implementation that follows JSR-330 scoping rules:
028 * defaulting to prototype scope unless {@link javax.inject.Singleton} is present.
029 *
030 * <p>This scope resolver can be used with {@link ClassPathBeanDefinitionScanner} and
031 * {@link AnnotatedBeanDefinitionReader} for standard JSR-330 compliance. However,
032 * in practice, you will typically use Spring's rich default scoping instead - or extend
033 * this resolver with custom scoping annotations that point to extended Spring scopes.
034 *
035 * @author Juergen Hoeller
036 * @since 3.0
037 * @see #registerScope
038 * @see #resolveScopeName
039 * @see ClassPathBeanDefinitionScanner#setScopeMetadataResolver
040 * @see AnnotatedBeanDefinitionReader#setScopeMetadataResolver
041 */
042public class Jsr330ScopeMetadataResolver implements ScopeMetadataResolver {
043
044        private final Map<String, String> scopeMap = new HashMap<String, String>();
045
046
047        public Jsr330ScopeMetadataResolver() {
048                registerScope("javax.inject.Singleton", BeanDefinition.SCOPE_SINGLETON);
049        }
050
051
052        /**
053         * Register an extended JSR-330 scope annotation, mapping it onto a
054         * specific Spring scope by name.
055         * @param annotationType the JSR-330 annotation type as a Class
056         * @param scopeName the Spring scope name
057         */
058        public final void registerScope(Class<?> annotationType, String scopeName) {
059                this.scopeMap.put(annotationType.getName(), scopeName);
060        }
061
062        /**
063         * Register an extended JSR-330 scope annotation, mapping it onto a
064         * specific Spring scope by name.
065         * @param annotationType the JSR-330 annotation type by name
066         * @param scopeName the Spring scope name
067         */
068        public final void registerScope(String annotationType, String scopeName) {
069                this.scopeMap.put(annotationType, scopeName);
070        }
071
072        /**
073         * Resolve the given annotation type into a named Spring scope.
074         * <p>The default implementation simply checks against registered scopes.
075         * Can be overridden for custom mapping rules, e.g. naming conventions.
076         * @param annotationType the JSR-330 annotation type
077         * @return the Spring scope name
078         */
079        protected String resolveScopeName(String annotationType) {
080                return this.scopeMap.get(annotationType);
081        }
082
083
084        @Override
085        public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
086                ScopeMetadata metadata = new ScopeMetadata();
087                metadata.setScopeName(BeanDefinition.SCOPE_PROTOTYPE);
088                if (definition instanceof AnnotatedBeanDefinition) {
089                        AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
090                        Set<String> annTypes = annDef.getMetadata().getAnnotationTypes();
091                        String found = null;
092                        for (String annType : annTypes) {
093                                Set<String> metaAnns = annDef.getMetadata().getMetaAnnotationTypes(annType);
094                                if (metaAnns.contains("javax.inject.Scope")) {
095                                        if (found != null) {
096                                                throw new IllegalStateException("Found ambiguous scope annotations on bean class [" +
097                                                                definition.getBeanClassName() + "]: " + found + ", " + annType);
098                                        }
099                                        found = annType;
100                                        String scopeName = resolveScopeName(annType);
101                                        if (scopeName == null) {
102                                                throw new IllegalStateException(
103                                                                "Unsupported scope annotation - not mapped onto Spring scope name: " + annType);
104                                        }
105                                        metadata.setScopeName(scopeName);
106                                }
107                        }
108                }
109                return metadata;
110        }
111
112}