AOP는 기능을 핵심 비즈니스 로직과 공통 모듈로 구분하고 핵심 로직에 영향을 미치지 않고 사이사이에 공통 모듈을 효과적으로 잘 끼워 넣어 중복성을 감소시킬 수 있는 개발 방법이다.
AOP 용어
용어 |
설명 |
Advice |
Joinpoint 에서 실행되어야 하는 프로그램 코드 독립된 클래스의 메소드로 작성함 실질적으로 어떤 일을 해야할 지에 대한 것, 실질적인 부가기능을 담은 구현체 BEFORE, AROUND, AFTER의 실행 위치 지정 |
Joinpoint |
Advice 를 적용할 수 있는 후보 지점 혹은 호출 이벤트 관심사를 구현한 코드를 끼워 넣을 수 있는 프로그램의 이벤트를 말하며, 예로는 call events, execution events, initialization events 등이 있음 |
Pointcut |
언제 Advice 를 실행할지를 정의
Target 클래스와 Advice 가 결합(Weaving )될 때 둘 사이의 결합규칙을 정의 특정 조건에 의해 필터링된 조인포인트, 수많은 조인포인트 중에 특정 메소드에서만 횡단 공통기능을 수행시키기 위해서 사용한다. |
Target |
실질적인 비지니스 로직을 구현하고 있는 코드
JoinPoint 에서 실제 Advice 가 적용될 지점 |
Weaving |
PointCut 에 Advice 메서드가 삽입되는 과정 |
AOP 구현
1
|
implementation 'org.springframework.boot:spring-boot-starter-aop'
|
1
2
3
4
5
|
// @EnableAspectJAutoProxy 생략 가능
@SpringBootApplication
public class Application {
public static void main(String[] args) { SpringApplication.run(Application.class,args); }
}
|
1
2
3
4
5
6
|
@Service
public class AuthServiceImpl {
public void businessLogicMethod(){
System.out.println("businessLogicMethod process!");
}
}
|
일반적인 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
import org.aspectj.lang.ProceedingJoinPoint;
@Aspect
@Configuration
public class UselessAspect {
Logger log = LoggerFactory.getLogger(UselessAdvisor.class);
@Around("execution(* com.demo.study.service.AuthServiceImpl.*(..))")
public Object stopWatch(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
try {
stopWatch.start();
return joinPoint.proceed();
} finally {
stopWatch.stop();
log.info("request spent {} ms", stopWatch.getLastTaskTimeMillis());
}
}
/**
* @Before
* @AfterReturning
* @AfterThrowing
*/
@After("execution(* com.demo.study.service.AuthServiceImpl.*(..))")
public void After() throws Throwable {
log.info("After 어드바이스");
}
}
|
어노테이션으로 구현
1
2
3
4
|
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface PerformanceCheck {
}
|
1
2
3
4
5
6
7
|
@Service
public class AuthServiceImpl {
@PerformanceCheck
public void businessLogicMethod(){
System.out.println("businessLogicMethod process!");
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
import org.aspectj.lang.ProceedingJoinPoint;
@Aspect
@Component
public class UselessAspect {
Logger log = LoggerFactory.getLogger(UselessAdvisor.class);
@Around("@annotation(com.demo.study.annotation.PerformanceCheck)")
public Object stopWatch(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
try {
stopWatch.start();
return joinPoint.proceed();
} finally {
stopWatch.stop();
log.info("request spent {} ms", stopWatch.getLastTaskTimeMillis());
}
}
/**
* @Before
* @AfterReturning
* @AfterThrowing
*/
@After("@annotation(com.demo.study.annotation.PerformanceCheck)")
public void After() throws Throwable {
log.info("After 어드바이스");
}
}
|
어노테이션
과 Aspect
가 동일 위치면 어노테이션만 적어도 된다.
- 패키지가 다르면 FQCN(Fully qualified Class Name)을 다 입력해주어야 한다.
빈으로 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
import org.aspectj.lang.ProceedingJoinPoint;
@Aspect
@Component
public class UselessAspect {
Logger log = LoggerFactory.getLogger(UselessAdvisor.class);
@Around("bean(com.demo.study.service.AuthServiceImpl)")
public Object stopWatch(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
try {
stopWatch.start();
return joinPoint.proceed();
} finally {
stopWatch.stop();
log.info("request spent {} ms", stopWatch.getLastTaskTimeMillis());
}
}
/**
* @Before
* @AfterReturning
* @AfterThrowing
*/
@After("bean(com.demo.study.service.AuthServiceImpl)")
public void After() throws Throwable {
log.info("After 어드바이스");
}
}
|
빈
과 Aspect
가 동일 위치면 어노테이션만 적어도 된다.
- 패키지가 다르면 FQCN(Fully qualified Class Name)을 다 입력해주어야 한다.
AOP PointCut 설정
1
|
execution(* com.springbook.biz..*Impl.get*(..))"
|
*
: 리턴값
com.springbook.biz..
: 패키지
*Impl
: 클래스 이름
get*
: 메서드 이름
(..)
: 매개변수
- 중간마다의
.
: 구분점
execution 포인트 컷 리턴
표현식 |
설명 |
* |
모든 리턴타입 허용 |
void |
리턴타입이 void인 메서드 선택 |
!void |
리턴타입이 void가 아닌 메서드 선택 |
execution 포인트 컷 패키지
표현식 |
설명 |
com.springbook.biz |
정학하게 해당 패키지만 선택 |
com.springbook.biz.. |
해당 패키지 및 모든 하위 패키지 선택 |
com.springbook..impl |
.. 앞 패키지로 시작하면서 마지막 패키지 이름이 .. 뒤로 끝나는 패키지 선택 |
execution 포인트 컷 클래스
표현식 |
설명 |
BoardServiceImpl |
정학하게 해당 클래스만 선택 |
*Impl |
클래스 이름이 * 뒷 글자로 끝나는 클래스만 선택 |
BoardService+ |
해당 클래스는 물론 파생된 모든 자식 클래스도 선택 가능 |
variable+ |
해당 인터페이스를 구현한 모든 클래스 선택 가능 |
execution execution 포인트 컷 메서드
표현식 |
설명 |
*(..) |
가장 기본 설정으로 모든 메서드 선택 |
get*(..) |
메서드 이름이 get으로 시작하는 모든 메서드 선택 |
매서드이름(..) |
특정 메서드 이름을 가진 메서드 선택 |
execution 포인트 컷 매개변수
표현식 |
설명 |
(..) |
가장 기본 설정으로서 매개변수 타입의 제한이 없음을 의미 |
(*) |
반드시 1개의 매개변수를 가지는 메서드만 허용한다는 의미 |
(com.spring.user.User) |
해당 패키지의 클래스를 가지는 메서드만 허용한다는 의미 |
(!com.spring.user.User) |
해당 패키지의 클래스를 가지지 않는 메서드만 허용한다는 의미 |
(Integer, ..) |
한 개 이상의 매개변수를 가지되, 첫 번째 매개변수의 타입이 integer인 메서드만 허용 |
(Integer, *) |
반드시 두 개의 매개변수를 가지되, 첫 번째 매개변수의 타입이 integer인 메서드만 허용 |
AOP 구현체
|
SpringAOP |
AspectJ |
목표 |
간단한 AOP 제공 |
완벽한 AOP 제공 |
join point |
메서드 레벨만 지원 |
생성자, 필드, 메서드 등 다양하게 지원 |
weaving |
런타임 시에만 가능 |
런타임은 제공하지 않음, compile-time, post-compile, load-time 제공 |
대상 |
Spring Container가 관리하는 Bean에만 가능 |
모든 JAVA Object에 가능 |
참고
https://github.com/kwj1270/TIL_Seminar/blob/master/AOP.md
https://swingswing.tistory.com/m/272