스터디/블랙커피 블로그 스터디

1주차 AOP, LiveStudy, @Log4j2 vs @Slf4j with log4j2

TheWing 2021. 1. 24. 20:51

블로그 스터디 1주차

AOP(Aspect-oriented programming)

  • 애플리케이션 전체에 걸쳐 사용되는 공통 기능을 재사용 하도록 지원하는 것

  • 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화를 하는 것

  • 프록시 패턴 사용

    • 이유
      • 접근 제어 및 부가 기능을 추가하기 위해서
  • OOP : 비즈니스 로직의 모듈화

    • 모듈화의 핵심 단위는 비즈니스 로직
  • AOP : 인프라 혹은 부가기능의 모듈화

    • 대표적 예 : 로깅, 트랜잭션, 보안 등
    • 각각의 모듈들의 주 목적 외에 필요한 부가적인 기능들

AOP 장점

  • 애플리케이션 전체에 흩어진 공통 기능이 하나의 장소에서 관리된다는 점
  • 다른 서비스 모듈들이 본인 목적에만 충실하고 그외 사항들은 신경쓰지 않아도 된다는 점

Spring AOP의 핵심

  • 스프링 AOP는 인터페이스(Interface) 기반이다.
  • 스프링 AOP는 프록시(proxy) 기반이다.
  • 스프링 AOP는 런타임(runtime) 기반이다.

Weaving

  • Aspect가 지정된 객체를 새로운 프록시 객체를 생성하는 과정
  • EX)
    • A라는 객체에 트랜잭션 Aspect가 지정되어 있다면, A라는 객체가 실행되기 전 커넥션을 오픈하고 실행이 끝나면 커넥션을 종료하는 기능이 추가된 프록시 객체가 생성되고, 이 프록시 객체가 앞으로 A객체가 호출되는 시점에서 사용이된다. 이 때의 프록시 객체가 생성되는 과정을 위빙이라 생각하면 된다.

AOP Weaving 종류

Runtime Weaving(RTW)

  • Spring AOP에서 사용하는 위빙 방식이다. Proxy를 생성하여 실제 타겟(Target) 오브젝트의 변형없이 위빙을 수행한다. 실제 런타임 상, Method 호출 시에 위빙이 이루어지는 방식이다.

장점

  • 설정이 쉽다(스프링 기본 설정)
  • 소스 코드를 변경하지 않고 적용 가능

단점

  • 메소드를 실행하기 전/후의 join point만 적용가능
  • 필드값 변경 같은 join point에 대해서는 적용이 불가
  • 포인트 컷에 대한 어드바이스 적용 갯수가 늘어 날수록 성능이 떨어진다는 단점이 있다. 또한 메소드 호출에 대해서만 어드바이스를 적용할 수 있다.
  • @Transactional 사용 시 private 메소드 적용불가

Compile Time Weaving(CTW)

  • AspectJ에는 AJC(AspectJ Compiler) 라는 컴파일러가 있는데 Java Compiler를 확장한 형태의 컴파일러이다. AJC를 통해 java 파일을 컴파일 하며, 컴파일 과정에서 바이트 코드 조작을 통해 Advisor 코드를 직접 삽입하여 위빙을 수행한다.
  • AspectJ에서 사용

장점

  • 3가지 위빙 중에서 가장 빠른 퍼포먼스를 보여준다(JVM 상에 올라갈 때 메소드 내에 이미 advise 코드가 삽입 되어 있기 때문)

단점

  • 설정이 복잡하다
  • lombok과 같이 컴파일 과정에서 코드를 조작하는 플러그인과 충돌이 발생할 가능성이 아주 높다

CRW를 사용하기 위한 메이븐 설정

라이브러리 추가 : (pom.xml)

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.6</version>
    <scope>runtime</scope>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjtools</artifactId>
    <version>1.8.6</version>
</dependency>

플러그인 설정: (pom.xml)

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.7</version>
    <configuration>
        <showWeaveInfo>true</showWeaveInfo>
            <source>${aspectj-compiler.version}</source>
            <target>${aspectj-compiler.version}</target>
            <verbose>true</verbose>
            <complianceLevel>${aspectj-compiler.version}</complianceLevel>
            <outxml>true</outxml>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Load-Time Weaving(LTW)

  • ClassLoader를 이용하여 클래스가 JVM에 로드 될 때 바이트 코드 조작을 통해 위빙이 되는 방식 RTW 처럼 소스파일과 클래스 파일에 조작을 가하지 않아 컴파일 시간은 상대적으로 CTW보다 짧다. 하지만 오브젝트가 메모리에 올라가는 과정에서 위빙이 일어나기 때문에 런타임 시, 시간은 CTW보다 상대적으로 느리다. Application Context 에 객체가 로드 될 때, AspectJ weaver와 spring-instrument에 의한 객체 handling이 발생하기 때문에 performance가 저하된다
  • AspectJ에서 사용

장점

  • 소스코드를 변경하지 않고 적용 가능

단점

  • 설정이 복잡함
  • CTW보다 느리다

설정

XML

  • 스프링 설정에 추가 servlet.xml
  • <context:load-time-weaver/>

Java

  • Java Configuration에서 해당 LTW 기능을 사용하도록 설정해준다
  • @EnableLoadTimeWeaving

Code

  • -javaagent:/fullpath/aspectjweaver-1.8.1.jar
  • -javaagent:/fullpath/spring-instrument-4.0.6.RELEASE.jar

