๋ณธ๋ฌธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

๐Ÿ”— AOP & DI/IoC Integration

๊ฐœ์š”โ€‹

์ด ๋ฌธ์„œ๋Š” Sprout Framework์—์„œ AOP(Aspect-Oriented Programming)๊ฐ€ DI/IoC ์ปจํ…Œ์ด๋„ˆ์™€ ์–ด๋–ป๊ฒŒ ํ†ตํ•ฉ๋˜์–ด ์ž๋™ ํ”„๋ก์‹œ ์ƒ์„ฑ์„ ์ˆ˜ํ–‰ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ์‹ฌ์ธต์ ์ธ ๊ธฐ์ˆ  ๋ถ„์„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ธํ”„๋ผ ๋นˆ์˜ ์ดˆ๊ธฐํ™” ์ˆœ์„œ๋ถ€ํ„ฐ CGLIB ๊ธฐ๋ฐ˜ ํ”„๋ก์‹œ ์ƒ์„ฑ, ๊ทธ๋ฆฌ๊ณ  ๋ฉ”์„œ๋“œ ์ธํ„ฐ์…‰์…˜ ์ฒด์ธ๊นŒ์ง€์˜ ์ „ ๊ณผ์ •์„ ์ƒ์„ธํžˆ ๋ถ„์„ํ•˜์—ฌ Sprout AOP์˜ ์™„์ „ํ•œ ์ž‘๋™ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

์ „์ฒด ์•„ํ‚คํ…์ฒ˜ ๊ฐœ์š”โ€‹

AOP-DI ํ†ตํ•ฉ ํ๋ฆ„๋„โ€‹

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘
โ†“
SproutApplicationContext.refresh()
โ†“
1. ๋นˆ ์ •์˜ ์Šค์บ” (scanBeanDefinitions)
โ”œโ”€โ”€ @Component, @Service, @Repository ์Šค์บ”
โ”œโ”€โ”€ @Aspect ํด๋ž˜์Šค ์Šค์บ”
โ””โ”€โ”€ InfrastructureBean vs ApplicationBean ๋ถ„๋ฅ˜
โ†“
2. ์ธํ”„๋ผ ๋นˆ ์ดˆ๊ธฐํ™” (instantiateInfrastructureBeans)
โ”œโ”€โ”€ AdvisorRegistry, AdviceFactory, ProxyFactory ์ƒ์„ฑ
โ”œโ”€โ”€ AspectPostProcessor ์ƒ์„ฑ ๋ฐ ๋“ฑ๋ก
โ””โ”€โ”€ PostInfrastructureInitializer ์‹คํ–‰
โ†“
3. AopPostInfrastructureInitializer ์‹คํ–‰
โ”œโ”€โ”€ @Aspect ํด๋ž˜์Šค ์Šค์บ”
โ”œโ”€โ”€ Advisor ์ƒ์„ฑ ๋ฐ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ๋“ฑ๋ก
โ””โ”€โ”€ AspectPostProcessor ์ดˆ๊ธฐํ™”
โ†“
4. BeanPostProcessor ๋“ฑ๋ก (registerBeanPostProcessors)
โ””โ”€โ”€ AspectPostProcessor๋ฅผ BeanPostProcessor๋กœ ๋“ฑ๋ก
โ†“
5. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นˆ ์ดˆ๊ธฐํ™” (instantiateAllSingletons)
โ”œโ”€โ”€ ๋นˆ ์ƒ์„ฑ ์‹œ BeanPostProcessor ์ฒด์ธ ์‹คํ–‰
โ”œโ”€โ”€ AspectPostProcessor.postProcessAfterInitialization ํ˜ธ์ถœ
โ”œโ”€โ”€ ํ”„๋ก์‹œ ํ•„์š”์„ฑ ํŒ๋‹จ ๋ฐ CGLIB ํ”„๋ก์‹œ ์ƒ์„ฑ
โ””โ”€โ”€ BeanMethodInterceptor๋กœ ๋ฉ”์„œ๋“œ ์ธํ„ฐ์…‰์…˜ ์„ค์ •

ํ•ต์‹ฌ ์„ค๊ณ„ ์›์น™โ€‹

  1. ์ธํ”„๋ผ ์šฐ์„  ์ดˆ๊ธฐํ™”: AOP ๊ด€๋ จ ์ธํ”„๋ผ ๋นˆ๋“ค์ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นˆ๋ณด๋‹ค ๋จผ์ € ์ดˆ๊ธฐํ™”
  2. PostProcessor ํŒจํ„ด: BeanPostProcessor๋ฅผ ํ†ตํ•œ ํˆฌ๋ช…ํ•œ ํ”„๋ก์‹œ ์ƒ์„ฑ
  3. CGLIB ๊ธฐ๋ฐ˜ ํ”„๋ก์‹œ: ์ธํ„ฐํŽ˜์ด์Šค ์—†์ด๋„ ํ”„๋ก์‹œ ์ƒ์„ฑ ๊ฐ€๋Šฅ
  4. ์ฒด์ธ ์˜ค๋ธŒ ๋ฆฌ์ŠคํŽ€์„œ๋นŒ๋ฆฌํ‹ฐ: ์—ฌ๋Ÿฌ ์–ด๋“œ๋ฐ”์ด์Šค์˜ ์ˆœ์ฐจ์  ์‹คํ–‰

์ธํ”„๋ผ ๋นˆ ์ดˆ๊ธฐํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜โ€‹

1. SproutApplicationContext์˜ ์ดˆ๊ธฐํ™” ์ „๋žตโ€‹

๋‹จ๊ณ„๋ณ„ ์ดˆ๊ธฐํ™” ๊ณผ์ •

@Override
public void refresh() throws Exception {
scanBeanDefinitions(); // 1. ๋นˆ ์ •์˜ ์Šค์บ”
instantiateInfrastructureBeans(); // 2. ์ธํ”„๋ผ ๋นˆ ์ดˆ๊ธฐํ™” (AOP ํฌํ•จ)
instantiateAllSingletons(); // 3. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นˆ ์ดˆ๊ธฐํ™”

// 4. ์ปจํ…์ŠคํŠธ ํ›„์ฒ˜๋ฆฌ
List<ContextInitializer> contextInitializers = getAllBeans(ContextInitializer.class);
for (ContextInitializer initializer : contextInitializers) {
initializer.initializeAfterRefresh(this);
}
}

2. ๋นˆ ๋ถ„๋ฅ˜ ์ „๋žต: ์ธํ”„๋ผ vs ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜โ€‹

์ž๋™ ๋ถ„๋ฅ˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜

