Open hyunjungkimm opened 5 months ago
AspectJExpressionPointcut에 pointcut.setExpression("execution(~~)")
을 통해서 포인트컷 표현식을 적용할 수 있음pointcut.matches(메서드, 대상 클래스)
를 실행하면 지정한 포인트컷 표현식의 매칭여부를 true, false로 반환함execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
execution 접근제어자? 반환타입 선언타입?메서드이름(파라미터) 예외?
?
는 생략할 수 있음*
같은 패턴을 지정할 수 있음..
은 파라미터의 타입과 수과 상관없다는 뜻.
,..
의 차이점.
: 정확하게 해당 위치의 패키지..
: 해당 위치의 패키지와 그 하위 패키지 포함this(hello.aop.member.MemberService)
*
같은 패턴을 사용할 수 없음 target(hello.aop.member.MemberService)
*
같은 패턴을 사용할 수 없음 @target
@target(hello.aop.member.annotation.ClassAop)
@within
@within(hello.aop.member.annotation.ClassAop)
@annotation
annotation.value()
해당 애노테이션의 값을 출력할 수 있음@args
@Check
애노테이션이 있는 경우에 매칭한다.@args(test.Check)
bean(orderService) || bean(*Repository)
this(hello.aop.member.MemberService)
target(hello.aop.member.MemberService)
this(hello.aop.member.MemberServiceImpl)
target(hello.aop.member.MemberServiceImpl)
this(hello.aop.member.MemberService)
target(hello.aop.member.MemberService)
this(hello.aop.member.MemberServiceImpl)
target(hello.aop.member.MemberServiceImpl)
* application.properties
* spring.aop.proxy-target-class = true // CGLIB - default
* spring.aop.proxy-target-class = false //JDK 동적 프록시 (인터페이스가 없다면 CGLIB를 사용함)
@SpringBootTest(properties = "spring.aop.proxy-target-class=false") //JDK 동적 프록시
//@SpringBootTest(properties = "spring.aop.proxy-target-class=true") //CGLIB - default
this, target, args, @target, @within, @annotation, @args
args, @args, @target
포인트컷 지시자는 단독으로 사용 금지@Around("execution(* hello.aop..*(..)) && @target(hello.aop.member.annotation.ClassAop))")
args, @args, @target
포인트컷 지시자들은 실제 객체 인스턴스가 생성되고 실행될 때 어드바이스 적용 여부를 확인할 수 있다.spring.main.allow-circular-references=true 설정을 넣거나 @Lazy
어노테이션 사용해야 함
/*
* 참고 : 생성자 주입은 순환 사이클을 만들기 때문에 실패함 -스프링 2.6부터 순환참조 기본적으로 금지
*/
@Component
@Slf4j
public class CallService1 {
private CallService1 callService1;
@Lazy
@Autowired
public void setCallService1(CallService1 callService1) {
this.callService1 = callService1;
}
public void external() {
log.info("call external");
callService1.internal(); //외부 메서드 호출
}
public void internal() {
log.info("call internal");
}
}
@Import(CallLogAspect.class)
@SpringBootTest
//@SpringBootTest("spring.main.allow-circular-references=true") //스프링 2.6부터 순환참조 기본적으로 금지
class CallService1Test {
@Autowired
CallService1 callService1;
@Test
void external() {
callService1.external();
}
}
ObjectProvider(Provider)
callServiceProvider.getObject()
를 호출하는 시점에 스프링 컨테이너에서 빈을 조회함
ApplicationContext
proxyTargetClass = false
JDK 동적 프록시를 사용해서 인터페이스 기반 프록시 생성 proxyTargetClass = true
CGLIB를 사용해서 구체 클래스 기반 프록시 생성구체 클래스를 상속 받기 때문에 문제 발생
aop
설정 파일
implementation 'org.springframework.boot:spring-boot-starter-aop'
체크
AOP
joinpoint.getSignature()
메서드의 정보를 다 알 수 있음joinpoint.proceed()
실제 target 호출@ Pointcut
트랜잭션 기능 동작
보통 트랜잭션을 service에 시작
어드바이스 순서
org.springframework.core.annotation.@ Order
애노테이션을 적용해야 함어드바이스 종류
joinPoint.proceed() 호출 여부 선택
joinPoint.proceed(args[])
try ~ catch ~ finally
모두 들어가는 구문 처리 가능proceed()
사용하지 않아도 메서드 종료시 자동으로 다음 타켓이 호출된다.JoinPoint - ProceedingJoinPoint(하위타입)
JoinPoint 인터페이스의 주요 기능
ProceedingJoinPoint 인터페이스의 주요 기능
@ Aspect 조인포인트 우선순위
@ Around 외에 다른 어드바이스가 존재하는 이유
@Around("hello.aop.order.aop.Pointcuts.orderAndService()")
public void doBefore(JoinPoint joinPoint) {
log.info("[before] {}", joinPoint.getSignature());
}
@Before("hello.aop.order.aop.Pointcuts.orderAndService()")
public void doBefore(JoinPoint joinPoint) {
log.info("[before] {}", joinPoint.getSignature());
}
joinPoint.proceed()를 호출하는 고민을 하지 않아도 됨
@ Around 는 가장 넓은 기능을 제공하지만 실수할 가능성이 있음
@ Before, @ After 같은 어드바이스는 기능은 적지만 실수 가능성이 낮고, 코드가 단순하고, 이 코드를 작성한 의도가 명확하게 드러남
좋은 설계는 제약이 있는 것