묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨스프링 핵심 원리 - 고급편
포인트컷 표현식 질문
안녕하세요 강의 잘 듣고있습니다.강의에서 등장하는 AtTargetAtWithinAspect의 execution 표현식을 보면 * hello.aop..*(..)로 기재되어 있습니다.이전 강의에서 학습했던 표현식(* hello.aop..*.*(..))과 어떤 차이점이 있는건가요?
-
해결됨스프링 핵심 원리 - 고급편
@AfterReturning에서 질문있습니다.
안녕하세요 강사님!강사님께서 말씀해주신 AOP로 구현하면 좋은 기능을 실제 코드로 작성해봤습니다.로그 트레이스기능에 매서드 종료가 1초 5초 10초가 될때 로그 레벨을 높여서 로그를 찍는 기능입니다. @Aspect @Slf4j static class LogTraceSelf { ThreadLocal<TraceStatus> trace = new ThreadLocal<>(); private TraceStatus SynchronizedTrace(){ TraceStatus traceStatus = trace.get(); if (traceStatus == null) { trace.set(new TraceStatus()); traceStatus = trace.get(); } return traceStatus; } @Pointcut("@annotation(hello.aop.exam.annotation.Trace)") public void logTrace() {} @Before("logTrace()") public void doTrace(JoinPoint joinPoint) { TraceStatus traceStatus = SynchronizedTrace(); traceStatus.begin(joinPoint.getSignature().getName()); } @AfterReturning("logTrace()") public void doTraceSuccess(JoinPoint joinPoint) { TraceStatus traceStatus = SynchronizedTrace(); long gap = traceStatus.end(joinPoint.getSignature().getName()); } @AfterThrowing(value = "logTrace()", throwing = "ex") public void doTraceException(JoinPoint joinPoint, Exception ex) { TraceStatus traceStatus = SynchronizedTrace(); traceStatus.end(joinPoint.getSignature().getName(),ex); } }조인포인트 실행 권한이 필요없는 부가 기능이다보니@Around 사용을 하지 않고 나누어서 처리를 하다보니 불필요한 코드가 더 증가하게되었습니다.그리고 @Around와 다르게 어드바이스내 지역변수에 TraceStatus를 저장하고 사용할수 없기때문에ThreadLoacal에 공유할 데이터와 본인이 가지고 있어야하는 timestamp도 저장했습니다.@Slf4j public class TraceStatus { private final String id; private int depth = 0; private Deque<Long> timeStampHolder = new ArrayDeque<>(); private final int offsetInfoMs; private final int offsetWarningMs; private final int offsetErrorMs; }기능은 stack 자료구조를 활용해서 begin을 할때마다 timeStampHolder에 시작시간을 저장합니다.end를 호출하면 pop으로 자신이 넣은 시작 시간을 꺼내서 시간을 계산하고 로그를 출력합니다.기타 기능 메서드들은 강사님이 작성하신거와 유사해서 제외했습니다. 이제 소요된 시간이 1초, 5초, 10초가 걸릴 경우 info,warning,error로 출력하는 로그를 남기려고할때어드바이스가 조건을 가지고 추가 기능을 만든다.위 코드처럼 로그 트레이스 내에 저장하고 로그 트레이스 내부에서 출력하도록 한다.경고 알림용 트레이스를 따로 만들고 상태를 전달한다.제 생각은 트레이스에게 넘길경우 트레이스를 인터페이스로 만들어서 필요에 따라 다른 트레이스를 사용한다고 한다면 이 기능을 계속 구현해야 된다는 단점이 생길거 같습니다.저라면 어드바이스에서 소요시간을 트레이스에게 받아서 추가 로직을 수행하려고 할거같습니다. 강사님께서 코드를 작성하신다면어떤 포인트컷을 사용하셔서 구현하실건지,그리고 애노테이션에서 넘어온 값을 가지고 어드바이스에서 처리하게 하시는지아니면 로그 트레이스 내에서 처리하게 하실건지 그 이유가 궁금합니다.
-
해결됨스프링 핵심 원리 - 고급편
Enhancer.setSuperclass
/** * Set the class which the generated class will extend. As a convenience, * if the supplied superclass is actually an interface, <code>setInterfaces</code> * will be called with the appropriate argument instead. * A non-interface argument must not be declared as final, and must have an * accessible constructor. * @param superclass class to extend or interface to implement * @see #setInterfaces(Class[]) */ public void setSuperclass(Class superclass) { if (superclass != null && superclass.isInterface()) { setInterfaces(new Class[]{superclass}); // SPRING PATCH BEGIN setContextClass(superclass); // SPRING PATCH END } else if (superclass != null && superclass.equals(Object.class)) { // affects choice of ClassLoader this.superclass = null; } else { this.superclass = superclass; // SPRING PATCH BEGIN setContextClass(superclass); // SPRING PATCH END } } 강사님 PDF 5.동적 프록시 기술 마지막 장인터페이스가 있는 경우에는 JDK 동적 프록시를 적용하고, 그렇지 않은 경우에는 CGLIB를 적용하려면어떻게 해야할까?두 기술을 함께 사용할 때 부가 기능을 제공하기 위해서 JDK 동적 프록시가 제공하는InvocationHandler 와 CGLIB가 제공하는 MethodInterceptor 를 각각 중복으로 만들어서 관리해야할까?라고 작성되어 있더라구요진짜 구현 클래스만 프록시를 만들어 주는지 궁금해서 찾아보니Enhancer 클래스 내부 메서드를 보면 setSuperclass 가 있는데내부에 인터페이스가 들어오던, 구체 클래스가 들어오던 알아서 구현체를 만들어준다고 되어있더라구요Enhancer 클래스 주석에도하지만 프록시가 인터페이스를 구현하는 것 외에 구체적인 기본 클래스를 확장할 수 있게 해준 것입니다.라고 작성되어 있습니다. 실제 코드로 작성해도 잘 동작합니다.package hello.proxy.my; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; @Slf4j public class EnhancerTest { @Test void enhancerInterface() { MyInterfaceImpl myInterface = new MyInterfaceImpl(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MyInterface.class); enhancer.setCallback(new MyInterceptor(myInterface)); MyInterface myInterfaceProxy = (MyInterface) enhancer.create(); myInterfaceProxy.call(); } static class MyInterceptor implements MethodInterceptor { private final MyInterface myInterface; public MyInterceptor(MyInterface myInterface) { this.myInterface = myInterface; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { log.info("로그 시작"); myInterface.call(); log.info("로그 종료"); return null; } } } interface MyInterface { void call(); } @Slf4j class MyInterfaceImpl implements MyInterface { @Override public void call() { log.info("나는 인터페이스를 구현한 클래스입니다."); } } //log 19:11:27.315 [Test worker] INFO hello.proxy.my.EnhancerTest - 로그 시작 19:11:27.317 [Test worker] INFO hello.proxy.my.MyInterfaceImpl - 나는 인터페이스를 구현한 클래스입니다. 19:11:27.317 [Test worker] INFO hello.proxy.my.EnhancerTest - 로그 종료Enhancer 클래스도 인터페이스나 구체 클래스를 모두 프록시로 생성할 수 있고더 향상된 호출 핸들러을 제공하는 MethodInterceptor 를 사용할 수도 있습니다.제 생각에는 MethodInterceptor 사용할 수 있는 Enhancer 프록시 객체를 디폴트로 사용할거같거든요 그런데 ProxyFactory는 false로 인터페이스랑 구체 클래스를 분리해서 생성하게 했더라구요 그 이유도 궁금합니다.제가 이해한게 맞다면 Enhancer도 인터페이스를 구현할 수 있는데 별도로 개발해야한다고 말씀하신 이유도 궁금합니다 !!
-
미해결스프링 핵심 원리 - 고급편
어드바이저 빈에 대해서는 프록시를 적용하지 않나요?
자동 프록시 생성기는 빈이 후처리기로 들어오면 어드바이저 빈과 후처리기로 들어온 빈을 매칭해서 프록시의 생성여부를 결정하는 걸로 이해했습니다.그러면 처음에 어드바이저 빈을 생성할 때에는 자동 프록시 생성기가 '얘는 어드바이저 빈이구나. 프록시를 생성할 필요가 없겠어' 라고 판단하고 걸러주는 건가요?
-
미해결스프링 핵심 원리 - 고급편
실행순서
안녕하세요실행 순서: @Around , @Before , @After , @AfterReturning , @AfterThrowing 라고 적어주셨는데 @After 이게 왜3 번째로 실행된다고 표현 해주신게 이해가 조금 안갑니다. 실제로는 @AfterReturnin 다음에 실행되는거 아닌가요 ?
-
미해결스프링 핵심 원리 - 고급편
setProxyTargetClass()
문서에는 true가 기본적으로 설정해서 사용한다고 적혀있는데 default는 false 아닌가요?? 그냥 true를 기본적으로 많이 쓴다는 소리일까요 ?
-
미해결코딩으로 학습하는 GoF의 디자인 패턴
코드에 약간 오타가 있는거 같습니다.
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.안녕하세요. 백기선님수업을 잘 듣고 있습니다. 제 생각에 간단한(?) 오타같은게 있는거 같아서 제보드립니다. 혹시 오타가 아니라면 제가 잘못 이해한 부분을 말씀 부탁드리겠습니다.옵저버 패턴 3부 - 1:56초쯤 unregister 를 subject가 "디자인패턴"인 것에 대해서 user2 를 해지 해주었는데요.이미 user 2는 오징어게임에만 등록중입니다. 그래서 결과값도 user2 가 보낸 "옵저버 패턴 장, 단점 보는 중" 이 user1한테 가서 출력된거 같습니다.아마 user1을 해지하거나 user2를 오징어게임에 대해서 해지하려고 하지 않았나 싶습니다..
-
미해결스프링 핵심 원리 - 고급편
joinPoint.proceed() 실행결과인 result 에 관한 질문
안녕하세요강의 정말 감사히 잘 듣고 있습니다.다름이 아니라 예전부터 궁금했던 건데 Object result = joinPoint.proceed();여기서 각 비즈니스 로직(target) 을 실행시키고 난 뒤에 항상 result 를 받습니다.그러나 정작 result 를 활용하여 어떤 동작을 하는 것은 보지를 못해서요....이 result 를 항상 return 시켜주는 데 1) 어떠한 상황에서 result 값을 유용하게 사용할 수 있는지?2) result 를 return 하는데 어디로 return 하는지?가 궁금합니다.혹시 제가 놓친 부분이 있는지 지적해주시면 감사하겠습니다.(항상 감사합니다. 소중한 강의 정말 잘 보고 있습니다)
-
미해결스프링 핵심 원리 - 고급편
프록시 기능
프록시 주요 기능중접근제어 캐싱 지연로딩이건프록시 없이는 불가능한 기술인가요??프록시 기술이 꼭 필요한 기술인지 궁금해서요 !
-
미해결스프링 핵심 원리 - 고급편
스프링에서 CGLiB를 기본적으로 설정했는데, JDK 프록시 방식이 더 유용한 경우가 있을까요?
스프링에서 CGLiB를 기본적으로 설정했는데, CGLiB와 비교하여 JDK 프록시 방식이 더 유용한 경우가 있을까요?
-
미해결스프링 핵심 원리 - 고급편
4:56쯤에 나오는 포인트 컷 지시자 관련해서 궁금한 점이 있습니다.
강의 4:56쯤 보면, 다음처럼 포인트 컷 지시자를 표현해주는데,execution(* hello.app..*(..)) && @target(hello.aop.member.annotation.ClassAop)앞에 hello.app..*(..))안붙여줘도 뒤 조건에 만족하는 것만 AOP만 적용되지 않나요?앞에 위와 같은 조건을 붙여주신 이유가 궁금합니다.
-
해결됨스프링 핵심 원리 - 고급편
@EnableAsync 에서의 default 가 JDK 동적 프록시인 이유
안녕하세요 김영한님!강의 감사히 잘 들었습니다!!완강을 한지는 몇개월이 지났는데, 코딩 중 궁금한 점이 생겨 글을 작성합니다!제목에서와 같이 @EnableAsync 라는 어노테이션을 코드에서 발견했는데요. 해당 어노테이션의 속성에는 proxyTargetClass 라는 속성을 넣을 수 있게 되어 있었습니다.하지만 저는 그 속성을 보자마자 당황스러웠는데요. 보통 스프링 부트는 default로 CGLIB를 사용해서 proxyTargetClass = true 라는 속성이 있으면 회색빛으로 "이 속성을 안써도 괜찮다" 라는 언급을 해줍니다. 그런데 제가 proxyTargetClass = true를 보았을땐 명확한 하얀색으로 되어있어 이제까지 잘못알고 있었나하여 굉장히 당황했습니다.당황함을 뒤로한채 해당 어노테이션의 설명을 보니, 이 어노테이션에서는 proxyTargetClass의 default = false로 되어있고, CGLIB를 사용하고 싶다면 true로 바꾸라는 설명이 쓰여있었습니다.왜 비동기를 설정하는 어노테이션에서 이런 속성을 정의해야하는것일까요..+ 추가질문) 이 어노테이션의 속성을 보면서 느낀점은 한 프로젝트 내에서 A는 CGLIB로 B는 JDK 동적 프록시로 사용하고 싶다면 B를 빈후처리기를 사용해서 직접 설정해주면 될까요?
-
미해결스프링 핵심 원리 - 고급편
Proxy객체의 cacheValue에서 동시성 문제가 발생하지 않는지 궁금합니다!
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]프록시 패턴을 실제 코드에 적용한다고 가정했을 때 Proxy객체의 cacheValue에서 동시성 문제가 발생하지 않는지 궁금합니다! 만약 동시성 문제가 발생한다면 ThreadLocal을 이용해서 해결하면 될까요?
-
미해결스프링 핵심 원리 - 고급편
실무에서의 AOP 사용법에 대한 질문
안녕하세요 김영한님!강의 감사히 잘 듣고 있습니다.지난 11강 포인트컷 파트에서 여러 지시자들의사용법을 많이 알려주셨는데요,제 기억에는 execution 지시자의 사용 빈도가 높다고말씀하셨던 것으로 기억합니다.그런데 이번 강의에서는 어노테이션을 생성해@annotation 지시자를 사용하셨습니다.제가 보기엔 @annotation 지시자는 조인포인트에 해당 어노테이션을 붙여야 하는 번거로움이 있지만 그래서 오히려 더욱 명확한 코드가 되는 것 같고,반대로 execution 지시자는 조인포인트가 Aspect를 알 필요가 없어 더 나은 코드처럼 보이기도 합니다.실무에서 execution과 @annotation 지시자 중어느 방식을 더 선호하시는지 알고 싶습니다.제가 잘못 이해한 부분이 있다면 양해부탁드립니다!
-
미해결스프링 핵심 원리 - 고급편
ApplicationContext, ObjectProvider
ApplicationContext, ObjectProvider 이 두가지는 생성자 주입으로 받을 때@Autowired가 필요 없나요?
-
미해결스프링 핵심 원리 - 고급편
NoLog
[질문 내용]DynamicProxyFilterConfig에서 PATTERNS에 들어있는 문자열이 메핑 주소에 들어있으면 로그가 안뜨는게 아니라 클래스 명에 포함되어 있으면 로그가 뜨지 않는 거로 이해했는데 맞을까요?
-
미해결스프링 핵심 원리 - 고급편
@Bean vs @Component
빈등록하는 방법에@Bean을 통한 직접등록,@Component를 통해 자동 스캔@Import를 통한 등록 3가지를 말씀해주셨는데요, @Bean과 @Component는 왜 구분지어지는건가요?@Bean은 @Configuration 객체안에서만 사용할 수 있는 규칙이라던지 있는건가요?
-
미해결스프링 핵심 원리 - 고급편
LogTrace 주입
안녕하세요.원래 위의 상태에서, 아래에서 ConcreteProxyConfig.class 를 @Bean 으로 등록을 하면 LogTrace 주입이 잘 되는 이유가 궁금하네요.ConcreteProxyConfig를 @Bean 으로 등록이 되어야만, ConcreteProxyConfig 내에 @Bean 으로 설정한 것들도 @Bean 으로 등록이 되는지는 알겠는데, 그렇다고 LogTrace 이 어떻게 @Bean으로 등록이 되어 주입이 될 수 있는지 궁금합니다. 감사합니다.
-
미해결스프링 핵심 원리 - 고급편
newProxyInstance 클래스 로더
예제에서보면 newProxyInstance 매개변수로 들어가는 클래스 로더는 프록시 타겟 클래스에서 가져오는걸로 맞추신 것 같은데그렇게 해야하는 이유가 있을까요?예를들어 Controller의 프록시를 만들 때 Controller.class.getClassLoader가 아닌 Service.class.getClassLoader로 하면 안되는건가요?
-
해결됨스프링 핵심 원리 - 고급편
동적프록시 인터페이스 함수 여러개
동적프로스 생성 시 지정하는 핸들러의 Invoke 메소드는 하나인데요, 만약 대상 인터페이스가 가진 메소드가 call() 하나가 아니라 두개 이상이라면 어떻게 되는건가요?A.call()을 하든 A.call2()를 하든 결국 Invoke안에서 method 호출만 메소드에 따라 달리하는 것이고 Invoke에서 지정한 공통 로직은 메소드에 따라 구분할 순 없는것인가요?