반응형
애노테이션 기반의 스프링 @AOP
의존성 추가
<dependency>
<groupId>org.sringframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Aspect 정의
- @Aspect
- 빈으로 등록해야 하니까 (컴포넌트 스캔을 사용한다면) @Component도 추가
포인트컷 정의
- @PointCut(표현식)
- 주요 표현식
- execution
@Service
public class SimpleEventService implements EventService{
@Override
public void createEvent() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("create an Event");
}
@Override
public void publishEvent() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("publish an Event");
}
@Override
public void deleteEvent(){
System.out.println("Delete an Event");
}
}
@Aspect //Aspect라는것을 알려준다.
@Component //빈으로 등록해야 하니까, 컴포넌트 스캔을 사용하기 때문에.
public class PerfAspect {
@Around("execution(* com.example..*.EventService.*(..))")
//아래 코드들을 com.example아래 모든 클래스들 중에 EventService클래스의 모든 메소드에 적용을 하겠다.
//Around는 메소드 호출을 감싸고 있는 형태이기 때문에, 메소드 호출 전, 후 또는 에러가 발생했을때도 특정한 일을 할수가 있는 다용도로 쓰일수 있는 애노테이션이다.
//Advice
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object retVal = pjp.proceed(); //메소드 호출을 감싸고 있다.
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
EventService eventService; //interface가 있다면 interface타입으로 받는게 가장 좋다. 구현체로 받지말고.
@Override
public void run(ApplicationArguments args) throws Exception {
eventService.createEvent();
eventService.publishEvent();
eventService.deleteEvent();
}
}
실행 결과
create an Event
1011
publish an Event
2001
Delete an Event
0
문제점 : deleteEvent 메소드에는 적용하고 싶지 않은데 , 모든 메소드에 적용이 되버린다.
(execution으로도 할수가 있긴한데, 되게 복잡하고 어렵다..)
- @annotation
먼저 Advice를 적용을 할 표시를 하기 위한 애노테이션을 생성한다.
@Target(ElementType.METHOD) //target이 METHOD 라는것은 명시해주는것이 좋다.
@Retention(RetentionPolicy.CLASS) //RetentionPolicy가 의미하는것은 이 애노테이션 정보를 얼마나 유지를 할것인가이다.
//SOURCE로 값을 주면, 컴파일이 끝나면 사라져 버린다. (주의)
//RetentionPolicy.CLASS 이상 값을 줘야한다. (기본값 : CLASS) 기본값을 사용하더라도 명시를 해주면 좋다!
public @interface PerfLogging {
}
적용할 메소드에 @PerfLogging 이라고 애노테이션을 붙여준다.
@Service
public class SimpleEventService implements EventService{
@PerfLogging // Advice를 실행하고 싶은 메소드에만 애노테이션을 붙인다.
@Override
public void createEvent() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("create an Event");
}
@PerfLogging // Advice를 실행하고 싶은 메소드에만 애노테이션을 붙인다.
@Override
public void publishEvent() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("publish an Event");
}
@Override
public void deleteEvent(){
System.out.println("Delete an Event");
}
}
@Aspect
@Component
public class PerfAspect {
@Around("@annotation(PerfLogging)")
// @PerfLogging 이라는 애노테이션이 붙은 메소드에만 Advice를 수행하겠다.
//Advice
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object retVal = pjp.proceed(); //메소드 호출을 감싸고 있다.
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
이 방법은 메소드에 @애노테이션을 붙여줌으로써 명시를 할수 있기 때문에, 유용하다.
/*
* 이 애노테이션을 사용하면 성능을 로깅해줍니다.
*/
@Target(ElementType.METHOD) //target이 METHOD 라는것은 명시해주는것이 좋다.
@Retention(RetentionPolicy.CLASS) //RetentionPolicy가 의미하는것은 이 애노테이션 정보를 얼마나 유지를 할것인가이다.
//RetentionPolicy.CLASS 이상 값을 줘야한다. (기본값 : CLASS) 기본값을 사용하더라도 명시를 해주면 좋다!
public @interface PerfLogging {
}
이런식으로 해주면 팀원들이나 누군가 이 코드를 봤을때, "아~ 이 애노테이션을 붙이면 성능을 로깅해주는구나!" 하고 쉽게 알수 있기 때문이다.
실행 결과
create an Event
1011
publish an Event
2001
Delete an Event
실행결과를 보다시피 @PerfLogging 애노테이션이 붙은 메소드만 Advice가 실행되었다.
- bean
빈으로 등록된 클래스의 모든 메소드에 대해서 Advice가 수행된다.
@Aspect //Aspect라는것을 알려준다.
@Component //빈으로 등록해야 하니까, 컴포넌트 스캔을 사용하기 때문에.
public class PerfAspect {
@Around("bean(simpleEventService)") //빈(simpleEventService)에 모든 메소드에 적용이 된다.
//Around는 메소드 호출을 감싸고 있는 형태이기 때문에, 메소드 호출 전, 후 또는 에러가 발생했을때도 특정한 일을 할수가 있는 다용도로 쓰일수 있는 애노테이션이다.
//Advice
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object retVal = pjp.proceed(); //메소드 호출을 감싸고 있다.
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
@Service
public class SimpleEventService implements EventService{
@Override
public void createEvent() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("create an Event");
}
@Override
public void publishEvent() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("publish an Event");
}
@Override
public void deleteEvent(){
System.out.println("Delete an Event");
}
}
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
EventService eventService; //interface가 있다면 interface타입으로 받는게 가장 좋다. 구현체로 받지말고.
@Override
public void run(ApplicationArguments args) throws Exception {
eventService.createEvent();
eventService.publishEvent();
eventService.deleteEvent();
}
}
실행 결과
create an Event
1011
publish an Event
2001
Delete an Event
0
- 포인트컷 조합
- &&, || , !
Advice 정의
- @Before : 어드바이스 타겟 메소드가 호출되기 전에 어드바이스 기능을 수행
@Around 같이 강력한 Advice를 사용안하고 간단하게 "메소드가 실행되기 이전에 뭔가를 하고 싶다" 하면 @Before를 사용해도 된다.
@Before(bean("simpleEventService")//빈으로 등록된 simpleEventService에 모든 메소드가 실행되기 이전에..
public void hello(){
System.out.pritnln("hello");
}
실행 결과
hello
create an Event
1011
hello
publish an Event
2001
hello
Delete an Event
0
메소드가 실행되기 이전에 hello가 찍히는걸 볼 수 있다.
- @AfterReturning (정상적 반환 이후) : 타겟 메소드가 성공적으로 결과값을 반환 후에 어드바이스 기능을 수행
- @AfterThrowing (예외 발생 이후) : 타겟 메소드가 수행 중 예외를 던지게 되면, 어드바이스 기능을 수행
- @Around (메소드 실행 전후) : 어드바이스가 타겟 메소드를 감싸서 메소드 호출 전과 후에 어드바이스 기능을 수행
출처:
https://engkimbs.tistory.com/746
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop- pointcuts
반응형