You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
인터페이스가 있는 경우에는 JDK 동적 프록시를 적용하고, 그렇지 않은 경우에는 CGLIB를 적용하려면 어떻게 해야할까?
두 기술을 함께 사용할 때 부가 기능을 제공하기 위해 JDK 동적 프록시가 제공하는 InvocationHandler 와 CGLIB가 제공하는 MethodInterceptor 를 각각 중복으로 만들어서 관리해야 할까?
특정 조건에 맞을 때 프록시 로직을 적용하는 기능도 공통으로 제공되었으면?
Q: 인터페이스가 있는 경우에는 JDK 동적 프록시를 적용하고, 그렇지 않은 경우에는 CGLIB를 적용하려면 어떻게 해야할까?
스프링은 유사한 기술이 있을때 그것을 통합해서 일관성 있게 접근할수 있도, 편리하게 사용할 수 있도록 추상화된 기술을 제공한다.
스프링은 동적 프록시를 통합해서 편리하게 만들어주는 프록시 팩토리(ProxyFactory )라는 기능을 제공한다. (프록시를 만들어주는 공장)
이전에는 상황에 따라서 JDK 동적 프록시를 사용하거나 CGLIB를 사용해야 했다면, 이제는 이 프록시 팩토리 하나로 편리하게 동적 프록시를 생성할 수 있다.
프록시 팩토리는 인터페이스가 있으면 JDK 동적 프록시를 사용하고, 구체 클래스만 있으면 CGLIB를 사용한다. 설정을 변경할수도 있다 (무조건 CGLIB)
이제는 JDK 나 CGLIB 에 프록시 생성을 직접 요청할 필요 없음.
프록시 팩토리에 요청.
인터페이스가 있으면 JDK 를 실행시켜 프록시 만들어 클라이언트에 반환
구체클래스만 있으면 CGLIB 실행시켜 프록시 만들어 반환
Q: 두 기술을 함께 사용할 때 부가 기능을 적용하기 위해 JDK 동적 프록시가 제공하는 InvocationHandler와 CGLIB가 제공하는 MethodInterceptor를 각각 중복으로 따로 만들어야 할까?
스프링은 이 문제를 해결하기 위해 부가 기능을 적용할 때 Advice 라는 새로운 개념을 도입했다. 개발자는 InvocationHandler 나 MethodInterceptor 를 신경쓰지 않고, Advice 만 만들면 된다.
결과적으로 InvocationHandler 나 MethodInterceptor 는 Advice 를 호출하게 된다.
프록시 팩토리를 사용하면 Advice 를 호출하는 전용 InvocationHandler , MethodInterceptor 를 내부에서 사용한다.
개발자는 Advice 에 로직을 만들면 된다.
JDK, CGLIB 프록시 내부에서 각각 특정 메서드(?)를 호출하도록 설정해놨고, 그 메서드들은 결국 Advice 를 호출하게 되어있다.
프록시 팩토리가 프록시를 만들떄 JDK 로 만들면 adviceInvocationHandler 를 붙여놈.
이 친구는 우리가 만든 Advice 를 호출하는 역할.
물론 우리가 만든 Advice는 프록시 팩토리 만들떄 넣어줌.
프록시 생성은 프록시 팩토리 / 로직은 Advice 나머지는 스프링이 추상화해서 처리.
Q: 특정 조건에 맞을 때 프록시 로직을 적용하는 기능도 공통으로 제공되었으면?
앞서 특정 메서드 이름의 조건에 맞을 때만 프록시 부가 기능이 적용되는 코드를 직접 만들었다. 스프링은 Pointcut 이라는 개념을 도입해서 이 문제를 일관성 있게 해결한다.
여기는 프록시를 적용해! 라는걸 정할 수 있음
프록시 팩토리 - 예제 코드1
Advice 만들기 Advice 는 프록시에 적용하는 부가 기능 로직이다. 이것은 JDK 동적 프록시가 제공하는 InvocationHandler 와 CGLIB가 제공하는 MethodInterceptor 의 개념과 유사한다. 둘을 개념적으로 추상화 한 것이다. 프록시 팩토리를 사용하면 둘 대신에 Advice 를 사용하면 된다.
Advice 를 만드는 방법은 여러가지가 있지만, 기본적인 방법은 다음 인터페이스를 구현하면 된다.
내부에는 다음 메서드를 호출하는 방법, 현재 프록시 객체 인스턴스, args , 메서드 정보 등이 포함되어 있다. 기존에 파라미터로 제공되는 부분들이 이 안으로 모두 들어갔다고 생각하면 된다.
CGLIB의 MethodInterceptor 와 이름이 같으므로 패키지 이름에 주의하자
참고로 여기서 사용하는 org.aopalliance.intercept 패키지는 스프링 AOP 모듈(spring-aop )
안에 들어있다. MethodInterceptor 는 Interceptor 를 상속하고 Interceptor 는 Advice 인터페이스를 상속한다.
new ProxyFactory(target) : 프록시 팩토리 생성시 호출 대상 같이 넣어줌. 프록시 팩토리는 넣어준 인스턴스 기반으로 프록시를 만듦. 인스턴스에 인터페이스가 있다면 JDK 동적 프록시를 기본으로 사용. 없다면 CGLIB로 동적 프록시 생성.
여기서는 target 이 new ServiceImpl() 의 인스턴스이기 때문에 ServiceInterface 인터페이스가 있다. 따라서 이 인터페이스를 기반으로 JDK 동적 프록시를 생성한다.
proxyFactory.addAdvice(new TimeAdvice()) : 프록시 팩토리를 통해서 만든 프록시가 사용할 부가 기능 로직을 설정한다. JDK 동적 프록시가 제공하는 InvocationHandler 와 CGLIB가 제공하는 MethodInterceptor 의 개념과 유사하다. 이렇게 프록시가 제공하는 부가 기능 로직을 어드바이스
(Advice )라 한다. 번역하면 조언을 해준다고 생각하면 된다.
proxyFactory.getProxy() : 프록시 객체를 생성하고 그 결과를 받는다.
AopUtils.isAopProxy(proxy) : 프록시 팩토리를 통해서 프록시가 생성되면 JDK 동적 프록시나, CGLIB 모두 참이다.
AopUtils.isJdkDynamicProxy(proxy) : 프록시 팩토리를 통해서 프록시가 생성되고, JDK 동적 프록시인 경우 참
AopUtils.isCglibProxy(proxy) : 프록시 팩토리를 통해서 프록시가 생성되고, CGLIB 동적 프록시인 경우 참
@Test@DisplayName("ProxyTargetClass 옵션을 사용하면 인터페이스가 있어도 CGLIB를 사용하고, 클래스 기반 프록시 사용")
voidproxyTargetClass() {
ServiceInterfacetarget = newServiceImpl();
ProxyFactoryproxyFactory = newProxyFactory(target);
proxyFactory.setProxyTargetClass(true);
proxyFactory.addAdvice(newTimeAdvice());
ServiceInterfaceproxy = (ServiceInterface) proxyFactory.getProxy();
log.info("targetClass={}", target.getClass());
log.info("proxyClass={}", proxy.getClass());
proxy.save();
assertThat(AopUtils.isAopProxy(proxy)).isTrue();
assertThat(AopUtils.isJdkDynamicProxy(proxy)).isFalse();
assertThat(AopUtils.isCglibProxy(proxy)).isTrue();
}
타겟 클래스를 기반으로 프록시를 만드는 설정을 True 로 하면 무조건 CGLIB 로 만듬.
프록시 팩토리의 기술 선택 방법
대상에 인터페이스가 있으면 : JDK 동적 프록시로 인터페이스 기반 프록시
대상에 인터페이스가 없으면 : CGLIB 로 구체 클래스 기반 프록시
proxyTargetClass=true : CGLIB 로 구체 클래스 기반 프록시, 인터페이스 여부와 상관없음
정리
프록시 팩토리의 서비스 추상화 덕분에 구체적인 CGLIB, JDK 동적 프록시 기술에 의존하지 않고, 매우 편리하게 동적 프록시를 생성할 수 있다.
프록시의 부가 기능 로직도 특정 기술에 종속적이지 않게 Advice 하나로 편리하게 사용할 수 있었다. 이것은 프록시 팩토리가 내부에서 JDK 동적 프록시인 경우 InvocationHandler 가 Advice 를 호출하도록 개발해두고, CGLIB인 경우 MethodInterceptor 가 Advice 를 호출하도록 기능을 개발해두었기 때문이다.
참고
2.0 이상의 스프링 부트는 AOP를 적용할 때 기본적으로 proxyTargetClass=true로 설정해서 사용한다.
따라서 인터페이스가 있어도 항상 CGLIB를 사용해서 구체 클래스를 기반으로 프록시를 생성한다.
자세한 이유는 강의 뒷 부분에서 설명한다.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
프록시 팩토리 - 소개
앞서 마지막 설명한 동적 프록시에서 문제점을 다시 확인해보자
InvocationHandler와 CGLIB가 제공하는MethodInterceptor를 각각 중복으로 만들어서 관리해야 할까?스프링은 유사한 기술이 있을때 그것을 통합해서 일관성 있게 접근할수 있도, 편리하게 사용할 수 있도록 추상화된 기술을 제공한다.

