001/* 002 * Copyright 2012-2018 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 * http://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.boot.web.reactive.context; 018 019import java.util.Arrays; 020import java.util.LinkedHashSet; 021import java.util.Set; 022 023import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 024import org.springframework.beans.factory.support.BeanNameGenerator; 025import org.springframework.beans.factory.support.DefaultListableBeanFactory; 026import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; 027import org.springframework.context.annotation.AnnotationConfigApplicationContext; 028import org.springframework.context.annotation.AnnotationConfigRegistry; 029import org.springframework.context.annotation.AnnotationConfigUtils; 030import org.springframework.context.annotation.AnnotationScopeMetadataResolver; 031import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; 032import org.springframework.context.annotation.ScopeMetadataResolver; 033import org.springframework.core.env.ConfigurableEnvironment; 034import org.springframework.stereotype.Component; 035import org.springframework.util.Assert; 036import org.springframework.util.ClassUtils; 037import org.springframework.util.ObjectUtils; 038 039/** 040 * {@link ReactiveWebServerApplicationContext} that accepts annotated classes as input - 041 * in particular 042 * {@link org.springframework.context.annotation.Configuration @Configuration}-annotated 043 * classes, but also plain {@link Component @Component} classes and JSR-330 compliant 044 * classes using {@code javax.inject} annotations. Allows for registering classes one by 045 * one (specifying class names as config location) as well as for classpath scanning 046 * (specifying base packages as config location). 047 * <p> 048 * Note: In case of multiple {@code @Configuration} classes, later {@code @Bean} 049 * definitions will override ones defined in earlier loaded files. This can be leveraged 050 * to deliberately override certain bean definitions via an extra Configuration class. 051 * 052 * @author Phillip Webb 053 * @since 2.0.0 054 * @see #register(Class...) 055 * @see #scan(String...) 056 * @see ReactiveWebServerApplicationContext 057 * @see AnnotationConfigApplicationContext 058 */ 059public class AnnotationConfigReactiveWebServerApplicationContext 060 extends ReactiveWebServerApplicationContext implements AnnotationConfigRegistry { 061 062 private final AnnotatedBeanDefinitionReader reader; 063 064 private final ClassPathBeanDefinitionScanner scanner; 065 066 private final Set<Class<?>> annotatedClasses = new LinkedHashSet<>(); 067 068 private String[] basePackages; 069 070 /** 071 * Create a new {@link AnnotationConfigReactiveWebServerApplicationContext} that needs 072 * to be populated through {@link #register} calls and then manually 073 * {@linkplain #refresh refreshed}. 074 */ 075 public AnnotationConfigReactiveWebServerApplicationContext() { 076 this.reader = new AnnotatedBeanDefinitionReader(this); 077 this.scanner = new ClassPathBeanDefinitionScanner(this); 078 } 079 080 /** 081 * Create a new {@link AnnotationConfigReactiveWebServerApplicationContext} with the 082 * given {@code DefaultListableBeanFactory}. The context needs to be populated through 083 * {@link #register} calls and then manually {@linkplain #refresh refreshed}. 084 * @param beanFactory the DefaultListableBeanFactory instance to use for this context 085 */ 086 public AnnotationConfigReactiveWebServerApplicationContext( 087 DefaultListableBeanFactory beanFactory) { 088 super(beanFactory); 089 this.reader = new AnnotatedBeanDefinitionReader(this); 090 this.scanner = new ClassPathBeanDefinitionScanner(this); 091 } 092 093 /** 094 * Create a new {@link AnnotationConfigReactiveWebServerApplicationContext}, deriving 095 * bean definitions from the given annotated classes and automatically refreshing the 096 * context. 097 * @param annotatedClasses one or more annotated classes, e.g. {@code @Configuration} 098 * classes 099 */ 100 public AnnotationConfigReactiveWebServerApplicationContext( 101 Class<?>... annotatedClasses) { 102 this(); 103 register(annotatedClasses); 104 refresh(); 105 } 106 107 /** 108 * Create a new {@link AnnotationConfigReactiveWebServerApplicationContext}, scanning 109 * for bean definitions in the given packages and automatically refreshing the 110 * context. 111 * @param basePackages the packages to check for annotated classes 112 */ 113 public AnnotationConfigReactiveWebServerApplicationContext(String... basePackages) { 114 this(); 115 scan(basePackages); 116 refresh(); 117 } 118 119 /** 120 * {@inheritDoc} 121 * <p> 122 * Delegates given environment to underlying {@link AnnotatedBeanDefinitionReader} and 123 * {@link ClassPathBeanDefinitionScanner} members. 124 */ 125 @Override 126 public void setEnvironment(ConfigurableEnvironment environment) { 127 super.setEnvironment(environment); 128 this.reader.setEnvironment(environment); 129 this.scanner.setEnvironment(environment); 130 } 131 132 /** 133 * Provide a custom {@link BeanNameGenerator} for use with 134 * {@link AnnotatedBeanDefinitionReader} and/or 135 * {@link ClassPathBeanDefinitionScanner}, if any. 136 * <p> 137 * Default is 138 * {@link org.springframework.context.annotation.AnnotationBeanNameGenerator}. 139 * <p> 140 * Any call to this method must occur prior to calls to {@link #register(Class...)} 141 * and/or {@link #scan(String...)}. 142 * @param beanNameGenerator the bean name generator 143 * @see AnnotatedBeanDefinitionReader#setBeanNameGenerator 144 * @see ClassPathBeanDefinitionScanner#setBeanNameGenerator 145 */ 146 public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) { 147 this.reader.setBeanNameGenerator(beanNameGenerator); 148 this.scanner.setBeanNameGenerator(beanNameGenerator); 149 this.getBeanFactory().registerSingleton( 150 AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, 151 beanNameGenerator); 152 } 153 154 /** 155 * Set the {@link ScopeMetadataResolver} to use for detected bean classes. 156 * <p> 157 * The default is an {@link AnnotationScopeMetadataResolver}. 158 * <p> 159 * Any call to this method must occur prior to calls to {@link #register(Class...)} 160 * and/or {@link #scan(String...)}. 161 * @param scopeMetadataResolver the scope metadata resolver 162 */ 163 public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) { 164 this.reader.setScopeMetadataResolver(scopeMetadataResolver); 165 this.scanner.setScopeMetadataResolver(scopeMetadataResolver); 166 } 167 168 /** 169 * Register one or more annotated classes to be processed. Note that 170 * {@link #refresh()} must be called in order for the context to fully process the new 171 * class. 172 * <p> 173 * Calls to {@code #register} are idempotent; adding the same annotated class more 174 * than once has no additional effect. 175 * @param annotatedClasses one or more annotated classes, e.g. {@code @Configuration} 176 * classes 177 * @see #scan(String...) 178 * @see #refresh() 179 */ 180 @Override 181 public final void register(Class<?>... annotatedClasses) { 182 Assert.notEmpty(annotatedClasses, 183 "At least one annotated class must be specified"); 184 this.annotatedClasses.addAll(Arrays.asList(annotatedClasses)); 185 } 186 187 /** 188 * Perform a scan within the specified base packages. Note that {@link #refresh()} 189 * must be called in order for the context to fully process the new class. 190 * @param basePackages the packages to check for annotated classes 191 * @see #register(Class...) 192 * @see #refresh() 193 */ 194 @Override 195 public final void scan(String... basePackages) { 196 Assert.notEmpty(basePackages, "At least one base package must be specified"); 197 this.basePackages = basePackages; 198 } 199 200 @Override 201 protected void prepareRefresh() { 202 this.scanner.clearCache(); 203 super.prepareRefresh(); 204 } 205 206 @Override 207 protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { 208 super.postProcessBeanFactory(beanFactory); 209 if (!ObjectUtils.isEmpty(this.basePackages)) { 210 this.scanner.scan(this.basePackages); 211 } 212 if (!this.annotatedClasses.isEmpty()) { 213 this.reader.register(ClassUtils.toClassArray(this.annotatedClasses)); 214 } 215 } 216 217}