Code

  • 톰캣 시작 시 jvmargs 옵션에 추가
  • -javaagent:/weavers/spring-instrument-4.0.6.RELEASE.jar

@After, @AfterReturing, @AfterThrowing

  • @Before (이전)
    • 어드바이스 타겟 메소드가 호출되기 전에 어드바이스 기능을 수행
  • @After (이후)
    • 타겟 메소드의 결과에 관계없이(즉 성공, 예외 관계없이) 타겟 메소드가 완료 되면 어드바이스 기능을 수행
  • @AfterReturning (정상적 반환 이후)
    • 타겟 메소드가 성공적으로 결과값을 반환 후에 어드바이스 기능을 수행
  • @AfterThrowing (예외 발생 이후)
    • 타겟 메소드가 수행 중 예외를 던지게 되면 어드바이스 기능을 수행
  • @Around (메소드 실행 전후)
    • 어드바이스가 타겟 메소드를 감싸서 타겟 메소드 호출전과 후에 어드바이스 기능을 수행

Spring AOP Proxy

  • AopProxy 라는 Delegator 인터페이스로 표현되며
  • Dynamic Proxy 기반은 JdkDynamicAopProxy 클래스 , Cglib 기반은 CglibAopProxy 클래스이다
  • JDK Dynamic Proxy, CGLIB Proxy

  • Dynamic Proxy 기반과 Cglib 기반은 프록시 객체를 생성하는 방식에 차이가 있다.
    • Dynamic Proxy는 프록시 객체 생성을 위해 인터페이스를 필수로 구현
    • Cglib는 인터페이스를 구현하지 않은 일반 클래스에 런타임 시 코드 조작으로 프록시 객체를 생성한다
  • DefaultAopProxyFactory 클래스의 createAopProxy 메소드를 통해 AopProxy의 실구현체가 생성된다.

  • AOP 적용 대상이 인터페이스 거나 인터페이스를 구현하고 있으면 jdkDynamicAopProxy가 생성된다

  • 그렇지 않으면 ObjenesisCglibAopProxy 기반 실 객체가 생성된다

  • Objenesis

    • 과거 Cglib의 몇 가지 단점을 해결해주는 라이브러리
  • Transaction 대상의 경우 기본적으로 cglib으로 생성하게 설정되어 있다.

    • 성능상 Cglib이 이점이 많고, 예외 발생 확률도 적다고 한다.
    • Cglib이 가지고 있던 몇 가지 문제점들을 Objenesis 라이브러리가 해결( 생성자 중복 호출, default 생성자 필요 문제 등)
  • 기본 Spring AOP 이외에 스프링에서 사용할 수 있는 방법으로는 AspectJ가 있다.

  • Spring AOP가 제공하는 프록시 기반 방식은 "Runtime Weaving (RTW)" 방식 - 프로그램 구동 중에 위빙(코드삽입)이 일어난다

    • 반면, AspectJ는 "Compile Weaving(CTW)" 또는 "Load Time Weaving(LTW)"을 이용하는데 RTW보다 성능이 우세하다
    • 또한 AspectJ는 기본 Spring AOP 보다 다양한 포인트 컷을 지원한다
  • 일반적인 경우 Spring AOP 에서 지원하는 방식으로 요구사항들이 충분히 해결되는 경우가 많고, 성능 또한 @AspectJ 갯수에 따라 달라진다. - 일반 적인 경우에서는 크게 체감하기 힘들다고 한다

  • AspectJ를 사용하기 위해서는 AJC 등 별도의 컴파일러 설정 등 추가적인 설정이 필요하나 Spring AOP는 그렇지 않다

  • 과거에는 기본적으로 인터페이스가 있고, 그의 구현체가 있는 클래스의 경우 JDK dynamic Proxy를 사용하고 인터페이스가 없는 경우 CGLIB Proxy를 사용 했다

  • CGLIB Proxy는 상속을 통해 Proxy를 구현하기 때문에 final 클래스인 경우 Proxy를 생성할 수 없다.

  • JDK Dynamic Proxy 는 Java Reflection을 이용해 조금 속도가 느리다.

  • JDK Dynamic Proxy를 강제로 사용하려면 아래와 같은 설정을 추가하면된다

@EnableAspectjAutoProxy(proxyTargetClass = false)
@Configuration
public class ProxyConfig {

}

CGLIB(Code Generate Library) 란

  • 런타임에 동적으로 자바 클래스의 프록시를 생성해주는 기능을 제공한다
  • 순수 Java JDK 라이브러리를 이용하는 것이 아닌 CGLIB이라는 외부 라이브러리를 추가함으로써 사용할 수 있다. 실제 CGLIB의 Enhancer라는 클래스를 바탕으로 프록시를 생성하며, 인터페이스가 아닌 클래스에 대해서 동적 프록시를 생성할 수 있기 때문에 다양한 프로젝트에서 널리 사용되고 있다. (예를 들어 , Hibernate는 자바빈 객체에 대한 프록시를 생성할 때 CGLIB를 사용하며, Spring 은 프록시 기반 AOP를 구현할 때 CGLIB를 사용하고 있다)

백기선님 LiveStudy 10주차 과제 Thread

LiveStudy
기선님의 라이브 스터디 입니다

@Log4j2 vs @Slf4j with log4j2

프로젝트 진행 도중 @Log4j2 을 사용할지 @Slf4j로 log4j2.xml 을 설정해서 같이 사용할지 고민하다가 작성해보았습니다
@Log4j2 vs @Slf4j with log4j2