https://www.youtube.com/watch?v=MFckVKrJLRQ
https://velog.io/@suhongkim98/AOP
클라이언트로부터 타겟에 대신해서 요청을 받는 대리인
타겟은 프록시를 통해 최종적으로 요청받아 처리
타겟은 자신의 기능에만 집중 (프록시가 나머지 처리)
public class Client{
public static void main(String[] args) {
Hello hello = new Hello();
hello.sayHello("깜빡");
}
}
public class Hello {
public String sayHello(String name){
return "Hello" + name;
}
public String sayThankyou(String name){
return "Thank you" + name;
}
}
client와 hello클래스 코드를 크게 손대지 않고 대문자로 출력하고 싶다.
어떻게 해야할까?
먼저 Hello를 인터페이스로 변경한다.
인터페이스를 필드로 가지고 있어야 한다
jdk프록시는 Method라는 reflection API를 사용한다.
리플렉션은 동적일 때 해결되는 타입을 포함하므로 JVM Optimization이 동작하지 않아 느리다
https://velog.io/@joosing/toby-spring-6-aop-history
public interface Hello{
String sayHello(String name);
String sayThankyou(String name);
}

public class HelloProxy implements Hello{
private Hello hello;
public HelloProxy(Hello hello){
this.hello = hello;
}
@Override
public String sayHello(String name){
return hello.sayHello(name).toUpperCase();
}
@Override
public String sayThankyou(String name){
return hello.sayThankyou(name).toUpperCase();
}
}
import java.lang.reflect.Method;
public class UpperCaseHandler implements InvocationHandler{
private final Object target;
public UpperCaseHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
if(method.getName().startsWith("say")){
return ((String) method.invoke(target, args)).toUpperCase();
}
return method.invoke(target, args);
}
}
@Test
public void dynamicProxy() {
HelloTarget helloTarget = new HelloTarget();
Hello hello = (Hello) Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class[] { Hello.class }, // 프록시가 구현할 타겟 인터페이스
new UpperCaseHandler(helloTarget, "say") // 부가기능 구현
);
Assertions.assertEquals("HELLO TOBY", hello.sayHello("Toby"));
Assertions.assertEquals("THANK YOU TOBY", hello.sayThankYou("Toby"));
}
다이내믹 프록시가 만들어질 때 추가된 메서드가 자동으로 포함되고 UppercaseHandler.invoke() 메서드로의 연결을 수행하는 코드가 자동으로 만들어 질 것이고,
부가기능은 Invoke() 메서드에서 처리 될 것이다.
이제 부가기능을 담당하는 코드는 UppercaseHandler로 분리되었고, 다이나믹 프록시의 각 메서드에서 공유해서 사용된다.
바이트코드를 조작하여 프록시 객체를 생성해주는 코드 생성(Code gen)라이브러리

모듈화한 부가 기능을 타겟에 적용해 핵심 기능과 연결하는 과정
런타임 시 Proxy를 생성하여 기능을 추가하기 때문에 바이트 코드와 소스 코드 사이에 차이가 거의 없다.
메소드 호출에 대해서만 어드바이스를 적용 할 수 있다는 단점
특수한 컴파일러를 활용해 컴파일 과정에서 바이트 코드 조작을 통해 Advisor 코드를 직접 삽입
java 파일을 컴파일한 결과물 자바 클래스가 JVM에 로드될 때 바이트 코드 조작
컴파일 후에 생성된 class파일을 바이트코드 조작을 통해 위빙
스프링 컨테이너 안의 Bean에만 적용가능하다.
Spring AOP는 Dynamic Proxy 기반으로 기본적으로 RTW를 사용한다.
AspectJ에 비해 가볍지만, 대부분의 기능을 구현할 수 있다.
Spring AOP가 성능과 기능은 매우 부족하지만, Spring Bean에 자동으로 적용되고 설정하기 매우 편리하다.
AspectJ는 RTW를 사용할 수 없다. 대신 CTW, PCW를 지원한다.
LTW가 설정되지 않을 경우 AspectJ 컴파일러가 필요하다.