private void scanBeanDefinitions() throws NoSuchMethodException {
// ๋ชจ๋“  ๋นˆ ์ •์˜ ์Šค์บ”
Collection<BeanDefinition> allDefs = scanner.scan(configBuilder,
Component.class, Controller.class, Service.class, Repository.class,
Configuration.class, Aspect.class, ControllerAdvice.class, WebSocketHandler.class
);

// ์ธํ”„๋ผ ๋นˆ ๋ถ„๋ฅ˜ (BeanPostProcessor + InfrastructureBean)
List<BeanDefinition> infraDefs = new ArrayList<>(allDefs.stream()
.filter(bd -> BeanPostProcessor.class.isAssignableFrom(bd.getType()) ||
InfrastructureBean.class.isAssignableFrom(bd.getType()))
.toList());

// ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นˆ ๋ถ„๋ฅ˜ (๋‚˜๋จธ์ง€)
List<BeanDefinition> appDefs = new ArrayList<>(allDefs);
appDefs.removeAll(infraDefs);

this.infraDefs = infraDefs;
this.appDefs = appDefs;
}

๋ถ„๋ฅ˜ ๊ธฐ์ค€

  • ์ธํ”„๋ผ ๋นˆ: BeanPostProcessor ๊ตฌํ˜„์ฒด + InfrastructureBean ๊ตฌํ˜„์ฒด
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นˆ: ๋‚˜๋จธ์ง€ ๋ชจ๋“  ๋นˆ (๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋นˆ๋“ค)

๋ถ„๋ฅ˜์˜ ์ค‘์š”์„ฑ

  1. ์ˆœ์„œ ๋ณด์žฅ: AOP ์ธํ”„๋ผ๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นˆ๋ณด๋‹ค ๋จผ์ € ์ค€๋น„๋จ
  2. ์˜์กด์„ฑ ํ•ด๊ฒฐ: PostProcessor๋“ค์ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นˆ ์ƒ์„ฑ ์‹œ์ ์— ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  3. ์ดˆ๊ธฐํ™” ๋ถ„๋ฆฌ: ๊ฐ ๊ทธ๋ฃน๋ณ„๋กœ ๋…๋ฆฝ์ ์ธ ์ดˆ๊ธฐํ™” ์ „๋žต ์ ์šฉ

3. PostInfrastructureInitializer ํŒจํ„ดโ€‹

์ธํ”„๋ผ ๋นˆ ์ดˆ๊ธฐํ™” ํ›„ ์ฝœ๋ฐฑ

private void instantiateInfrastructureBeans() {
instantiateGroup(infraDefs); // ์ธํ”„๋ผ ๋นˆ๋“ค ์ƒ์„ฑ

// PostInfrastructureInitializer ์‹คํ–‰
List<PostInfrastructureInitializer> initializers = beanFactory.getAllBeans(PostInfrastructureInitializer.class);
for (PostInfrastructureInitializer initializer : initializers) {
initializer.afterInfrastructureSetup(beanFactory, basePackages);
}
}

AopPostInfrastructureInitializer ๊ตฌํ˜„

@Component
public class AopPostInfrastructureInitializer implements PostInfrastructureInitializer {
private final AspectPostProcessor aspectPostProcessor;

@Override
public void afterInfrastructureSetup(BeanFactory beanFactory, List<String> basePackages) {
aspectPostProcessor.initialize(basePackages); // AspectPostProcessor ์ดˆ๊ธฐํ™”
}
}

์ดˆ๊ธฐํ™” ํƒ€์ด๋ฐ์˜ ์ค‘์š”์„ฑ

  • ๋ชจ๋“  AOP ๊ด€๋ จ ์ธํ”„๋ผ ๋นˆ(AdvisorRegistry, AdviceFactory ๋“ฑ)์ด ์ค€๋น„๋œ ํ›„ ์‹คํ–‰
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นˆ ์ƒ์„ฑ ์ „์— ๋ชจ๋“  Advisor๊ฐ€ ๋“ฑ๋ก ์™„๋ฃŒ
  • BeanPostProcessor ๋“ฑ๋ก ์ „์— AOP ์„ค์ • ์™„๋ฃŒ

AspectPostProcessor: AOP์˜ ํ•ต์‹ฌ ์—”์ง„โ€‹

1. ์ด์ค‘ ์—ญํ•  ์•„ํ‚คํ…์ฒ˜โ€‹

AspectPostProcessor๋Š” ๋‘ ๊ฐ€์ง€ ํ•ต์‹ฌ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค:

  1. PostInfrastructureInitializer ์‹œ์ : Aspect ์Šค์บ” ๋ฐ Advisor ๋“ฑ๋ก
  2. BeanPostProcessor ์‹œ์ : ํ”„๋ก์‹œ ์ƒ์„ฑ ์—ฌ๋ถ€ ํŒ๋‹จ ๋ฐ ์‹คํ–‰

2. Aspect ์Šค์บ” ๋ฐ Advisor ๋“ฑ๋ก ๊ณผ์ •โ€‹

์ดˆ๊ธฐํ™” ๋ฉ”์„œ๋“œ

public void initialize(List<String> basePackages) {
if (initialized.compareAndSet(false, true)) { // AtomicBoolean์œผ๋กœ ์ค‘๋ณต ์‹คํ–‰ ๋ฐฉ์ง€
this.basePackages = basePackages;
scanAndRegisterAdvisors();
}
}

Reflections ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ธฐ๋ฐ˜ ์Šค์บ”

private void scanAndRegisterAdvisors() {
// ConfigurationBuilder๋กœ ์Šค์บ” ๋ฒ”์œ„ ์„ค์ •
ConfigurationBuilder configBuilder = new ConfigurationBuilder();
for (String pkg : basePackages) {
configBuilder.addUrls(ClasspathHelper.forPackage(pkg));
}
configBuilder.addScanners(Scanners.TypesAnnotated, Scanners.SubTypes);

// ํŒจํ‚ค์ง€ ํ•„ํ„ฐ๋ง
FilterBuilder filter = new FilterBuilder();
for (String pkg : basePackages) {
filter.includePackage(pkg);
}
configBuilder.filterInputsBy(filter);

// @Aspect ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ํด๋ž˜์Šค ๊ฒ€์ƒ‰
Reflections reflections = new Reflections(configBuilder);
Set<Class<?>> aspectClasses = reflections.getTypesAnnotatedWith(Aspect.class);

// ๊ฐ Aspect ํด๋ž˜์Šค์—์„œ Advisor ์ƒ์„ฑ ๋ฐ ๋“ฑ๋ก
for (Class<?> aspectClass : aspectClasses) {
List<Advisor> advisorsForThisAspect = createAdvisorsFromAspect(aspectClass);
for (Advisor advisor : advisorsForThisAspect) {
advisorRegistry.registerAdvisor(advisor);
}
}
}

Aspect์—์„œ Advisor ์ƒ์„ฑ

