Spring에는 Spring Triangle이라고 불리는 3대 요소 IoC
, PSA
, AOP
가 있습니다.
그 중 하나인 AOP(Aspect Oriented Programming), 관점 지향 프로그래밍에 대해 정리해보겠습니다.
What is AOP?
AOP, 즉 관점 지향 프로그래밍이란 어떤 로직을 기준으로 핵심적인 관점과 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화하는 것입니다.
여기서 핵심 관점은 주로 핵심 비즈니스 로직을, 부가적인 관점은 핵심 로직을 위한 데이터베이스 연결, 로깅 등을 말합니다.
class A {
method a() {
AAAA
// a do something
BBBB
}
method b() {
AAAA
// b do something
BBBB
}
}
class B {
method c() {
AAAA
// c do something
BBBB
}
}
위 코드의 AAA,BBB 처럼 소스 코드상에서 여러군데서 사용되는 중복되는 코드들을 발견할 수 있는데, 이것을 흩어진 관심사 (Crosscutting Concerns)라 부르고, 이것들을 Aspect로 모듈화해 비즈니스 로직에서 분리하고 재사용하는 것이 AOP입니다.
즉 필요하지만 중복해서 작성해야하는 핵심 로직 이외의 코드는 외부로 분리하여, 핵심로직에만 집중할 수 있도록 하는 것 입니다.
AOP
의 주요 개념term | description |
---|---|
Aspect | 위에서 설명한 흩어진 관심사를 모듈화 한 것으로, 공통적으로 정의될 부가기능을 의미합니다. |
Target | Aspect 가 적용되는 대상 (클래스, 메서드 .. ) |
JointPoint | Advice 가 적용 가능한 위치.메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능 |
PointCut | Advice 가 어떤 Joinpoint 에 적용될 지를 나타냅니다.(어디에)execution, @annotation 등 대상을 지정합니다. |
Advice | Aspect 를 언제 핵심 코드에 적용할 지를 정의합니다. |
PointCut
정규식리턴타입 | 메소드 이름 | 파라미터 |
---|---|---|
String | getUser | (String word1, String word2) |
* | com.example.demo.service..* | (..) |
..은 하위경로 전부라는 뜻입니다.
Advice
의 종류동작 시점 | description |
---|---|
Before | 메소드 실행 전 |
After | 메소드 실행 후 |
AfterReturning | 메소드가 정상적으로 실행된 후 (반환 후) |
AfterThrowing | 예외가 발생한 후 |
Around | 메소드 호출 이전, 이후, 예외발생 등 모든 시점에서 동작 매개변수로 ProceedingJoinPoint 를 받음 |
Around 의 value에는 어노테이션, 빈, 메소드 등을 등록할 수 있습니다
@Around("bean(userService)")
@Around("@annotation(Logging)")
@Around("execution(* com.example.demo.service.*Service.*(..))")
Spring AOP를 사용하기위해 dependency를 추가해줍니다.
implementation 'org.springframework.boot:spring-boot-starter-aop'
Aspect 적용 대상을 지정할 어노테이션을 생성합니다.
@Target(ElementType.METHOD)
, @Retention(RetentionPolicy.RUNTIME)
을 추가해 적용대상과 생명주기를 설정해줍니다.
// Logging.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Logging {
}
이제 해당 어노테이션을 추가한 메소드에 어떤 기능을 추가할 것인지를 적용할 실제 Aspect를 구현합니다.
@Aspect
는 @Component
를 포함하지 않기때문에 @Component
를 따로 추가해 Bean으로 등록해줘야합니다.
@Around()
로 위에서 생성한 @Logging
어노테이션이 붙은 메소드의 실행 전후로 아래 로직을 실행하도록 합니다.
// LoggingAspect.java
@Slf4j
@Component
@Aspect
public class LoggingAspect {
@Around("@annotation(com.example.demo.common.annotation.Logging)")
public Object Logging(ProceedingJoinPoint joinPoint) throws Throwable {
String nowTime = new SimpleDateFormat("YYYY. MM. DD HH:mm:ss")
.format(System.currentTimeMillis());
Object ret = joinPoint.proceed();
log.info(String.format("[%s] %s Called.", nowTime, joinPoint.getSignature().toShortString()));
return ret;
}
}
어노테이션이 같은 경로에 있는 것이 아니라면 전체 경로를 명시해줘야합니다.
그렇지 않는다면 java.lang.IllegalArgumentException: error Type referred to is not an annotation type
에러가 발생합니다.
이제 메서드에 @Logging
어노테이션을 추가하면, 생성한 Aspect가 잘 동작하는 것을 확인할 수 있습니다.
// service.java
@Service
public class AspectService {
@Logging
public void testAspect() {
// do something
}
}
Ref.
https://atoz-develop.tistory.com/entry/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-AOP-%EA%B0%9C%EB%85%90-%EC%9D%B4%ED%95%B4-%EB%B0%8F-%EC%A0%81%EC%9A%A9-%EB%B0%A9%EB%B2%95
https://giron.tistory.com/70
https://onlyformylittlefox.tistory.com/15
https://engkimbs.tistory.com/746
https://congsong.tistory.com/25
https://devlog-wjdrbs96.tistory.com/398?category=882236