AOP란?
AOP는 관심 지향 프로그래밍이라는 뜻으로 실제 서비스 기능을 나타내는 비즈니스 로직 전, 후로 로깅이나 성능 검사, 권한 체크 등의 부가적인 기능, 즉 인프라 로직을 추가할 때 사용하는 나타나는 개념으로 인프라 로직이 기능별로 횡단적으로 공통적으로 나타나기 때문에 이를 횡단 관심사라고 한다.
AOP는 쉽게 생각하면 이런 횡단 관심사에 따라 프로그래밍 하는 것이라고 생각할 수 있다.
개인적으로는 기능마다 공통적으로 비즈니스 로직 전, 후에 인프라 로직을 추가하는 프로그래밍 이라고 생각한다.
AOP는 객체 지향 프로그래밍을 의미하는 OOP와 대비되는것으로 오해할 수 있는데, 실제로 AOP는 OOP를 보완하는 역할로 Spring Document에 나와있다고 한다.
(참고 : https://docs.spring.io/springframework/docs/current/reference/html/core.html#aop)
AOP 용어
- Aspect : 부여되는 부가 기능 (인프라 로직) 모듈
- Target Object : 부가 기능이 부여될 대상 객체. 스프링에선 주로 Service
- Advice : 어떤 시점에 부가 기능을 적용할 지. Before, AfterReturning, AfterThrowing, After, Around로 구성
- Join Point : 어디에 적용할 지. 메서드, 필드, 객체, 생성자 등이 있지만 Spring AOP는 메서드만 해당된다 (프록시 패턴을 사용하기 때문에)
- Point cut : 부가 기능이 부여될 Target의 Join Point, Spring으로 말하면 Target 서비스 객체의 어떤 메소드에 적용할 지
Advice의 요소
이해하기 쉽게 메서드에 적용된다고 가정
- Before : 메서드 실행 전
- AfterReturning : 메서드 성공적으로 실행 완료 후
- AfterThrowing : throwing exception시 -> Exception 에러가 발생했을때
- After : AfterReturning + AfterThrowing의 개념으로 메서드 실행 후
- Around : Before + After의 개념으로 메서드 실행 전과 후
구현
나는 어노테이션 설정으로 aop를 적용할 예정이기 때문에, dependency추가, aop태그 추가, aspect(aop 실행될 메서드가 있는 클래스) 개발 외에 annotation 인터페이스를 추가로 구현할 것이다.
pom.xml
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
2개의 aspectj dependency가 필요하다. version은 1.9.6으로 했다.
참고로 스프링과 aspectj의 버전이 맞지 않으면 Invalid byte tag in constant pool: 18 에러를 뱉는데 스프링 버전을 내려주거나 aspectj의 버전을 올려주면 된다.
servlet-context.xml
beans 안에
<aop:aspectj-autoproxy />
를 추가해준다.
커스텀 어노테이션
Controller나 Service에서 aop를 적용시킬 메서드에 사용할 커스텀한 어노테이션을 추가해준다.
위치는 원하는 패키지에 아무데나
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface WorkLnbSet {
}
@WorkLnbSet의 형태로 사용할 어노테이션이다.
Aspect
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.poozim.jobcall.model.Member;
import com.poozim.jobcall.model.Work;
import com.poozim.jobcall.model.WorkCategory;
import com.poozim.jobcall.model.WorkGroup;
import com.poozim.jobcall.service.WorkService;
import com.poozim.jobcall.util.LoginUtil;
@Component
@Aspect
public class WorkAspect {
private static final Logger log = LoggerFactory.getLogger(WorkAspect.class);
@Autowired
private WorkService workService;
@Before("@annotation(WorkLnbSet)")
public void setWorkLnbAttr() throws Throwable {
log.info("======================================set WorkLNBAttrs Logic");
System.out.println("==========================================set WorkLNBAttrs Logic");
HttpServletRequest request = ((ServletRequestAttributes)(RequestContextHolder.currentRequestAttributes())).getRequest();
HttpServletResponse response = ((ServletRequestAttributes)(RequestContextHolder.currentRequestAttributes())).getResponse();
}
}
@PerformanceCheck 어노테이션을 붙인 메서드에서 Before로 동작하게된다.
로직에는 request와 response를 가져오는 부분을 넣었다.
@Around로 동작하는 경우
@Around("execution(* com.poozim.jobcall.controller.WorkController.*(...))")
public Object setWorkLnbAttr(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("======================================set WorkLNBAttrs Logic");
System.out.println("==========================================set WorkLNBAttrs Logic");
HttpServletRequest request = ((ServletRequestAttributes)(RequestContextHolder.currentRequestAttributes())).getRequest();
HttpServletResponse response = ((ServletRequestAttributes)(RequestContextHolder.currentRequestAttributes())).getResponse();
return joinPoint.proceed();
}
처럼 메서드를 구현해야한다. @Around가 아닌데 ProceedingJoinPoint을 사용하는 경우 ProceedingJoinPoint is only supported for around advice 에러를 볼 수 있다.
'Spring' 카테고리의 다른 글
Spring MVC JPA 적용하기 (spirng-data-jpa) (0) | 2021.10.21 |
---|---|
Spring XSS Filter 개발 및 적용 (3) | 2021.10.19 |
Spring Bean Scope 빈 스코프 (0) | 2021.10.12 |
Spring DI(의존성 주입)와 IoC(제어의 역전) (0) | 2021.10.11 |
eclipse 스프링 레거시 프로젝트 생성 및 나의 기본 세팅 (0) | 2021.09.25 |