private List<Advisor> createAdvisorsFromAspect(Class<?> aspectClass) {
List<Advisor> advisors = new ArrayList<>();

// ApplicationContext์—์„œ ๋นˆ ์กฐํšŒ๋ฅผ ์œ„ํ•œ Supplier
Supplier<Object> aspectSupplier = () -> container.getBean(aspectClass);

// ๋ชจ๋“  ๋ฉ”์„œ๋“œ๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ ์–ด๋“œ๋ฐ”์ด์Šค ์–ด๋…ธํ…Œ์ด์…˜ ํ™•์ธ
for (Method m : aspectClass.getDeclaredMethods()) {
adviceFactory.createAdvisor(aspectClass, m, aspectSupplier)
.ifPresent(advisors::add);
}

return advisors;
}

3. BeanPostProcessor๋กœ์„œ์˜ ํ”„๋ก์‹œ ์ƒ์„ฑโ€‹

ํ›„์ฒ˜๋ฆฌ ๋ฉ”์„œ๋“œ

@Override
public Object postProcessAfterInitialization(String beanName, Object bean) {
Class<?> targetClass = bean.getClass();

// ํ”„๋ก์‹œ ํ•„์š”์„ฑ ํŒ๋‹จ
boolean needsProxy = false;
for (Method method : targetClass.getMethods()) {
if (Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers())) {
if (!advisorRegistry.getApplicableAdvisors(targetClass, method).isEmpty()) {
needsProxy = true;
break;
}
}
}

// ํ”„๋ก์‹œ ์ƒ์„ฑ ๋ฐ ๋ฐ˜ํ™˜
if (needsProxy) {
CtorMeta meta = container.lookupCtorMeta(bean);
return proxyFactory.createProxy(targetClass, bean, advisorRegistry, meta);
}

return bean; // ํ”„๋ก์‹œ ๋ถˆํ•„์š” ์‹œ ์›๋ณธ ๋ฐ˜ํ™˜
}

ํ”„๋ก์‹œ ํ•„์š”์„ฑ ํŒ๋‹จ ์ตœ์ ํ™”

  1. public ๋ฉ”์„œ๋“œ๋งŒ ๊ฒ€์‚ฌ: private/protected ๋ฉ”์„œ๋“œ๋Š” AOP ์ ์šฉ ๋Œ€์ƒ ์•„๋‹˜
  2. static ๋ฉ”์„œ๋“œ ์ œ์™ธ: ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ๋งŒ ์ธํ„ฐ์…‰์…˜ ๊ฐ€๋Šฅ
  3. ์กฐ๊ธฐ ์ข…๋ฃŒ: ํ•˜๋‚˜๋ผ๋„ ์ ์šฉ ๊ฐ€๋Šฅํ•œ Advisor ๋ฐœ๊ฒฌ ์‹œ ์ฆ‰์‹œ ํ”„๋ก์‹œ ์ƒ์„ฑ
  4. ์บ์‹œ ํ™œ์šฉ: AdvisorRegistry์˜ ๋ฉ”์„œ๋“œ๋ณ„ ์บ์‹ฑ ํ™œ์šฉ

CGLIB ๊ธฐ๋ฐ˜ ํ”„๋ก์‹œ ์ƒ์„ฑ ์‹œ์Šคํ…œโ€‹

1. CglibProxyFactory: ํ”„๋ก์‹œ ์ƒ์„ฑ ์ „๋ฌธ๊ฐ€โ€‹

๊ฐ„๊ฒฐํ•œ ํ”„๋ก์‹œ ์ƒ์„ฑ

@Component
public class CglibProxyFactory implements ProxyFactory, InfrastructureBean {
@Override
public Object createProxy(Class<?> targetClass, Object target, AdvisorRegistry registry, CtorMeta meta) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass); // ์ƒ์† ๊ธฐ๋ฐ˜ ํ”„๋ก์‹œ
enhancer.setCallback(new BeanMethodInterceptor(target, registry)); // ๋ฉ”์„œ๋“œ ์ธํ„ฐ์…‰ํ„ฐ ์„ค์ •
return enhancer.create(meta.paramTypes(), meta.args()); // ์ƒ์„ฑ์ž ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
}
}

CGLIB Enhancer ์„ค์ •

  1. setSuperclass: ์›๋ณธ ํด๋ž˜์Šค๋ฅผ ๋ถ€๋ชจ ํด๋ž˜์Šค๋กœ ์„ค์ • (์ƒ์† ๊ธฐ๋ฐ˜ ํ”„๋ก์‹œ)
  2. setCallback: ๋ชจ๋“  ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์„ ์ธํ„ฐ์…‰ํŠธํ•  ์ฝœ๋ฐฑ ์„ค์ •
  3. create: ์›๋ณธ ๊ฐ์ฒด์™€ ๋™์ผํ•œ ์ƒ์„ฑ์ž ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํ”„๋ก์‹œ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ

CtorMeta ํ™œ์šฉ

  • ์›๋ณธ ๋นˆ ์ƒ์„ฑ ์‹œ ์‚ฌ์šฉ๋œ ์ƒ์„ฑ์ž ์ •๋ณด๋ฅผ ๋ณด์กด
  • ํ”„๋ก์‹œ ์ƒ์„ฑ ์‹œ ๋™์ผํ•œ ์ƒ์„ฑ์ž ํŒŒ๋ผ๋ฏธํ„ฐ ์‚ฌ์šฉ
  • DI ์ปจํ…Œ์ด๋„ˆ์˜ ์ƒ์„ฑ ์ผ๊ด€์„ฑ ๋ณด์žฅ

2. BeanMethodInterceptor: ๋ฉ”์„œ๋“œ ์ธํ„ฐ์…‰์…˜ ํ—ˆ๋ธŒโ€‹

CGLIB MethodInterceptor ๊ตฌํ˜„

public class BeanMethodInterceptor implements MethodInterceptor {
private final Object target; // ์›๋ณธ ๊ฐ์ฒด
private final AdvisorRegistry advisorRegistry; // ์–ด๋“œ๋ฐ”์ด์ € ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// ์ ์šฉ ๊ฐ€๋Šฅํ•œ ์–ด๋“œ๋ฐ”์ด์ € ์กฐํšŒ (์บ์‹œ ํ™œ์šฉ)
List<Advisor> applicableAdvisors = advisorRegistry.getApplicableAdvisors(target.getClass(), method);

if (applicableAdvisors.isEmpty()) {
// ์–ด๋“œ๋ฐ”์ด์ € ์—†์œผ๋ฉด ์›๋ณธ ๋ฉ”์„œ๋“œ ์ง์ ‘ ํ˜ธ์ถœ
return proxy.invoke(target, args);
}

// ์–ด๋“œ๋ฐ”์ด์Šค ์ฒด์ธ ์‹คํ–‰์„ ์œ„ํ•œ MethodInvocation ์ƒ์„ฑ
MethodInvocationImpl invocation = new MethodInvocationImpl(target, method, args, proxy, applicableAdvisors);
return invocation.proceed();
}
}

