본문 바로가기

프로그래밍/Spring

Spring AOP

출처:

https://jeong-pro.tistory.com/171

https://blog.hanumoka.net/2018/09/01/spring-20180901-spring-AOP-summary/

https://heowc.dev/2018/02/07/spring-boot-aop/

https://www.baeldung.com/spring-aop-vs-aspectj

https://gmun.github.io/spring/aop/2019/04/20/jdk-dynamic-proxy-and-cglib.html

https://tram-devlog.tistory.com/entry/Spring-AOP-weaving-proxy












AOP(Aspect Oriented Programming) 는 관점지향 프로그래밍 라고 할 수 있다. 


Spring 등의 Framework를 사용하지 않는다면, 서비스 비즈니스 코드는 항상 트랜잭션, 로깅, 인증 등과 관련된 코드들이 함께 구현되어야 하며 결국 코드에는 핵심 비즈니스 로직과 분리되어 관리 할 수 없다. 

핵심 비즈니스 기능과 그 외 공통 기능을 분리해 사용하도록 한 것이 AOP이다.


예)

  • 로깅

  • 예외처리

  • 인증

  • 트랜잭션 (@Transactional)

  • 캐싱(@Cache)




Spring에서 AOP를 살펴보면 Joinpoint, Pointcut, Advice, Weaving, Aspect 등의 용어가 있다.

일단 스프링부트에서 AspectJ 실습을 보자. 간단하다.


-디펜던시 등록

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>



- enable하기

@EnableAspectJAutoProxy
@SpringBootApplication
public class Boot06Application {

public static void main(String[] args) {
SpringApplication.run(Boot06Application.class, args);
}
}




- 공통기능 및 사용 시점 정의

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;


@Aspect
@Component
public class LogAspect {
Logger logger = LoggerFactory.getLogger(LogAspect.class);

// 공통기능을 정의하고 공통기능이 사용될 시점을 정의한다.
@Around("execution(* com.kkwonsy.springboot.boot06.*(..))")
public Object logging(ProceedingJoinPoint pjp) throws Throwable {
logger.info("start - " + pjp.getSignature().getDeclaringTypeName() + " / " + pjp.getSignature().getName());
Object result = pjp.proceed();
logger.info("finished - " + pjp.getSignature().getDeclaringTypeName() + " / " + pjp.getSignature().getName());
return result;
}
}



AOP 용어 정리
Aspect: 공통 관심사에 대한 추상적인 명칭. 예를 들어 로깅이나 보안, 트랜잭션과 같은 기능 등을 가리킨다.

Advice: 실제로 기능을 구현한 객체, 공통로직을 담고 있는 클래스가 된다.

Join points: 공통 관심사를 적용할 수 있는 대상. Spring AOP에서는 각 객체의 메소드가 이에 해당

Pointcuts: 여러 메소드 중 실제 Advice가 적용될 대상 메소드

target: 대상 메소드를 가지는 객체

Proxy: Advice가 적용되었을 때 만들어지는 객체

Introduction: target에는 없는 새로운 메소드나 인스턴스 변수를 추가하는 기능

Weaving: Advice와 target이 결함되어서 프록시 객체를 만드는 과정





이것저것 읽다보니 Spring AOP와 AspectJ는 다르다는 것을 알게 되었다.

https://www.baeldung.com/spring-aop-vs-aspectj


퀵하게는 아래 표를 살펴보면 좋을 것 같다. 


Spring AOP가 좀더 심플하고 AspectJ는 파워풀한 것 같다.


위에 기재한 단어 설명을 보면 weaving은 프록시 객체를 만드는 과정이라고적어 놓았다.

Spring AOP에서는 JDK dynamic proxy 또는 CGLib Proxy를 사용하여 작동하게 된다.




@Service

public class SomeService {

    @Autowired

    private SomeRepository someRepository;


    @Transactional

    public Some save(SomeDto someDto) {

        return someRepository.save(someDto.toEntity());

    }

}


SomeService의 save를 호출하면 바로 접근되는 것이 아니다. 



weaving으로 생성된 ...CGLIB Proxy를 통해 간접적으로 접근된다. 

내부적으로 commit(), rollback() 하는 부분이 try..catch로 감싸져서 구현되어 있지 않을까 싶다. 





자, 그럼 JDK dynamic proxy는?

간단하게 말하면 인터페이스가 있고, 그 구현체가 있는 클래스의 경우에는 JDK dynamic proxy를 사용하고, 인터페이스가 없는 경우에는 CGLIB를 사용한다.


Spring boot는 디폴트로 CGLIB Proxy를 사용한다. 

Reflection을 사용하는 JDK dynamic proxy 에 비해서 성능이나 예외적인 측면에서 낫다고 한다. 





위의 예제에서 @EnalbeAspectJProxy 로 명시되어 있어서 Spring AOP가 아니라 AspectJ로 착각하기 쉽다.

저녀석은 Spring AOP인걸로...



AspectJ를 사용하면 효과적이라고 하는데 언제쯤 써볼 수 있을지는 모르겠다.