Spring에 대한 정리글입니다.
이번 글에서는 빈 조회 방식, 빈 등록 흐름, 그리고 @Autowired
어노테이션이 실제로 어떻게 동작하는지에 대해 깊이 있게 설명합니다.
버전은 Spring Boot 3.3.3을 기준으로 하며, SpringApplication 내부에서 빈을 어떻게 스캔하고 등록하고 주입하는지에 중점을 둡니다.
SpringApplication.run()
의 전체 흐름 중 실제로 빈을 등록하고 주입하는 데 영향을 주는 두 메서드를 깊이 파고듭니다.
실행 순서는 다음과 같습니다
prepareContext()
refreshContext()
prepareContext()
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
// 생략된 다양한 작업들...
if (!AotDetector.useGeneratedArtifacts()) {
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));
}
listeners.contextLoaded(context);
}
ApplicationContext
가 아직 완전히 초기화되기 전 단계에서 실행됩니다.this.load()
메서드를 호출하는 시점입니다.load()
가 실제로 빈 정의(BeanDefinition)를 생성하고 등록하는 역할을 합니다.@Configuration
클래스나 @Component
가 붙은 클래스를 인자로 받아서 실행합니다참고
- 리프레시 상태 : 애플리케이션 컨텍스트가 완전히 초기화되고 모든 빈이 생성되고 설정이 끝난 상태를 의미합니다
if (!AotDetector.useGeneratedArtifacts())
false
이므로, 위 조건은 항상 load()
를 실행하게 됩니다.spring.aot.enabled=true
로 변경하면 이 루트가 달라집니다.ApplicationContext
기본 구현체 생성 흐름SpringApplication.run()
내부에서는 다음과 같이 ApplicationContext
를 생성합니다context = this.createApplicationContext();
해당 메서드 호출은 다음과 같이 진행됩니다
ApplicationContextFactory.DEFAULT
→ DefaultApplicationContextFactory
create(WebApplicationType)
호출createDefaultApplicationContext()
호출기본 설정 Spring Boot 에서의 BeanFactory
구현체를 알아야 합니다
DefaultApplicationContextFactory
를 실행하게 됩니다public interface ApplicationContextFactory {
ApplicationContextFactory DEFAULT = new DefaultApplicationContextFactory();
...
}
DefaultApplicationContextFactory.create()
메서드를 실행하게 됩니다class DefaultApplicationContextFactory implements ApplicationContextFactory {
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
try {
return (ConfigurableApplicationContext)this.getFromSpringFactories(webApplicationType, ApplicationContextFactory::create, this::createDefaultApplicationContext);
} catch (Exception var3) {
Exception ex = var3;
throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", ex);
}
}
private ConfigurableApplicationContext createDefaultApplicationContext() {
return (ConfigurableApplicationContext)(!AotDetector.useGeneratedArtifacts() ? new AnnotationConfigApplicationContext() : new GenericApplicationContext());
}
private <T> T getFromSpringFactories(WebApplicationType webApplicationType, BiFunction<ApplicationContextFactory, WebApplicationType, T> action, Supplier<T> defaultResult) {
Iterator var4 = SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class, this.getClass().getClassLoader()).iterator();
Object result;
do {
if (!var4.hasNext()) {
return defaultResult != null ? defaultResult.get() : null;
}
ApplicationContextFactory candidate = (ApplicationContextFactory)var4.next();
result = action.apply(candidate, webApplicationType);
} while(result == null);
return result;
}
}
getFromSpringFactories()
메서드를 호출합니다 SpringFactoriesLoader.loadFactories()
메서드에서 반환된 ApplicationContextFactory
구현체들을 순회하면서 적절한 ApplicationContext
를 생성하게 됩니다 createDefaultApplicationContext()
메서드에 의해서 AnnotationConfigApplicationContext
객체가 ApplicationContext
로 설정됩니다AnnotationConfigApplicationContext
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
...
}
AnnotationConfigApplicationContext
는 GenericApplicationContext
를 상속받아서 구현하고 있습니다public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
private final DefaultListableBeanFactory beanFactory;
public final ConfigurableListableBeanFactory getBeanFactory() {
return this.beanFactory;
}
// 그외 많은 기능들
}
GenericApplicationContext
안에는 DefaultListableBeanFactory
타입의 beanFactory
를 가지고 있습니다getBeanFactory()
를 통해 외부에서 꺼내서 사용할 수 있습니다 beanFactory
가 결국 모든 빈 정의를 등록/조회/생성하는 역할을 담당합니다참고
ApplicationContext
가 곧beanFactory
를 확장한 것 인데 내부에beanFactory
필드가 또 있는 이유는 ?
ApplicationContext
는 기능이 많기 때문에, 핵심 빈 관리 책임을beanFactory
에 위임하는 구조로 설계되었다
load()
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader = this.createBeanDefinitionLoader(this.getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
if (context instanceof BeanDefinitionRegistry registry) {
return registry;
} else if (context instanceof AbstractApplicationContext abstractApplicationContext) {
return (BeanDefinitionRegistry)abstractApplicationContext.getBeanFactory();
} else {
throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
}
}
protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) {
return new BeanDefinitionLoader(registry, sources);
}
getBeanDefinitionRegistry(context)
를 통해 빈 정의 레지스트리를 가져옵니다.DefaultListableBeanFactory
가 됩니다.registry
는 간단히 이야기해서 "빈에 대한 정의를 생성하는 객체" 라고 이해하면 될 것 같습니다BeanDefinitionLoader
를 생성합니다loader.load()
를 호출하며 BeanDefinitionLoader
는 AnnotatedBeanDefinitionReader
와 ClassPathBeanDefinitionScanner
를 활용하여 애플리케이션의 클래스 경로나 어노테이션을 스캔하여 빈 정의를 생성합니다AnnotatedBeanDefinitionReader
: 주로 @Configuration
, @Component
등의 어노테이션을 읽고 빈 정의를 생성합니다ClassPathBeanDefinitionScanner
: 특정 패키지를 스캔하여 @Component
, @Service
, @Repository
, @Controller
등으로 선언된 클래스를 빈으로 등록합니다class BeanDefinitionLoader {
private static final Pattern GROOVY_CLOSURE_PATTERN = Pattern.compile(".*\\$_.*closure.*");
private final Object[] sources;
private final AnnotatedBeanDefinitionReader annotatedReader;
private final AbstractBeanDefinitionReader xmlReader;
private final BeanDefinitionReader groovyReader;
private final ClassPathBeanDefinitionScanner scanner;
private ResourceLoader resourceLoader;
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
Assert.notNull(registry, "Registry must not be null");
Assert.notEmpty(sources, "Sources must not be empty");
this.sources = sources;
...
}
void load() {
for(Object source : this.sources) {
this.load(source);
}
}
...
}
BeanDefinitionLoader
는 load()
메서드에서 각 타입에 맞게 load()
메서드를 오버로딩하여 타입에 맞는 Scanner
혹은 Reader
를 호출. Reader
, Scanner
둘다 BeanDefinitionReaderUtils.registerBeanDefinition()
를 호출하게 됩니다 public abstract class BeanDefinitionReaderUtils {
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
String[] var4 = aliases;
int var5 = aliases.length;
for(int var6 = 0; var6 < var5; ++var6) {
String alias = var4[var6];
registry.registerAlias(beanName, alias);
}
}
}
}
registry.registerBeanDefinition()
이 DefaultListableBeanFactory.registerBeanDefinition()
를 호출하고, 인자로 beanName 의 문자열을 받아서 beanDefinitionNames
에 추가하는 작업을 합니다 beanDefinitionMap
을 생성하고 이 맵은 모든 빈의 정의들의 중앙 저장소 역할을 합니다 prepareContext()
를 통해 빈 정의를 생성하고 현재 컨텍스트 기준으로 beanDefinitionMap
을 생성했습니다 refreshContext()
를 통해 빈 등록 및 빈 후처리 작업을 시작하게 됩니다 BeanPostProcessor
빈 후처리기 (BeanPostProcessor
)를 알아보겠습니다
Bean
의 인스턴스를 만든 이후 Bean
의 Initialization Life Cycle
의 이전, 이후에 실행되는 작업들을 의미합니다
Bean
초기화 전postProcessBeforeInitialization()
: Bean
이 초기화되거 전에 호출Bean
초기화 후postProcessAfterInitialization()
: Bean
의 초기화가 완료된 후에 호출@PostConstruct
, InitializingBean.afterPropertiesSet()
@Autowired
는 AutowiredAnnotationBeanPostProcessor
를 통해 구현되어 있습니다
refreshContext()
refreshContext()
는 ApplicationContext
의 refresh()
메서드를 실행하게 되는데 기본 설정으로는 AbstractApplicationContext.refresh()
메서드를 실행하게 됩니다 public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
public void refresh() throws BeansException, IllegalStateException {
...
try {
...
this.registerBeanPostProcessors(beanFactory);
this.finishBeanFactoryInitialization(beanFactory);
...
} catch (Error | RuntimeException var12) {
...
}
}
}
registerBeanPostProcessors()
finishBeanFactoryInitialization()
registerBeanPostProcessors()
PostProcessorRegistrationDelegate.registerBeanPostProcessors()
를 호출하고 있습니다final class PostProcessorRegistrationDelegate {
public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
...
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList();
List<BeanPostProcessor> internalPostProcessors = new ArrayList();
List<String> orderedPostProcessorNames = new ArrayList();
List<String> nonOrderedPostProcessorNames = new ArrayList();
...
String ppName;
BeanPostProcessor pp;
for(int var10 = 0; var10 < var9; ++var10) {
ppName = var8[var10];
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
pp = (BeanPostProcessor)beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
}
}
...
registerBeanPostProcessors(beanFactory, (List)nonOrderedPostProcessors);
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, (List)internalPostProcessors);
}
private static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, List<? extends BeanPostProcessor> postProcessors) {
if (beanFactory instanceof AbstractBeanFactory abstractBeanFactory) {
abstractBeanFactory.addBeanPostProcessors(postProcessors);
} else {
Iterator var3 = postProcessors.iterator();
while(var3.hasNext()) {
BeanPostProcessor postProcessor = (BeanPostProcessor)var3.next();
beanFactory.addBeanPostProcessor(postProcessor);
}
}
}
}
registerBeanPostProcessors()
를 두가지 형태로 오버로딩한 메서드입니다 public
메서드private
메서드public
메서드) registerBeanPostProcessors()
의 시작은 위의 첫번째 메서드부터 시작되며 beanFactory.getBeanNamesForType()
를 통해 BeanPostProcessor
타입을 모두 조회합니다 BeanPostProcessor
타입을 가져옵니다 @Autowired
의 빈 후처리기인 AutowiredAnnotationBeanPostProcessor
구현체를 가져오게 됩니다 beanFactory.getBean()
을 호출하게 됩니다 AbstractBeanFactory.getBean()
를 호출하게 되며 이는 doGetBean()
인 내부 메서드를 호출하며 빈을 가져오는 과정에서 빈을 생성할지, 기존의 인스턴스를 반환할지를 결정합니다 private
메서드) registerBeanPostProcessors()
메서드를 통해 abstractBeanFactory.addBeanPostProcessor(postProcessors)
를 실행하게 됩니다 addBeanPostProcessors()
public void addBeanPostProcessors(Collection<? extends BeanPostProcessor> beanPostProcessors) {
synchronized(this.beanPostProcessors) {
this.beanPostProcessors.removeAll(beanPostProcessors);
this.beanPostProcessors.addAll(beanPostProcessors);
}
}
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
private final List<BeanPostProcessor> beanPostProcessors = new BeanPostProcessorCacheAwareList();
@Nullable
private BeanPostProcessorCache beanPostProcessorCache;
}
beanPostProcessors
를 살펴보면 BeanPostProcessorCacheAwareList
를 사용한걸로 보여집니다BeanPostProcessorCacheAwareList
는 add
메서드를 통해 빈 후처리기를 등록하는데 등록하는 부분을 보면 beanProcessorCache
를 삭제한 후 등록하는 것으로 구현되어 있다 private class BeanPostProcessorCacheAwareList extends CopyOnWriteArrayList<BeanPostProcessor> {
private BeanPostProcessorCacheAwareList() {
}
public BeanPostProcessor set(int index, BeanPostProcessor element) {
BeanPostProcessor result = (BeanPostProcessor)super.set(index, element);
AbstractBeanFactory.this.resetBeanPostProcessorCache();
return result;
}
public boolean add(BeanPostProcessor o) {
boolean success = super.add(o);
AbstractBeanFactory.this.resetBeanPostProcessorCache();
return success;
}
public void add(int index, BeanPostProcessor element) {
super.add(index, element);
AbstractBeanFactory.this.resetBeanPostProcessorCache();
}
public BeanPostProcessor remove(int index) {
BeanPostProcessor result = (BeanPostProcessor)super.remove(index);
AbstractBeanFactory.this.resetBeanPostProcessorCache();
return result;
}
public boolean remove(Object o) {
boolean success = super.remove(o);
if (success) {
AbstractBeanFactory.this.resetBeanPostProcessorCache();
}
return success;
}
}
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
private void resetBeanPostProcessorCache() {
synchronized(this.beanPostProcessors) {
this.beanPostProcessorCache = null;
}
}
}
BeanPostProcessorCacheAwarList
는 일반적인 리스트가 아니라 BeanPostProcessor
객체들을 효율적으로 관리하기 위한 Spring 의 특별한 리스트 클래스입니다 CopyOnWriteArrayList<BeanPostProcessor>
는 java.util.concurrent
패키지에 포함되어 있으며 CopyOnWriteArrayList
는 스레드 안전한 리스트로 쓰기 작업 (추가, 수정, 삭제) 이 일어날 때마다 기존 배열을 복사하여 새로운 배열을 생성하는 방식으로 동작합니다 BeanPostProcessor
들을 분류하고 캐시처럼 활용합니다 BeanPostProcessor
들은 빈 생성 과정에서 빈의 초기화 전후로 자주 호출됩니다, 매번 beanFactory
에서 BeanPostProcessor
를 조회하는 것은 성능에 영향을 줄 수 있기때문에 Spring 은 BeanPostProcessor
들을 한 번 가져와 캐시에 저장하고, 이후에는 이 캐시에서 BeanPostProcessor
들을 사용합니다 getBean()
BeanPostProcessor
를 가져오기 위해 beanFactory.getBean()
을 호출합니다 public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
public Object getBean(String name) throws BeansException {
return this.doGetBean(name, (Class)null, (Object[])null, false);
}
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return this.doGetBean(name, requiredType, (Object[])null, false);
}
public Object getBean(String name, Object... args) throws BeansException {
return this.doGetBean(name, (Class)null, args, false);
}
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args) throws BeansException {
return this.doGetBean(name, requiredType, args, false);
}
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
String beanName = this.transformedBeanName(name);
Object sharedInstance = this.getSingleton(beanName);
Object beanInstance;
if (sharedInstance != null && args == null) {
beanInstance = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
} else {
if (this.isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
BeanFactory parentBeanFactory = this.getParentBeanFactory();
if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {
String nameToLookup = this.originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
AbstractBeanFactory abf = (AbstractBeanFactory)parentBeanFactory;
return abf.doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
}
if (args != null) {
return parentBeanFactory.getBean(nameToLookup, args);
}
if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
return parentBeanFactory.getBean(nameToLookup);
}
if (!typeCheckOnly) {
this.markBeanAsCreated(beanName);
}
try {
RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
this.checkMergedBeanDefinition(mbd, beanName, args);
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for(int var14 = 0; var14 < var13; ++var14) {
String dep = prototypeInstance[var14];
...
try {
this.getBean(dep);
} catch (NoSuchBeanDefinitionException var33) {
...
} catch (BeanCreationException var34) {
...
}
}
}
if (mbd.isSingleton()) {
sharedInstance = this.getSingleton(beanName, () -> {
try {
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
BeansException ex = var5;
this.destroySingleton(beanName);
throw ex;
}
});
beanInstance = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {
prototypeInstance = null;
Object prototypeInstance;
try {
this.beforePrototypeCreation(beanName);
prototypeInstance = this.createBean(beanName, mbd, args);
} finally {
this.afterPrototypeCreation(beanName);
}
beanInstance = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
}
Scope scope = (Scope)this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
this.beforePrototypeCreation(beanName);
Object var4;
try {
var4 = this.createBean(beanName, mbd, args);
} finally {
this.afterPrototypeCreation(beanName);
}
return var4;
});
beanInstance = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
}
}
}
}
}
Spring 에서 싱글톤 객체는 디자인 패턴의 생성자를 이용한 싱글톤 패턴을 구현한 것이 아닙니다
Spring 의 싱글톤 빈은 객체에 대한 전통적인 싱글톤 패턴을 코드로 구현한 것이 아니라 Spring 컨테이너가 싱글톤 범위로 관리 하여 구현된 것 입니다
즉, 캐시를 통해서 싱글톤을 구현한 것 입니다
getBean()
을 통해 Spring은 빈의 라이프사이클에 따라 객체를 생성합니다
생성된 인스턴스는 addSingleton()
메서드를 통해 singletoneObjects
라는 캐시에 저장됩니다
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
protected void addSingleton(String beanName, Object singletonObject) {
synchronized(this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized(this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
그래서 이미 생성된 싱글톤 빈이 캐시에 존재하는지 확인한 후 존재한다면 캐시된 인스턴스를 사용하게 되도록 합니다 (this.getSingleton()
)
if (sharedInstance != null && args == null) {
// 캐시된 인스턴스를 반환
}
if (this.isPrototypeCurrentlyInCreation(beanName)) {
// 예외
}
beanName
과 같으면 순환 의존성이 생겼다고 파악합니다if (!typeCheckOnly) {
this.markBeanAsCreated(beanName);
}
RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
this.checkMergedBeanDefinition(mbd, beanName, args);
getMergedLocalBeanDefinition()
은 특정 빈의 최종 BeanDefinition
을 반환하는 역할을 합니다 RootBeanDefinition
과 같은 실제 빈 정의 객체를 반환합니다 String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
// 종속된 빈을 순회하며 가져옵니다.
}
dependsOn
속성을 통해 다른 빈에 대한 종속성을 확인하고, 필요한 빈들을 먼저 생성하여 등록합니다 @Autowired
와는 다릅니다 dependsOn
은 일반적으로 빈 정의 수준에서 사용되며 특정 빈이 먼저 초기화되어야 할 때 사용됩니다 dependsOn
으로 설정할 수 있습니다 createBean()
을 호출하여 새로 생성하고 생성된 인스턴스를 싱글톤 캐시에 저장합니다 finishBeanFactoryInitialization()
@Autowired
가 실제로 동작합니다 protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
...
beanFactory.freezeConfiguration();
beanFactory.preInstantiateSingletons();
}
freezeConfiguration()
: 빈 팩토리의 설정을 "동결"합니다, 이 설정이 완료되면 빈 정의를 더 이상 변경할 수 없으며 애플리케이션의 안정적인 동작을 보장합니다 preInstantiateSingletons()
: 메서드의 핵심으로 모든 싱글톤 빈을 미리 인스턴화합니다, 이 과정에서 @Autowired
어노테이션에 의한 의존성 주입이 실제로 발생합니다 preInstantiateSingletons()
DefaultListableBeanFactory.preInstantiateSingletons()
를 실행합니다 public void preInstantiateSingletons() throws BeansException {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Pre-instantiating singletons in " + this);
}
List<String> beanNames = new ArrayList(this.beanDefinitionNames);
Iterator var2 = beanNames.iterator();
String beanName;
while(var2.hasNext()) {
beanName = (String)var2.next();
RootBeanDefinition bd = this.getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (this.isFactoryBean(beanName)) {
Object bean = this.getBean("&" + beanName);
if (bean instanceof SmartFactoryBean) {
SmartFactoryBean<?> smartFactoryBean = (SmartFactoryBean)bean;
if (smartFactoryBean.isEagerInit()) {
this.getBean(beanName);
}
}
} else {
this.getBean(beanName);
}
}
}
var2 = beanNames.iterator();
while(var2.hasNext()) {
beanName = (String)var2.next();
Object singletonInstance = this.getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton smartSingleton) {
StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize").tag("beanName", beanName);
smartSingleton.afterSingletonsInstantiated();
smartInitialize.end();
}
}
}
this.beanDefinitionNames
는 이전에 load()
를 통해서 이미 beanDefinitionMap
을 만들고 beanDefinitionNames
에 모든 빈을 추가했습니다 getBean()
을 찾아가 보면 createBean()
을 실행하는 걸 볼 수 있습니다.AbstractAutowireCapableBeanFactory.createBean()
입니다 createBean()
은 내부 메서드인 doCreateBean()
을 호출하게 됩니다 doCreateBean()
은 populateBean()
메서드를 호출하게 됩니다 if (this.hasInstantiationAwareBeanPostProcessors()) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
PropertyValues pvsToUse;
for (Iterator var11 = this.getBeanPostProcessorCache().instantiationAware.iterator(); var11.hasNext(); pvs = pvsToUse) {
InstantiationAwareBeanPostProcessor bp = (InstantiationAwareBeanPostProcessor)var11.next();
pvsToUse = bp.postProcessProperties((PropertyValues)pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
}
InstantiationAwareBeanPostProcessor
를 통해 @Autowired
와 같은 어노테이션이 있는 필드에 값을 주입하는 작업이 시작됩니다 getBeanPostProssorCache()
를 통해 이전에 가져온 BeanPostProcessor
들을 모두 가져옵니다 static class BeanPostProcessorCache {
final List<InstantiationAwareBeanPostProcessor> instantiationAware = new ArrayList();
final List<SmartInstantiationAwareBeanPostProcessor> smartInstantiationAware = new ArrayList();
final List<DestructionAwareBeanPostProcessor> destructionAware = new ArrayList();
final List<MergedBeanDefinitionPostProcessor> mergedDefinition = new ArrayList();
BeanPostProcessorCache() {
}
}
InstantiationAwareBeanPostProcessor
: 빈이 인스턴스화될 때와 프로퍼티가 설정될 때 개입됨, 이 프로세서가 빈의 인스턴스화와 의존성 주입 시점에 직접적으로 영향을 미침 SmartInstantiationAwareBeanPostProcessor
: 위 인터페이스의 하위 인터페이스이며 인스턴스화 과정에서 더욱 세밀하게 제어할 수 있는 기능을 제공함 DestructionAwareBeanPostProcessor
: 빈의 소멸 단계에서 개입하는 프로세서이며 주로 destroy
메서드를 호출하거나, 빈이 컨테이너에서 제거될 때 필요한 작업을 처리함 MergedBeanDefinitionPostProcessor
: 빈 정의가 병합된 후, 정의를 수정하거나 추가 작업을 수행하는 프로세서, 빈의 메타 데이터를 변경하거나 어노테이션 정보를 처리하는데 사용함 pvsToUse = bp.postProcessProperties((PropertyValues)pvs, bw.getWrappedInstance(), beanName);
postProcessProperties()
메서드를 통해 빈 후처리가 시작되며 AutowiredAnnotationBeanPostProcessor.postProcessProperties()
메서드가 실행되며 이때 의존성 주입이 시작됩니다 public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
return pvs;
} catch (BeanCreationException var6) {
BeanCreationException ex = var6;
throw ex;
} catch (Throwable var7) {
Throwable ex = var7;
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
}
InjectionMetadata
클래스의 inject()
메서드를 통해 실제 의존성 주입을 하게됩니다 public class InjectionMetadata {
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectedElement> checkedElements = this.checkedElements;
Collection<InjectedElement> elementsToIterate = checkedElements != null ? checkedElements : this.injectedElements;
if (!((Collection)elementsToIterate).isEmpty()) {
Iterator var6 = ((Collection)elementsToIterate).iterator();
while(var6.hasNext()) {
InjectedElement element = (InjectedElement)var6.next();
element.inject(target, beanName, pvs);
}
}
}
}
inject()
메서드에서는 빈들을 순환하면서 @Autowired
등의 어노테이션이 적용된 필드나 메서드에 의존성을 주입합니다load()
를 통해 빈을 조회하기 쉽도록 mapping table을 생성합니다BeanDefinition
)을 모두 조회함 refreshContext()
를 통해 빈 후처리기 (BeanPostProcessor
) 를 등록합니다 @Autowired
의 후처리기인 AutoWiredAnnotationBeanPostProcessor
가 등록됩니다 finishBeanFactoryInitialization()
를 통해 미리 조회한 빈을 가져온 후 빈으로 등록하며 생성하면서 빈 후처리기를 실행합니다 beanFactory.getBean()
InjectionMetadata.inject()
참고.
BeanFactory,
&
Prefix
- 빈 이름에는
&
프리픽스가 붙어있는 빈 이름들이 있는데 이는getBean("&test")
호출 시getFactoryBean("test")
를 통해FactoryBean
을 직접 반환하는 역할하는 하는 프리픽스입니다
FactoryBean
은 빈을 생성하는 팩토리 역할을 하는 특별한 스프링 빈입니다public interface BeanFactory { String FACTORY_BEAN_PREFIX = "&"; ... }
@Value
어노테이션 처리
finishBeanFactoryInitialization()
메서드 과정에서@Value("${mail.host}")
와 관련된 로직도 구현되어 있다if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver((strVal) -> { return this.getEnvironment().resolvePlaceholders(strVal); } );
- 이 리졸버는
${}
형식의 플레이스 홀더를 실제 값으로 치환하는 역할을 하며, 환경 변수나 설정 파일의 값을 해결하는데 사용됩니다
hasEmbeddedValueResolver()
는BeanFactory
에 값 리졸버가 등록되어 있는지 확인하는 부분이며 만약, 등록되어 있지 않다면 새로운 리졸버를 추가합니다addEmbeddedValueResolver()
는${}
형식의 플레이스 홀더를 처리하기 위해 리졸버를 추가하는 작업이며, 이 리졸버는 주로@Value
어노테이션에서 사용되는 플레이스홀더를 실제 값으로 변환하는 역할을 합니다