์ธํ„ฐ์…‰์…˜ ์ตœ์ ํ™” ์ „๋žต

  1. ์กฐ๊ธฐ ๋ถ„๊ธฐ: ์ ์šฉํ•  ์–ด๋“œ๋ฐ”์ด์ €๊ฐ€ ์—†์œผ๋ฉด ์ฆ‰์‹œ ์›๋ณธ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
  2. ์บ์‹œ ํ™œ์šฉ: AdvisorRegistry์˜ ๋ฉ”์„œ๋“œ๋ณ„ ์–ด๋“œ๋ฐ”์ด์ € ์บ์‹ฑ
  3. ์ง€์—ฐ ์ƒ์„ฑ: MethodInvocation์€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ์ƒ์„ฑ
  4. ์ง์ ‘ ํ˜ธ์ถœ: CGLIB์˜ MethodProxy.invoke()๋กœ ์„ฑ๋Šฅ ์ตœ์ ํ™”

ํ”„๋ก์‹œ ์ „๋žต: ์œ„์ž„ํ˜• vs ๋‹จ์ผ ์ธ์Šคํ„ด์Šคํ˜•โ€‹

Sprout AOP์—์„œ ํ”„๋ก์‹œ ์ƒ์„ฑ์€ ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€ ์ „๋žต์œผ๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค.

1. ์œ„์ž„ํ˜•(Delegating Proxy)โ€‹

  • ๊ตฌ์กฐ: ์›๋ณธ ์ธ์Šคํ„ด์Šค๋ฅผ ๋จผ์ € ์ƒ์„ฑํ•˜๊ณ , ํ”„๋ก์‹œ๋Š” ๋‹จ์ˆœํžˆ ํ˜ธ์ถœ์„ ์œ„์ž„
  • ์ธํ„ฐ์…‰ํ„ฐ ๋™์ž‘: proxy.invoke(target, args)
  • ํŠน์ง•:
    • ์›๋ณธ๊ณผ ํ”„๋ก์‹œ๊ฐ€ ๋ชจ๋‘ ์กด์žฌ
    • ์›๋ณธ์˜ ์ƒ์„ฑ์ž ๋ถ€์ž‘์šฉ์ด ๋‘ ๋ฒˆ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ (์›๋ณธ ์ƒ์„ฑ + ํ”„๋ก์‹œ ์ƒ์„ฑ)
    • Objenesis๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋ก์‹œ ์ƒ์„ฑ์ž์˜ ์‹คํ–‰์„ ๊ฑด๋„ˆ๋›ฐ์–ด โ€œ2์ค‘ ์ƒ์„ฑโ€ ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€
  • ์‚ฌ์šฉ ์‹œ์ : ์›๋ณธ ์ธ์Šคํ„ด์Šค์˜ ์ƒํƒœ๋‚˜ ์ƒ์„ฑ์ž ๋กœ์ง์„ ๋ฐ˜๋“œ์‹œ ์‚ด๋ ค์•ผ ํ•  ๋•Œ

2. ๋‹จ์ผ ์ธ์Šคํ„ด์Šคํ˜•(Subclassing Proxy)โ€‹

  • ๊ตฌ์กฐ: CGLIB์ด ์›๋ณธ ํด๋ž˜์Šค๋ฅผ ์ƒ์†ํ•œ ์„œ๋ธŒํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑ, ์ด๊ฒƒ์ด ๊ณง ๋นˆ
  • ์ธํ„ฐ์…‰ํ„ฐ ๋™์ž‘: proxy.invokeSuper(this, args)
  • ํŠน์ง•:
    • ๋ณ„๋„์˜ ์›๋ณธ ์ธ์Šคํ„ด์Šค๋Š” ์—†์Œ
    • ํ”„๋ก์‹œ ์ƒ์„ฑ ์‹œ ์„ ํƒ๋œ ์ƒ์„ฑ์ž๋ฅผ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœํ•˜๋ฏ€๋กœ โ€œ2์ค‘ ์ƒ์„ฑโ€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ
    • DI๋Š” ํ”„๋ก์‹œ ์ธ์Šคํ„ด์Šค ์ž์ฒด์— ์ˆ˜ํ–‰๋จ (์ƒ์„ฑ์ž/ํ•„๋“œ/์„ธํ„ฐ ๋ชจ๋‘ ํ”„๋ก์‹œ๊ฐ€ ๋Œ€์ƒ)
  • ์‚ฌ์šฉ ์‹œ์ : ํ”„๋ก์‹œ๊ฐ€ ๊ณง ๋นˆ ์—ญํ• ์„ ํ•˜๊ณ , ์›๋ณธ ๊ฐ์ฒด๋ฅผ ๋”ฐ๋กœ ๊ด€๋ฆฌํ•  ํ•„์š”๊ฐ€ ์—†์„ ๋•Œ

Sprout์˜ ์„ ํƒโ€‹

Sprout์€ ๋‹จ์ผ ์ธ์Šคํ„ด์Šคํ˜• ์ „๋žต์„ ๊ธฐ๋ณธ์œผ๋กœ ์ฑ„ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด๋Š” ๊ตฌ์กฐ์ ์œผ๋กœ ๋‹จ์ˆœํ•˜๊ณ , โ€œ์ƒ์„ฑ์ž 2๋ฒˆ ํ˜ธ์ถœโ€ ๋ฌธ์ œ๋ฅผ ์ œ๊ฑฐํ•˜๋ฉฐ, DI ์ปจํ…Œ์ด๋„ˆ์™€๋„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ†ตํ•ฉ๋ฉ๋‹ˆ๋‹ค.

์ฆ‰:

  • Aspect ํด๋ž˜์Šค๋Š” ์ผ๋ฐ˜ ๋นˆ์œผ๋กœ DI ์™„๋ฃŒ ํ›„ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ๋“ฑ๋ก
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นˆ์€ ํ”„๋ก์‹œ ์ธ์Šคํ„ด์Šค๋กœ ์ƒ์„ฑ์ž DI๋ฅผ ํ•œ ๋ฒˆ๋งŒ ์ˆ˜ํ–‰
  • ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด getBean() ์žฌ์ง„์ž…์„ ํ†ตํ•ด ํ•ด๊ฒฐ

์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ์ž๋Š” ํ”„๋ก์‹œ ์กด์žฌ ์—ฌ๋ถ€์— ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š๊ณ , ํ‰๋ฒ”ํ•œ ๋นˆ์ฒ˜๋Ÿผ ์˜์กด์„ฑ์„ ์ฃผ์ž…๋ฐ›๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Objenesis Fallback: ๋ž˜ํ•‘ AOP ์ง€์› ์ „๋žตโ€‹

