π‘ Spring ν΅μ¬ κ°λ μΈν°λ·° Q&A
AOPλ₯Ό μ μ©ν λμ κ°μ²΄(Bean)κ° μΈν°νμ΄μ€λ₯Ό ꡬννκ³ μμΌλ©΄, μ€νλ§μ JDK λμ νλ‘μλ₯Ό μ¬μ©νλ€.
[ν΄λΌμ΄μΈνΈ] -> [νλ‘μ] -> [μ€μ κ°μ²΄]
μ€μ κ°μ²΄λ₯Ό νλ‘μκ°μ²΄λ‘ κ°μΈμ
λ©μλ μ€ν μ ν λ‘κ·Έ
νΈλμμ
μ μΆκ°νκΈ° μν΄ μ€νλ§μ νλ‘μ κ°μ²΄λ₯Ό μμ±νλ€.
login() λ©μλ μ€ν β λ‘κ·Έ μ°κ³ β μ§μ§ login μ€ν β λ€μ λ‘κ·Έ μ°κΈ°

implements νκ³ μλμ§κ° μ ν κΈ°μ€μ΄λ€.Proxy.newProxyInstance()
1. μ μ μλΉμ€λ¨μ μΈν°νμ΄μ€λ‘ μ μΈνμλ€.
public interface MemberService {
void login(String name);
}
2. MemberService μΈν°νμ΄μ€λ₯Ό μμν κ°μ²΄ (MemberServiceImpl)
public class MemberServiceImpl implements MemberService {
public void login(String name) {
System.out.println(name + " λ‘κ·ΈμΈ");
}
}
3. νλ‘μ μμ±
MemberService target = new MemberServiceImpl();
MemberService proxy = (MemberService) Proxy.newProxyInstance(
MemberService.class.getClassLoader(),
new Class[]{MemberService.class}, // β
μΈν°νμ΄μ€κ° λ°λμ νμ
(proxyObj, method, args) -> {
System.out.println(">> νΈμΆ μ ");
Object result = method.invoke(target, args);
System.out.println(">> νΈμΆ ν");
return result;
} );
[ν΄λΌμ΄μΈνΈ] -> [JDK Proxy] -> [MemberServiceImpl(μ€μ κ°μ²΄)]
μ¬μ©λͺ©μ
public class MemberService {
public void login(String name) { ... }
}
java.lang.IllegalArgumentException: com.example.MemberService is not an interface
// MemberService β MemberService$$CGLIB$$Proxy λΌλ ν΄λμ€λ₯Ό λ§λ€μ΄μ login()μ μ€λ²λΌμ΄λ
CGLIBμ ν΄λμ€ μ체λ₯Ό μμν΄μ μμ ν΄λμ€λ₯Ό λ§λ€κ³ , λ©μλλ₯Ό μ€λ²λΌμ΄λ©ν΄μ κ°λ‘μ±λ λ°©μμΌλ‘ νλ‘μλ₯Ό λ§λλλ€.
μ¦, μΈν°νμ΄μ€κ° μμ΄λ νλ‘μλ₯Ό λ§λ€ μ μμ΅λλ€.
1.μ€μ ν΄λμ€
public class MemberService {
public void login(String name) {
System.out.println(name + " λ‘κ·ΈμΈ μλ£");
}
}
2. CGLIB νλ‘μ μμ± μ½λ
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MemberService.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println(">> λ‘κ·ΈμΈ μλ (Before)");
Object result = proxy.invokeSuper(obj, args); // μλ³Έ λ©μλ νΈμΆ
System.out.println(">> λ‘κ·ΈμΈ μλ£ (After)");
return result; }
}
);
MemberService proxy = (MemberService) enhancer.create(); // νλ‘μ κ°μ²΄ μμ±
proxy.login("iamUser");
}
}
3. CGLIB λ΄λΆλμ
class MemberService$$EnhancerByCGLIB extends MemberService {
public void login(String name) {
System.out.println(">> Before");
super.login(name);
System.out.println(">> After");
}
}
4. μ€νκ²°κ³Ό
>> λ‘κ·ΈμΈ μλ (Before)
iamUser λ‘κ·ΈμΈ μλ£
>> λ‘κ·ΈμΈ μλ£ (After)
@Transactional, @Async, @Cacheable κ°μ AOP μ΄λ
Έν
μ΄μ
μ μ© μ μΈν°νμ΄μ€κ° μμΌλ©΄ CGLIB μ¬μ©μμ
1. νλ‘μ νΈ κ΅¬μ‘°
com.example.demo
βββ AppConfig.java
βββ MemberService.java
βββ LogAspect.java
βββ DemoApplication.java
2. μμ‘΄μ± (Spring Boot + AOP)
3. AOP λμ ν΄λμ€ β μΈν°νμ΄μ€ μμ
package com.example.demo;
import org.springframework.stereotype.Service;
@Service
public class MemberService {
public void login(String name) {
System.out.println(name + " λ‘κ·ΈμΈ μ±κ³΅");
}
}
4. AOP μ€μ : Aspect μ μ
package com.example.demo;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
@Before("execution(* com.example.demo.MemberService.*(..))")
public void logBefore() {
System.out.println(">> AOP: λ‘κ·ΈμΈ μ μ λ‘κ·Έ μΆλ ₯");
}
}
5. μ€ν
package com.example.demo;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public CommandLineRunner runner(MemberService memberService) {
return args -> {
System.out.println("νλ‘μ ν΄λμ€: " + memberService.getClass());
memberService.login("iamUserser");
};
}
}
6. μ€νκ²°κ³Ό
νλ‘μ ν΄λμ€: class com.example.demo.MemberService$$SpringCGLIB$$0
>> AOP: λ‘κ·ΈμΈ μ μ λ‘κ·Έ μΆλ ₯
iamUserser λ‘κ·ΈμΈ μ±κ³΅
Enhancer.create()
| νλ‘μ ν΄λμ€ μ΄λ¦ | $$SpringCGLIB$$μ΄ λΆμ β CGLIB μ¬μ© |
|---|---|
| λ‘κ·Έ μΆλ ₯ μμ | @Before Aspectκ° login() μ€ν μ μ μλν¨ |
| μΈν°νμ΄μ€ μμ | κ·Έλμ JDK νλ‘μκ° μλ CGLIB λ°©μ μ¬μ© |
κ²°λ‘
μμ
@Service
public class MemberService {
public void login() {
System.out.println("λ‘κ·ΈμΈ μ²λ¦¬");
validate(); // λ΄λΆ λ©μλ νΈμΆ
}
public void validate() {
System.out.println("μ ν¨μ± κ²μ¬");
}
}
@Aspect
@Component
public class LogAspect {
@Before("execution(* com.example.demo.MemberService.validate(..))")
public void beforeValidate() {
System.out.println("AOP: validate() νΈμΆ μ λ‘κ·Έ");
}
}
μ€νκ²°κ³Ό
this.validate()λ νλ‘μλ₯Ό μ κ±°μΉκ³ κ·Έλ₯ μκΈ° μμ μ νΈμΆνκΈ° λλ¬Έμ
λλ€.μ΄μ
AOP μλ ꡬ쑰
[Client]
β
[νλ‘μ κ°μ²΄] β Advice μ€ν β Target κ°μ²΄μ λ©μλ νΈμΆ
νλ‘μ μ€ν¨νκ² λλ thisνΈμΆ(μκΈ°μμ νΈμΆ)
[μκΈ° ν΄λμ€ μμμ]
β
[λ°λ‘ μκΈ° λ©μλ νΈμΆ] β νλ‘μ μ κ±°μΉ¨ β AOP μ μ© μ λ¨
ν΄κ²° λ°©λ² (μΈλΆμμ νΈμΆνλλ‘ κ΅¬μ‘° λ³κ²½)
memberServiceλ νλ‘μλ₯Ό ν΅ν΄ λ©μλκ° νΈμΆλλ―λ‘ AOPκ° μλν¨@Component
public class LoginManager {
@Autowired
private MemberService memberService;
public void loginProcess() {
memberService.login(); // νλ‘μλ₯Ό ν΅ν΄ νΈμΆ
}
}
μ 리
@Transactionalμ νλ‘μ κΈ°λ° AOPλ‘ λμν©λλ€.this.save()μ²λΌ μ§μ νΈμΆνλ©΄ νλ‘μλ₯Ό μ°ννκ² λμ΄ νΈλμμ
μ΄ μ μ©λμ§ μμ΅λλ€μλλ μμ
@Service
public class OrderService {
@Transactional
public void save() { ... }
public void process() {
save(); // this.save() β νλ‘μ μ κ±°μΉ¨ β @Transactional 무ν¨
}
}
ν΄κ²°λ² : νΈμΆμ μΈλΆ BeanμΌλ‘ λΆλ¦¬
@Service
public class OrderProcessor {
@Autowired
private OrderService orderService;
public void process() {
orderService.save(); // νλ‘μ ν΅ν΄ νΈμΆλ¨ β @Transactional μ μ©λ¨
}
}
@Transactional λμ μμ
TransactionManager.begin() νΈμΆcommit(), μμΈ μ rollback()