์ด์ ์ ์์ฑํ๋ Spring AOP ๋ธ๋ก๊ทธ ๊ธ ์ฎ๊ธด ๋ด์ฉ
1. AOP ??

Spring AOP๋ ์คํ๋ง์์ ์ง์ํ๋ AOP(Aspect Oriented Programming)์ ๋งํ๋ค. Aspect๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์๋ฏธํ๋ค. ๋ถ๊ฐ ๊ธฐ๋ฅ์ ๋น์ฆ๋์ค ๋ก์ง๋ฅผ ๋ด๋นํ์ง๋ ์์ง๋ง ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ง๋ณด์์ ์์ ์ฑ์ ์ํด์๋ ํ์ํ ๊ธฐ๋ฅ์ด๋ค. ๋ํ์ ์ธ ์์๋ก ๋ก๊น ๊ณผ ํธ๋์ญ์ ์ด๋ค. ๋ง์ฝ AOP๋ฅผ ์ฌ์ฉํ์ง ์์ผ๋ฉด ๋น์ฆ๋์ค ๋ก์ง ์ฌ์ด์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ๊ด๋ จ๋ ์ฝ๋๋ฅผ ์ถ๊ฐํด์ผ ํ๋ค. ๋น์ฆ๋์ค ๋ก์ง ์ฌ์ด์ ์ถ๊ฐ๋ ๋ถ๊ฐ ๊ธฐ๋ฅ ์ฝ๋๋ค์ ๋น์ฆ๋์ค ๋ก์ง์ ๊ฐ๋ ์ฑ์ ๋จ์ด๋จ๋ฆฌ๊ณ ๊ฐ์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ๋ํ ์ฝ๋ ์ค๋ณต์ด ๋ฐ์ํด ์ ์ง๋ณด์๊ฐ ์ด๋ ต๋ค. AOP์ ํตํด ๋น์ฆ๋์ค ๋ก์ง๊ณผ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ๋ถ๋ฆฌํ๊ณ ์ฝ๋๋ฅผ ํ ๊ณณ์์ ๊ด๋ฆฌํด ๋ก์ง์ ์์ง๋์ ์ ์ง๋ณด์์ฑ์ ๋ํ ์ ์๋ค.
2. Spring AOP ์ค์ ๋ฐฉ๋ฒ
์คํ๋ง ๋ถํธ์์ ์คํ๋ง AOP๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ์์กด์ฑ ํ๋๋ง ์ถ๊ฐํ๋ฉด Spring AOP๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ์๋ ์์กด์ฑ์ ์ถ๊ฐํ๋ฉด aspectj.weaver์ aspectJ ๊ด๋ จ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฑ๋กํด asspectj์์ ์ ๊ณตํ๋ ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ ์ ์๋ค.(e.g. @Pointcut, @AfterThrowing, @Around...)
implementation 'org.springframework.boot:spring-boot-starter-aop'
์คํ๋ง ๋ถํธ์์ AOP ๊ด๋ จ ํด๋์ค๋ฅผ ์๋์ผ๋ก ๋น์ผ๋ก ๋ฑ๋กํ๊ณ ์คํํ ์ค์ ํด๋์ค๋ค์ ์ด๋ฆ์ ๋ด๊ณ ์์ด ๊ด๋ จ ์ค์ ํด๋์ค๋ฅผ ์คํ์ํจ๋ค.(at org.springframework.boot.autoconfigure.AutoConfiguration.imports) ์ด ์ค ์คํ๋ง AOP ์ค์ ์ ๋ด๋นํ๋AopAutoConfiguration ํด๋์ค๋ฅผ ํธ์ถ ๋ฐ ์คํ์์ผ AOP ๊ด๋ จ ์ค์ ์ ์๋์ผ๋ก ์ง์ํ๋ค.
//META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
@AutoConfiguration
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
static class AspectJAutoProxyingConfiguration {
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")
static class JdkDynamicAutoProxyConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class CglibAutoProxyConfiguration {
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.aspectj.weaver.Advice")
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class ClassProxyingConfiguration {
@Bean
static BeanFactoryPostProcessor forceAutoProxyCreatorToUseClassProxying() {
return (beanFactory) -> {
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
};
}
}
}
3. Spring AOP ๊ฐ GCLib proxy ๋ฅผ ์ ํํ ์ด์
์คํ๋ง AOP๋ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ์ผ๋ก ํ๋ก์๋ฅผ ์ฌ์ฉํ๋ค. ์คํ๋ง์์ ์ง์ํ๋ ํ๋ก์๋ฅผ ์์ฑํ๋ ๋ฐฉ์ ์๋์ ๊ฐ๋ค.
- JDK Dynamic Proxy : Java Reflection ์์ ์ ๊ณตํ๋ Proxy ๋ฅผ ์์ฑํ๋ ๋ฐฉ์ (์ธํฐํ์ด์ค ๊ธฐ๋ฐ์ผ๋ก ํ๋ก์ ์์ฑ)
- CGLlib(Code Generation Library) : ํด๋์ค์ ๋ฐ์ดํธ์ฝ๋๋ฅผ ์กฐ์ํ์ฌ Proxy ๋ฅผ ์์ฑํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (๊ตฌ์ฒด ํด๋์ค ๊ธฐ๋ฐ์ผ๋ก ํ๋ก์ ์์ฑ)

[1] CGLib proxy ์ ์ฌ์ฉํ๊ธฐ ์ํ ๊ฐ์ ๊ณผ์
Spring 4.3(SpringBoot 1.4) ๋ฒ์ ์ด์ ์๋ JDK Dynamic Proxy ๋ฅผ ๊ธฐ๋ณธ ํ๋ก์๋ก ์ฌ์ฉํ์ง๋ง ์ดํ์๋ CGLib์ ์ฌ์ฉํ๊ณ ์๋ค. ์ด์ ๋ฒ์ ๊น์ง๋ CGLib ๋ฅผ ์ฌ์ฉํ๋๋ฐ ๋ณต์กํจ์ด ์กด์ฌํ์ง๋ง ๋ค์๊ณผ ๊ฐ์ด ํด๊ฒฐ๋์๋ค.
- ์ธ๋ถ ์์กด์ฑ(net.sf.cglib.proxy.Enhancer.proxy)๋ฅผ ์ถ๊ฐํด์ผ ํ๋ค. ํ์ง๋ง spring version 3.2 ๋ถํฐ CGLib์ Spring Core ํจํค์ง์ ํฌํจ์์ผ ๋์ด์ ์์กด์ฑ์ ์ถ๊ฐํ์ง ์์๋ ๋๋ค.
- default constructor ๋ฅผ ์ ์ธํ๊ณ ์์ฑ์๋ฅผ ๋๋ฒ ํธ์ถํด์ผ ํ๋ ๋ฌธ์ ๊ฐ ์์์ง๋ง, spring version 4 ๋ถํฐ Objensis ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋์์ ๋ฐ์ ์ด ๋ฌธ์ ๋ํ ๊ฐ์ ๋์๋ค.
[2] CGLib proxy ์ ์ฌ์ฉํ ์ด์ : ์ฑ๋ฅ
CGLib ์ ์ฌ์ฉํ๊ฒ ๋ ๊ฐ์ฅ ์ฃผ๋ ์ด์ ๋ ์ฑ๋ฅ์ด๋ค. CGLib์ ํ๊น ํด๋์ค์ ๋ํ ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์กฐ์ํ๊ณ ์ดํ์ ํธ์ถ์์ ์กฐ์๋ ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์ฌ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ relection์ ์ฌ์ฉํ๋ JDK Dynamic Proxy์ ๋นํด ์ฑ๋ฅ์ด ๋น ๋ฅด๋ค. (AOP Benchmark) AopAutoConfiguration์ ํ์ธํด๋ณด๋ฉด Default๋ก Cglib ํ๋ก์ ์์ฑ ๋ฐฉ์์ ์ฌ์ฉํ๊ณ ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
@AutoConfiguration
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
static class AspectJAutoProxyingConfiguration {
...
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class CglibAutoProxyConfiguration {
}
}
...
}
4. ProxyFactory
ProxyFactory๋ ๋์ ํ๋ก์๋ฅผ ํตํฉํ์ฌ ํธ๋ฆฌํ๊ฒ ์์ฑํ๋ ์ญํ ์ ํ๋ค. ProxyFactory ๋ฅผ ์ฌ์ฉํ๋ฉด ๋์ ํ๋ก์ ๊ธฐ์ ์ ์ ๊ฒฝ์ฐ์ง ์๊ณ Proxy ๋ฅผ ์ฌ์ฉํ ์ ์๋ ์ฅ์ ์ด ์๋ค. ๊ฐ๊ฐ์ ๋์ ํ๋ก์ ๊ธฐ์ ์ ํด๋น Advisor ๋ฅผ ํตํด ํ๋ก์ ์์ฑ์ ์์ํ๋๋ฐ ๊ฐ ๋์ ํ๋ก์๋ ํด๋น ํธ๋ค๋ฌ์๊ฒ ํ๋ก์ ์์ฑ ์ญํ ์ ์์ํ์ฌ ๋์ ํ๋ก์ ๊ธฐ์ ์ ์ ๊ฒฝ์ฐ์ง ์๊ณ Proxy๋ฅผ ์์ฑํ ์ ์๋ค
- JDK Dynamic Proxy : (JDKDynamicProxy) adviceInvocationHandler
- CGLIB : (CglibAopProxy) adviceMethodInterceptor
package org.springframework.aop.framework;
public class ProxyFactory extends ProxyCreatorSupport {
...
public ProxyFactory(Class<?> proxyInterface, Interceptor interceptor) {
addInterface(proxyInterface);
addAdvice(interceptor);
}
}
@Configuration
public class ProxyFactoryConfig {
@Bean
public OrderServiceV1 orderServiceV1(LogTrace logTrace) {
OrderServiceV1 orderService = new OrderServiceV1Impl(orderRepositoryV1(logTrace));
ProxyFactory factory = new ProxyFactory(orderService);
factory.addAdvisor(getAdvisor(logTrace));
OrderServiceV1 proxy = (OrderServiceV1) factory.getProxy();
log.info("ProxyFactory proxy={}, target={}", proxy.getClass(), orderService.getClass());
return proxy;
}
private Advisor getAdvisor(LogTrace logTrace) {
//pointcut
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedNames("request*", "order*", "save*");
//advice
LogTraceAdvice advice = new LogTraceAdvice(logTrace);
return new DefaultPointcutAdvisor(pointcut, advice);
}
}
5. BeanPostProcessor
BeanPostProcessor๋ ๋น์ ์์ฑํ๋ ๊ณผ์ ์์ ProxyFactory์์ ์์ฑํ ํ๋ก์๋ก ๋ฑ๋กํ๋ ์์ ์ ์งํํ๋ค. ์๋์ผ๋ก ๋น์ ํ๋ก์ ๊ฐ์ฒด๋ก ๋์ฒดํด์ฃผ๊ธฐ ๋๋ฌธ์ ํ๋ก์๋ฅผ ๋น์ผ๋ก ๋ฑ๋กํด์ผํ๋ ๋ฒ๊ฑฐ๋ก์์ ์ค์ผ ์ ์๋ค.
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
6. Weaving