Sprout์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹จ์ผ ์ธ์Šคํ„ด์Šคํ˜•(Subclassing Proxy) ๋ชจ๋ธ์„ ์ฑ„ํƒํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํ–ฅํ›„ ๋ž˜ํ•‘(Delegating) AOP๋ฅผ ์ง€์›ํ•ด์•ผ ํ•  ๊ฒฝ์šฐ, ๋ณ„๋„์˜ Objenesis ๊ธฐ๋ฐ˜ fallback ๊ฒฝ๋กœ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์™œ Objenesis๊ฐ€ ํ•„์š”ํ•œ๊ฐ€โ€‹

  • ์œ„์ž„ํ˜•์—์„œ๋Š” ํ”„๋ก์‹œ ์ƒ์„ฑ ์‹œ ์›๋ณธ ์ธ์Šคํ„ด์Šค๋ฅผ ์ด๋ฏธ ๊ฐ–๊ณ  ์žˆ์Œ
  • ๋งŒ์•ฝ enhancer.create(..)๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋ฉด:
    • ํ”„๋ก์‹œ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ๊ณผ์ •์—์„œ ์Šˆํผ ์ƒ์„ฑ์ž๊ฐ€ ๋‹ค์‹œ ํ˜ธ์ถœ
    • ๊ฒฐ๊ณผ์ ์œผ๋กœ ์›๋ณธ ์ƒ์„ฑ์ž ๋กœ์ง์ด 2๋ฒˆ ์‹คํ–‰๋จ (์›๋ณธ + ํ”„๋ก์‹œ)
  • ์ด๋Š” ๋ถ€์ž‘์šฉ ๋ฐœ์ƒ, final ํ•„๋“œ ์žฌํ• ๋‹น, ๋ฆฌ์†Œ์Šค ์ด์ค‘ ์ดˆ๊ธฐํ™” ๋ฌธ์ œ๋ฅผ ์•ผ๊ธฐํ•  ์ˆ˜ ์žˆ์Œ
  • ๋”ฐ๋ผ์„œ ์ƒ์„ฑ์ž ํ˜ธ์ถœ์„ ๊ฑด๋„ˆ๋›ฐ๊ณ  ๋นˆ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๊ธฐ์ˆ ์ด ํ•„์š” โ†’ Objenesis ํ™œ์šฉ

Fallback ๊ฒฝ๋กœ ์˜ˆ์‹œโ€‹

@Component
public class CglibProxyFactory implements ProxyFactory, InfrastructureBean {

@Override
public Object createProxy(Class<?> targetClass, Object target, AdvisorRegistry registry, CtorMeta meta) {
Enhancer e = new Enhancer();
e.setSuperclass(targetClass);

if (target != null) {
// Delegating Proxy ๊ฒฝ๋กœ: ์ด๋ฏธ target์ด ์กด์žฌ โ†’ Objenesis๋กœ ctor skip
e.setCallbackType(MethodInterceptor.class);
Class<?> proxyClass = e.createClass();
Object proxy = objenesis.newInstance(proxyClass); // ์ƒ์„ฑ์ž ํ˜ธ์ถœ ์ƒ๋žต
((Factory) proxy).setCallback(0, new BeanMethodInterceptor(target, registry));
return proxy;
} else {
// Subclassing Proxy ๊ฒฝ๋กœ: ํ”„๋ก์‹œ๊ฐ€ ๊ณง ๋นˆ โ†’ ctor ์ •์ƒ ํ˜ธ์ถœ
e.setCallback(new BeanMethodInterceptor(null, registry));
return e.create(meta.paramTypes(), meta.args());
}
}
}

์ „๋žต ์š”์•ฝโ€‹

  • SubClassing(๋‹จ์ผ ์ธ์Šคํ„ด์Šคํ˜•): ๊ธฐ๋ณธ ๊ฒฝ๋กœ. ํ”„๋ก์‹œ = ๋นˆ, ctor ์ •์ƒ ํ˜ธ์ถœ, ์ฃผ์ž… ๊ทธ๋Œ€๋กœ ๋ฐ˜์˜.
  • Delegating(๋ž˜ํ•‘ํ˜•): Fallback ๊ฒฝ๋กœ. ์›๋ณธ ๋ณ„๋„ ์กด์žฌ โ†’ ํ”„๋ก์‹œ๋Š” Objenesis๋กœ ์ƒ์„ฑ, ctor ์ƒ๋žต.

์ ์šฉ ์‹œ ๊ณ ๋ ค์‚ฌํ•ญโ€‹

  1. DI ์ผ๊ด€์„ฑ: Delegating ๋ชจ๋ธ์—์„œ๋Š” ์›๋ณธ ๊ฐ์ฒด์— DI๊ฐ€ ์ด๋ฏธ ์™„๋ฃŒ๋˜์–ด์•ผ ํ•จ. ํ”„๋ก์‹œ๋Š” ๋‹จ์ˆœ ์œ„์ž„์ž.
  2. ์บ์‹ฑ ์ „๋žต: (targetClass, advisorsSignature)๋ฅผ ํ‚ค๋กœ ํ”„๋ก์‹œ ํด๋ž˜์Šค๋ฅผ ์บ์‹ฑ, Objenesis ์ธ์Šคํ„ด์Šคํ™” ๋น„์šฉ ์ตœ์†Œํ™”.
  3. ์ˆœํ™˜ ์ฐธ์กฐ ์ฒ˜๋ฆฌ: ์›๋ณธ๊ณผ Aspect๊ฐ€ ์„œ๋กœ ์ฐธ์กฐํ•˜๋Š” ๊ฒฝ์šฐ, ์ปจํ…Œ์ด๋„ˆ์˜ getBean() ์žฌ์ง„์ž… ๊ตฌ์กฐ๋กœ ํ•ด๊ฒฐ ๊ฐ€๋Šฅ.
  4. ํ…Œ์ŠคํŠธ ๊ถŒ์žฅ ์‹œ๋‚˜๋ฆฌ์˜ค:
    • ์›๋ณธ ์ƒ์„ฑ์ž ๋ถ€์ž‘์šฉ์ด 1ํšŒ๋งŒ ๋ฐœ์ƒํ•˜๋Š”์ง€
    • final ํ•„๋“œ๋‚˜ ๋ฆฌ์†Œ์Šค ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์•ˆ์ „ํ•˜๊ฒŒ ์œ ์ง€๋˜๋Š”์ง€
    • Delegating/Subclassing ๋‘ ๊ฒฝ๋กœ๊ฐ€ ๋™์‹œ์— ์„ž์—ฌ๋„ ๋ฌธ์ œ์—†๋Š”์ง€

MethodInvocation ์ฒด์ธ ์‹คํ–‰ ์‹œ์Šคํ…œโ€‹

1. MethodInvocationImpl: ์ฒด์ธ ์˜ค๋ธŒ ๋ฆฌ์ŠคํŽ€์„œ๋นŒ๋ฆฌํ‹ฐ ๊ตฌํ˜„โ€‹

