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