스프링은 동적 프록시를 통합해서 편리하게 만들어주는 프록시 팩토리(
ProxyFactory)라는 기능을 제공한다. (프록시를 만들어주는 공장)이전에는 상황에 따라서 JDK 동적 프록시를 사용하거나 CGLIB를 사용해야 했다면, 이제는 이 프록시 팩토리 하나로 편리하게 동적 프록시를 생성할 수 있다.
프록시 팩토리는 인터페이스가 있으면 JDK 동적 프록시를 사용하고, 구체 클래스만 있으면 CGLIB를 사용한다. 설정을 변경할수도 있다 (무조건 CGLIB)
이제는 JDK 나 CGLIB 에 프록시 생성을 직접 요청할 필요 없음.
프록시 팩토리에 요청.
인터페이스가 있으면 JDK 를 실행시켜 프록시 만들어 클라이언트에 반환
구체클래스만 있으면 CGLIB 실행시켜 프록시 만들어 반환
스프링은 이 문제를 해결하기 위해 부가 기능을 적용할 때
Advice라는 새로운 개념을 도입했다. 개발자는InvocationHandler나MethodInterceptor를 신경쓰지 않고,Advice만 만들면 된다.결과적으로
InvocationHandler나MethodInterceptor는Advice를 호출하게 된다.프록시 팩토리를 사용하면
Advice를 호출하는 전용InvocationHandler,MethodInterceptor를 내부에서 사용한다.개발자는 Advice 에 로직을 만들면 된다.