์–ด๋“œ๋ฐ”์ด์Šค ์ฒด์ธ ์ƒํƒœ ๊ด€๋ฆฌ

public class MethodInvocationImpl implements MethodInvocation {
private final Object target; // ์›๋ณธ ๊ฐ์ฒด
private final Method method; // ํ˜ธ์ถœ๋  ๋ฉ”์„œ๋“œ
private final Object[] args; // ๋ฉ”์„œ๋“œ ์ธ์ž
private final MethodProxy methodProxy; // CGLIB ๋ฉ”์„œ๋“œ ํ”„๋ก์‹œ
private final List<Advisor> advisors; // ์ ์šฉํ•  ์–ด๋“œ๋ฐ”์ด์ € ๋ชฉ๋ก
private int currentAdvisorIndex = -1; // ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์–ด๋“œ๋ฐ”์ด์ € ์ธ๋ฑ์Šค

@Override
public Object proceed() throws Throwable {
currentAdvisorIndex++; // ๋‹ค์Œ ์–ด๋“œ๋ฐ”์ด์ €๋กœ ์ด๋™

if (currentAdvisorIndex < advisors.size()) {
// ๋‹ค์Œ ์–ด๋“œ๋ฐ”์ด์ €์˜ Advice ์‹คํ–‰
Advisor advisor = advisors.get(currentAdvisorIndex);
return advisor.getAdvice().invoke(this); // ์žฌ๊ท€์  ์ฒด์ธ ์‹คํ–‰
} else {
// ๋ชจ๋“  ์–ด๋“œ๋ฐ”์ด์ € ์‹คํ–‰ ์™„๋ฃŒ โ†’ ์›๋ณธ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
return methodProxy.invoke(target, args);
}
}
}

์ฒด์ธ ์‹คํ–‰ ํ๋ฆ„

proceed() ํ˜ธ์ถœ
โ†“
currentAdvisorIndex++
โ†“
index < advisors.size() ?
โ”œโ”€ Yes โ†’ advisor.getAdvice().invoke(this) โ†’ ์–ด๋“œ๋ฐ”์ด์Šค ์‹คํ–‰
โ”‚ โ†“
โ”‚ proceed() ์žฌ๊ท€ ํ˜ธ์ถœ
โ”‚ โ†“
โ”‚ ๋‹ค์Œ ์–ด๋“œ๋ฐ”์ด์ € ๋˜๋Š” ์›๋ณธ ๋ฉ”์„œ๋“œ
โ””โ”€ No โ†’ methodProxy.invoke(target, args) โ†’ ์›๋ณธ ๋ฉ”์„œ๋“œ ์‹คํ–‰

2. MethodSignature: ๋ฉ”์„œ๋“œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ตœ์ ํ™”โ€‹

์ง€์—ฐ ๊ณ„์‚ฐ๊ณผ ์บ์‹ฑ ์ „๋žต

public class MethodSignature implements Signature {
private final Method method;
private volatile String cachedToString; // ๋ฌธ์ž์—ด ํ‘œํ˜„ ์บ์‹ฑ
private volatile String cachedLongName; // ๊ธด ์ด๋ฆ„ ์บ์‹ฑ

@Override
public String toLongName() {
String local = cachedLongName;
if (local == null) { // ์ฒซ ํ˜ธ์ถœ ์‹œ null
synchronized (this) { // ๋™๊ธฐํ™” ๋ธ”๋ก
if (cachedLongName == null) { // double-checked locking
cachedLongName = method.toGenericString();
}
local = cachedLongName;
}
}
return local;
}
}

์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ธฐ๋ฒ•

  1. Volatile ํ•„๋“œ: ๋ฉ”๋ชจ๋ฆฌ ๊ฐ€์‹œ์„ฑ ๋ณด์žฅ
  2. Double-Checked Locking: ๋™๊ธฐํ™” ๋น„์šฉ ์ตœ์†Œํ™”
  3. ์ง€์—ฐ ์ดˆ๊ธฐํ™”: ์‹ค์ œ ์‚ฌ์šฉ ์‹œ์ ์—๋งŒ ๊ณ„์‚ฐ
  4. ๋กœ์ปฌ ๋ณ€์ˆ˜ ํ™œ์šฉ: ์ค‘๋ณต volatile ์ฝ๊ธฐ ๋ฐฉ์ง€

DI ์ปจํ…Œ์ด๋„ˆ์™€์˜ ํ†ตํ•ฉ ๋ฉ”์ปค๋‹ˆ์ฆ˜โ€‹

1. BeanPostProcessor ๋“ฑ๋ก ์‹œ์ โ€‹

๋“ฑ๋ก ์ „๋žต

private void registerBeanPostProcessors() {
List<BeanPostProcessor> allBeanPostProcessor = beanFactory.getAllBeans(BeanPostProcessor.class);

for (BeanPostProcessor beanPostProcessor : allBeanPostProcessor) {
beanFactory.addBeanPostProcessor(beanPostProcessor);
}
}

์‹คํ–‰ ์‹œ์ : ์ธํ”„๋ผ ๋นˆ ์ดˆ๊ธฐํ™” ์™„๋ฃŒ ํ›„, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นˆ ์ดˆ๊ธฐํ™” ์ง์ „

2. ๋นˆ ์ƒ์„ฑ ๋ผ์ดํ”„์‚ฌ์ดํด๊ณผ AOP ํ†ตํ•ฉโ€‹

๋นˆ ์ƒ์„ฑ ๊ณผ์ •์—์„œ์˜ AOP ๊ฐœ์ž…

// DefaultListableBeanFactory ๋‚ด๋ถ€์˜ ๋นˆ ์ƒ์„ฑ ๊ณผ์ •
public Object createBean(BeanDefinition bd) {
// 1. ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
Object instance = instantiateBean(bd);

// 2. ์˜์กด์„ฑ ์ฃผ์ž…
injectDependencies(instance, bd);

// 3. BeanPostProcessor ์‹คํ–‰ (AOP ํฌํ•จ)
for (BeanPostProcessor processor : beanPostProcessors) {
instance = processor.postProcessAfterInitialization(bd.getName(), instance);
}

return instance;
}

3. ํ”„๋ก์‹œ์™€ ์›๋ณธ ๊ฐ์ฒด ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ณด์กดโ€‹

CtorMeta ํ™œ์šฉ

// ์›๋ณธ ๋นˆ ์ƒ์„ฑ ์‹œ ์ƒ์„ฑ์ž ์ •๋ณด ์ €์žฅ
private final Map<Object, CtorMeta> ctorCache = new IdentityHashMap<>();