์๋น(weaving)์ ๋น์ฆ๋์ค ๋ก์ง์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ๊ฒ์ ์๋ฏธํ๋ค. ์๋น ๋ฐฉ๋ฒ์๋ ์ปดํ์ผ ์์ , ํด๋์ค ๋ก๋ฉ ์์ , ๋ฐํ์ ์์ (ํ๋ก์)์ด ์๋ค. ํ์ฌ ์คํ๋ง AOP๋ ๋ฐํ์ ์๋น์ ์ ํํ๊ณ ์๋ค. ๋ฐํ์ ์๋น์ ์ ํํ ์ด์ ๋ ์ค์ ์ด ํธ๋ฆฌํ๊ธฐ ๋๋ฌธ์ด๋ค. ์ปดํ์ผ ์์ ๊ณผ ํด๋์ค ๋ก๋ฉ ์์ ์๋น์ ์ฌ์ฉํ๊ธฐ ์ํด์๋ AspectJ ์ปดํ์ผ๋ฌ, AspectJ ํด๋์ค ๋ก๋ ์กฐ์๊ธฐ(๋ฐ์ดํธ ์ฝ๋ ์กฐ์)์ด ํ์ํ๋ค.
'๐ backend > spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| hibernate.query.in_clause_parameter_padding = true ? (1) | 2024.03.06 |
|---|---|
| JPA ์ UUID version 7 ์ ์ฉํ๊ธฐ (0) | 2024.03.04 |
| [JPA] ์ฝ๋๋ก ๋ณด๋ OSIV ์ ๊ณ ์ฐฐ (2) | 2024.01.30 |
| QueryDSL transform() ์์ ๊ผญ @Transactional ์ ์ฌ์ฉํด์ผ ํ๋ ์ด์ (0) | 2024.01.29 |
| ํ ์คํธ์์ @Sql ๋ก ํ ์คํธ ๋ฐ์ดํฐ๊ฐ ๋ค์ด๊ฐ์ง ์์๋ ์ด์ (with. custom TestExecutionListener) (1) | 2024.01.24 |