JDK, CGLIB 프록시 내부에서 각각 특정 메서드(?)를 호출하도록 설정해놨고, 그 메서드들은 결국
Advice를 호출하게 되어있다.프록시 팩토리가 프록시를 만들떄 JDK 로 만들면 adviceInvocationHandler 를 붙여놈.
이 친구는 우리가 만든 Advice 를 호출하는 역할.
물론 우리가 만든 Advice는 프록시 팩토리 만들떄 넣어줌.
프록시 생성은 프록시 팩토리 / 로직은 Advice 나머지는 스프링이 추상화해서 처리.
앞서 특정 메서드 이름의 조건에 맞을 때만 프록시 부가 기능이 적용되는 코드를 직접 만들었다. 스프링은
Pointcut이라는 개념을 도입해서 이 문제를 일관성 있게 해결한다.여기는 프록시를 적용해! 라는걸 정할 수 있음
프록시 팩토리 - 예제 코드1
Advice 만들기
Advice는 프록시에 적용하는 부가 기능 로직이다. 이것은 JDK 동적 프록시가 제공하는InvocationHandler와 CGLIB가 제공하는MethodInterceptor의 개념과 유사한다. 둘을 개념적으로 추상화 한 것이다. 프록시 팩토리를 사용하면 둘 대신에Advice를 사용하면 된다.Advice를 만드는 방법은 여러가지가 있지만, 기본적인 방법은 다음 인터페이스를 구현하면 된다.이걸 따라가다 보면 Advice 가 있음.
MethodInvocation invocationargs, 메서드 정보 등이 포함되어 있다. 기존에 파라미터로 제공되는 부분들이 이 안으로 모두 들어갔다고 생각하면 된다.CGLIB의
MethodInterceptor와 이름이 같으므로 패키지 이름에 주의하자org.aopalliance.intercept패키지는 스프링 AOP 모듈(spring-aop)안에 들어있다.
MethodInterceptor는Interceptor를 상속하고Interceptor는Advice인터페이스를 상속한다.지금까지와 다른점 : target 을 넣어주지 않음.
왜냐면 프록시 팩토리 만들떄 넣어주기 때문에.
Object result = invocation.proceed()invocation.proceed()를 호출하면target클래스를 호출하고 그 결과를 받는다.target클래스의 정보가 보이지 않는다.target클래스의 정보는
MethodInvocation invocation안에 모두 포함되어 있다.target정보를 파라미터로 전달받기 때문이다.new ProxyFactory(target): 프록시 팩토리 생성시 호출 대상 같이 넣어줌. 프록시 팩토리는 넣어준 인스턴스 기반으로 프록시를 만듦. 인스턴스에 인터페이스가 있다면 JDK 동적 프록시를 기본으로 사용. 없다면 CGLIB로 동적 프록시 생성.여기서는
target이new ServiceImpl()의 인스턴스이기 때문에ServiceInterface인터페이스가 있다. 따라서 이 인터페이스를 기반으로 JDK 동적 프록시를 생성한다.proxyFactory.addAdvice(new TimeAdvice()): 프록시 팩토리를 통해서 만든 프록시가 사용할 부가 기능 로직을 설정한다. JDK 동적 프록시가 제공하는InvocationHandler와 CGLIB가 제공하는MethodInterceptor의 개념과 유사하다. 이렇게 프록시가 제공하는 부가 기능 로직을 어드바이스(
Advice)라 한다. 번역하면 조언을 해준다고 생각하면 된다.proxyFactory.getProxy(): 프록시 객체를 생성하고 그 결과를 받는다.AopUtils.isAopProxy(proxy): 프록시 팩토리를 통해서 프록시가 생성되면 JDK 동적 프록시나, CGLIB 모두 참이다.AopUtils.isJdkDynamicProxy(proxy): 프록시 팩토리를 통해서 프록시가 생성되고, JDK 동적 프록시인 경우 참AopUtils.isCglibProxy(proxy): 프록시 팩토리를 통해서 프록시가 생성되고, CGLIB 동적 프록시인 경우 참프록시 팩토리 - 예제 코드2
타겟 클래스를 기반으로 프록시를 만드는 설정을 True 로 하면 무조건 CGLIB 로 만듬.
프록시 팩토리의 기술 선택 방법
proxyTargetClass=true: CGLIB 로 구체 클래스 기반 프록시, 인터페이스 여부와 상관없음정리
프록시 팩토리의 서비스 추상화 덕분에 구체적인 CGLIB, JDK 동적 프록시 기술에 의존하지 않고, 매우 편리하게 동적 프록시를 생성할 수 있다.
프록시의 부가 기능 로직도 특정 기술에 종속적이지 않게
Advice하나로 편리하게 사용할 수 있었다. 이것은 프록시 팩토리가 내부에서 JDK 동적 프록시인 경우InvocationHandler가Advice를 호출하도록 개발해두고, CGLIB인 경우MethodInterceptor가Advice를 호출하도록 기능을 개발해두었기 때문이다.참고
2.0 이상의 스프링 부트는 AOP를 적용할 때 기본적으로
proxyTargetClass=true로 설정해서 사용한다.따라서 인터페이스가 있어도 항상 CGLIB를 사용해서 구체 클래스를 기반으로 프록시를 생성한다.
자세한 이유는 강의 뒷 부분에서 설명한다.
Beta Was this translation helpful? Give feedback.
All reactions