// ํ”„๋ก์‹œ ์ƒ์„ฑ ์‹œ ๋™์ผํ•œ ์ƒ์„ฑ์ž ์ •๋ณด ์‚ฌ์šฉ
CtorMeta meta = container.lookupCtorMeta(bean);
return proxyFactory.createProxy(targetClass, bean, advisorRegistry, meta);

์„ฑ๋Šฅ ๋ถ„์„ ๋ฐ ์ตœ์ ํ™”โ€‹

1. ์‹œ๊ฐ„ ๋ณต์žก๋„ ๋ถ„์„โ€‹

ํ”„๋ก์‹œ ์ƒ์„ฑ ๊ฒฐ์ • ๊ณผ์ •

  • ๋ฉ”์„œ๋“œ ์ˆœํšŒ: O(m) (m = ํด๋ž˜์Šค์˜ public ๋ฉ”์„œ๋“œ ์ˆ˜)
  • ์–ด๋“œ๋ฐ”์ด์ € ๋งค์นญ: O(n) ร— O(p) (n = ์–ด๋“œ๋ฐ”์ด์ € ์ˆ˜, p = ํฌ์ธํŠธ์ปท ๋งค์นญ ๋ณต์žก๋„)
  • ์บ์‹œ ์ ์ค‘ ์‹œ: O(1) (AdvisorRegistry ์บ์‹ฑ ํ™œ์šฉ)

๋ฉ”์„œ๋“œ ์ธํ„ฐ์…‰์…˜ ๊ณผ์ •

  • ์–ด๋“œ๋ฐ”์ด์ € ์กฐํšŒ: O(1) (์บ์‹œ ์ ์ค‘ ์‹œ)
  • ์ฒด์ธ ์‹คํ–‰: O(a) (a = ์ ์šฉ ๊ฐ€๋Šฅํ•œ ์–ด๋“œ๋ฐ”์ด์ € ์ˆ˜)
  • ์›๋ณธ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ: O(1) (CGLIB MethodProxy ์ง์ ‘ ํ˜ธ์ถœ)

2. ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ์ตœ์ ํ™”โ€‹

์บ์‹ฑ ์ „๋žต

// AdvisorRegistry์—์„œ ๋ฉ”์„œ๋“œ๋ณ„ ์บ์‹ฑ
private final Map<Method, List<Advisor>> cachedAdvisors = new ConcurrentHashMap<>();

// MethodSignature์—์„œ ๋ฌธ์ž์—ด ํ‘œํ˜„ ์บ์‹ฑ
private volatile String cachedToString;
private volatile String cachedLongName;

๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์„ฑ

  1. ConcurrentHashMap: ์ฝ๊ธฐ ์ค‘์‹ฌ ์ตœ์ ํ™”
  2. IdentityHashMap: ๊ฐ์ฒด ๋™์ผ์„ฑ ๊ธฐ๋ฐ˜ ๋น ๋ฅธ ์กฐํšŒ
  3. AtomicBoolean: ์ดˆ๊ธฐํ™” ์ค‘๋ณต ๋ฐฉ์ง€
  4. Volatile ์บ์‹ฑ: ์ง€์—ฐ ์ดˆ๊ธฐํ™”์™€ ๋ฉ”๋ชจ๋ฆฌ ๊ฐ€์‹œ์„ฑ

3. CGLIB vs JDK ๋™์  ํ”„๋ก์‹œ ๋น„๊ตโ€‹

ํŠน์„ฑCGLIBJDK ๋™์  ํ”„๋ก์‹œ
๊ธฐ๋ฐ˜ ๊ธฐ์ˆ ๋ฐ”์ดํŠธ์ฝ”๋“œ ์ƒ์„ฑ๋ฆฌํ”Œ๋ ‰์…˜
์ธํ„ฐํŽ˜์ด์Šค ์š”๊ตฌ๋ถˆํ•„์š”ํ•„์ˆ˜
์ƒ์† ๊ธฐ๋ฐ˜ํด๋ž˜์Šค ์ƒ์†์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„
์„ฑ๋Šฅ๋น ๋ฆ„ (์ง์ ‘ ํ˜ธ์ถœ)๋А๋ฆผ (๋ฆฌํ”Œ๋ ‰์…˜)
final ๋ฉ”์„œ๋“œ์ธํ„ฐ์…‰ํŠธ ๋ถˆ๊ฐ€ํ•ด๋‹น ์—†์Œ
์ƒ์„ฑ์ž ์ง€์›์ง€์›๋ฏธ์ง€์›

Sprout์ด CGLIB๋ฅผ ์„ ํƒํ•œ ์ด์œ 

  1. ์ธํ„ฐํŽ˜์ด์Šค ๋…๋ฆฝ์„ฑ: ๋น„์ฆˆ๋‹ˆ์Šค ํด๋ž˜์Šค์— ์ธํ„ฐํŽ˜์ด์Šค ๊ฐ•์ œ ๋ถˆํ•„์š”
  2. ์„ฑ๋Šฅ ์šฐ์„ : MethodProxy๋ฅผ ํ†ตํ•œ ์ง์ ‘ ํ˜ธ์ถœ๋กœ ์„ฑ๋Šฅ ์ตœ์ ํ™”
  3. ์ƒ์„ฑ์ž ์ง€์›: DI์™€ ์ž์—ฐ์Šค๋Ÿฌ์šด ํ†ตํ•ฉ

Spring AOP์™€์˜ ๋น„๊ตโ€‹

์•„ํ‚คํ…์ฒ˜ ์ฐจ์ด์ โ€‹

์ธก๋ฉดSpring AOPSprout AOP
ํ”„๋ก์‹œ ์ƒ์„ฑ ์‹œ์ BeanPostProcessorBeanPostProcessor
์ธํ”„๋ผ ์ดˆ๊ธฐํ™”BeanFactoryPostProcessorPostInfrastructureInitializer
Aspect ์Šค์บ”์ปดํฌ๋„ŒํŠธ ์Šค์บ” ํ†ตํ•ฉ๋ณ„๋„ Reflections ์Šค์บ”
์–ด๋“œ๋ฐ”์ด์ € ๋“ฑ๋ก์ž๋™ + BeanDefinition๋ช…์‹œ์  ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ
ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌProxyFactory (๋ณต์žก)CglibProxyFactory (๋‹จ์ˆœ)
๋ฉ”์„œ๋“œ ์ฒด์ธReflectiveMethodInvocationMethodInvocationImpl

์„ค๊ณ„ ์ฒ ํ•™ ์ฐจ์ดโ€‹

Spring AOP

  • ๋ณต์žกํ•˜๊ณ  ์œ ์—ฐํ•œ ํ”„๋ก์‹œ ์ƒ์„ฑ ์ „๋žต
  • ๋‹ค์–‘ํ•œ ํ”„๋ก์‹œ ํƒ€์ž… ์ง€์› (JDK + CGLIB)
  • BeanDefinition ๊ธฐ๋ฐ˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ

