본문 바로가기

Spring

Spring AOP (Aspect Oriented Programming)

728x90

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 에러를 볼 수 있다.

 

 

 

728x90