Sprout AOP

  • ๋‹จ์ˆœํ•˜๊ณ  ๋ช…ํ™•ํ•œ ํ”„๋ก์‹œ ์ƒ์„ฑ ์ „๋žต
  • CGLIB๋งŒ ์ง€์›ํ•˜์—ฌ ๋ณต์žก์„ฑ ์ œ๊ฑฐ
  • ๋ช…์‹œ์  ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ํŒจํ„ด์œผ๋กœ ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ

ํ™•์žฅ์„ฑ๊ณผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•โ€‹

1. ์ƒˆ๋กœ์šด ProxyFactory ๊ตฌํ˜„โ€‹

@Component
public class CustomProxyFactory implements ProxyFactory, InfrastructureBean {
@Override
public Object createProxy(Class<?> targetClass, Object target, AdvisorRegistry registry, CtorMeta meta) {
// JDK ๋™์  ํ”„๋ก์‹œ ๋˜๋Š” ๋‹ค๋ฅธ ํ”„๋ก์‹œ ๊ธฐ์ˆ  ์‚ฌ์šฉ
return createCustomProxy(targetClass, target, registry);
}
}

2. ์ปค์Šคํ…€ PostInfrastructureInitializerโ€‹

@Component
public class CustomAopInitializer implements PostInfrastructureInitializer {
@Override
public void afterInfrastructureSetup(BeanFactory beanFactory, List<String> basePackages) {
// ์ปค์Šคํ…€ AOP ์ดˆ๊ธฐํ™” ๋กœ์ง
initializeCustomAspects();
}
}

3. BeanPostProcessor ์ฒด์ธ ํ™•์žฅโ€‹

@Component
@Order(100) // AspectPostProcessor ์ดํ›„ ์‹คํ–‰
public class CustomPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(String beanName, Object bean) {
// ์ถ”๊ฐ€์ ์ธ ํ›„์ฒ˜๋ฆฌ ๋กœ์ง
return enhanceBean(bean);
}
}

๋””๋ฒ„๊น…๊ณผ ๋ชจ๋‹ˆํ„ฐ๋งโ€‹

1. AOP ์ ์šฉ ์—ฌ๋ถ€ ํ™•์ธโ€‹

// AspectPostProcessor์—์„œ ํ”„๋ก์‹œ ์ƒ์„ฑ ์‹œ ๋กœ๊น…
if (needsProxy) {
System.out.println("Applying AOP proxy to bean: " + beanName + " (" + targetClass.getName() + ")");
// ...
}

2. Advisor ๋“ฑ๋ก ํ˜„ํ™ฉ ์ถ”์ โ€‹

// AspectPostProcessor์—์„œ Advisor ๋“ฑ๋ก ํ›„ ๋กœ๊น…
System.out.println(aspectClass.getName() + " has " + advisorsForThisAspect.size() + " advisors: " + advisorsForThisAspect);
System.out.println("advisorRegistry#getAllAdvisors()" + advisorRegistry.getAllAdvisors());

3. ๋ฉ”์„œ๋“œ ์ธํ„ฐ์…‰์…˜ ๋ชจ๋‹ˆํ„ฐ๋งโ€‹

// BeanMethodInterceptor์—์„œ ์ธํ„ฐ์…‰์…˜ ๋ฐœ์ƒ ์‹œ ๋กœ๊น…
if (!applicableAdvisors.isEmpty()) {
System.out.println("Intercepting method: " + method.getName() + " with " + applicableAdvisors.size() + " advisors");
}

๋ณด์•ˆ ๊ณ ๋ ค์‚ฌํ•ญโ€‹

1. CGLIB ๊ธฐ๋ฐ˜ ํ”„๋ก์‹œ์˜ ์ œํ•œ์‚ฌํ•ญโ€‹

๋ณด์•ˆ ์ œ์•ฝ

  • final ํด๋ž˜์Šค: CGLIB๋กœ ํ”„๋ก์‹œ ์ƒ์„ฑ ๋ถˆ๊ฐ€
  • final ๋ฉ”์„œ๋“œ: ์˜ค๋ฒ„๋ผ์ด๋“œ ๋ถˆ๊ฐ€๋กœ ์ธํ„ฐ์…‰์…˜ ๋ถˆ๊ฐ€
  • private ๋ฉ”์„œ๋“œ: ํ”„๋ก์‹œ์—์„œ ์ ‘๊ทผ ๋ถˆ๊ฐ€
  • ์ƒ์„ฑ์ž ํ˜ธ์ถœ: ์›๋ณธ ๊ฐ์ฒด์˜ ์ƒ์„ฑ์ž๊ฐ€ ๋‘ ๋ฒˆ ํ˜ธ์ถœ๋จ

2. ๊ถŒํ•œ ๊ฒ€์ฆ ๊ฐ•ํ™”โ€‹

// AspectPostProcessor์—์„œ ํ”„๋ก์‹œ ์ƒ์„ฑ ์ „ ๊ถŒํ•œ ๊ฒ€์ฆ
if (needsProxy && !hasProxyPermission(targetClass)) {
throw new SecurityException("Proxy creation not allowed for: " + targetClass.getName());
}

Sprout์˜ AOP์™€ DI/IoC ํ†ตํ•ฉ ์‹œ์Šคํ…œ์€ Spring์˜ ๋ณต์žกํ•œ ํ”„๋ก์‹œ ์ƒ์„ฑ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๊ต์œก์  ๋ชฉ์ ์— ๋งž๊ฒŒ ๋‹จ์ˆœํ™”ํ•˜๋ฉด์„œ๋„, ์‹ค์ œ AOP์˜ ํ•ต์‹ฌ ์›๋ฆฌ๋ฅผ ๋ช…ํ™•ํžˆ ๋ณด์—ฌ์ฃผ๋Š” ๊ตฌ์กฐ๋กœ ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ธํ”„๋ผ ๋นˆ์˜ ์šฐ์„  ์ดˆ๊ธฐํ™”, PostInfrastructureInitializer ํŒจํ„ด, BeanPostProcessor ์ฒด์ธ, ๊ทธ๋ฆฌ๊ณ  CGLIB ๊ธฐ๋ฐ˜ ํ”„๋ก์‹œ ์ƒ์„ฑ์„ ํ†ตํ•ด ํˆฌ๋ช…ํ•˜๊ณ  ํšจ์œจ์ ์ธ AOP ํ†ตํ•ฉ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

ํ™•์žฅ์„ฑ๊ณผ ๋””๋ฒ„๊น… ํŽธ์˜์„ฑ์„ ๊ณ ๋ คํ•œ ์„ค๊ณ„๋กœ ๊ฐœ๋ฐœ์ž๋“ค์ด AOP์˜ ๋‚ด๋ถ€ ๋™์ž‘์„ ์‰ฝ๊ฒŒ ์ดํ•ดํ•˜๊ณ  ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.