5. Programmatic Creation of @AspectJ Proxies, Using AspectJ with Spring Applications

2023. 5. 26. 14:00Spring Framework/Spring AOP

Programmatic Creation of @AspectJ Proxies

<aop:config> 또는 <aop:aspectj-autoproxy>를 사용하여 설정에서 aspect를 선언하는 것 외에도, 타겟 객체를 어드바이스하는 프록시를 프로그래밍 방식으로 생성할 수도 있습니다. Spring AOP API에 대한 전체 자세한 내용은 다음 장을 참조하세요. 여기서는 @AspectJ aspect를 사용하여 자동으로 프록시를 생성하는 기능에 중점을 두고 설명하겠습니다.

org.springframework.aop.aspectj.annotation.AspectJProxyFactory 클래스를 사용하여 하나 이상의 @AspectJ aspect에 의해 어드바이스된 타겟 객체에 대한 프록시를 생성할 수 있습니다. 이 클래스의 기본 사용법은 매우 간단하며, 다음 예제가 이를 보여줍니다:

// 주어진 타겟 객체에 대한 프록시를 생성할 수 있는 팩토리를 생성합니다.
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);

// aspect를 추가합니다. 클래스는 @AspectJ aspect여야 합니다.
// 필요한 경우 다른 aspect로 여러 번 이 메서드를 호출할 수 있습니다.
factory.addAspect(SecurityManager.class);

// 기존 aspect 인스턴스를 추가할 수도 있습니다. 제공된 객체의 타입은
// @AspectJ aspect여야 합니다.
factory.addAspect(usageTracker);

// 이제 프록시 객체를 가져옵니다...
MyInterfaceType proxy = factory.getProxy();

자세한 내용은 Javadoc을 참조하세요.

Using AspectJ with Spring Applications

이전까지 다룬 내용은 순수한 Spring AOP에 대한 것이었습니다. 이 섹션에서는 AspectJ 컴파일러나 위버(weaver)를 Spring AOP 대신 또는 함께 사용하여, Spring AOP가 제공하는 기능 이상이 필요한 경우 이를 어떻게 활용할 수 있는지 살펴보겠습니다.

1. 스프링에서 AspectJ 소개

  • Spring AOP vs. AspectJ: Spring AOP는 더 넓은 AspectJ 프레임워크의 하위 집합입니다. Spring AOP는 기업 애플리케이션의 일반적인 문제를 해결하도록 설계되었지만, 메서드 수준의 인터셉션에만 한정되며 필드 수준이나 생성자 수준의 인터셉션을 지원하지 않습니다. 반면에 AspectJ는 이를 지원합니다.
  • 왜 AspectJ를 사용해야 하는가: AspectJ는 더 강력하고 유연한 AOP 접근 방식을 제공합니다. 특히 Spring AOP가 제공하는 기능을 넘어서는 추가 기능이 필요한 애플리케이션의 경우, AspectJ와 Spring을 통합하는 것이 유용할 수 있습니다.

2. Spring-Aspects 라이브러리

  • AspectJ와 스프링의 통합: 스프링은 AspectJ에 대한 소규모 라이브러리를 포함하고 있으며, 이 라이브러리는 spring-aspects.jar 파일에서 찾을 수 있습니다. 이 라이브러리는 사전에 구축된 Aspect들을 제공하여 Spring 애플리케이션에서 사용할 수 있습니다.
  • Spring-Aspects 사용: 이 Aspect들을 사용하려면 애플리케이션의 클래스 경로에 spring-aspects.jar 파일을 포함시켜야 합니다. 이를 통해 Spring에서 제공하는 사전 정의된 Aspect들을 활용하고 애플리케이션의 기능과 통합할 수 있습니다.

3. AspectJ에서 Spring을 사용한 의존성 주입

  • Spring IoC와 함께 AspectJ 사용: Spring과 AspectJ 간의 주요 통합 기능 중 하나는 Spring의 IoC(Inversion of Control) 컨테이너를 사용하여 AspectJ Aspect들을 관리할 수 있다는 것입니다. 이를 통해 Aspect들이 Spring의 의존성 주입을 사용할 수 있습니다.
  • AspectJ Aspect 설정: AspectJ Aspect를 설정할 때 Spring의 XML 또는 Java 기반 설정을 사용할 수 있습니다. 이 설정에서는 Aspect를 Spring 빈으로 정의하여 Spring 컨테이너에서 관리하는 의존성을 주입받을 수 있습니다.

4. AspectJ의 로드 타임 위빙(LTW)

  • LTW 소개: 로드 타임 위빙은 클래스가 클래스 로더에 의해 로드될 때 Aspect가 클래스에 삽입되는 과정입니다. 이는 빌드 타임이나 포스트 컴파일 위빙과 달리, 코드가 로드될 때 Aspect가 삽입됩니다.
  • Spring에서의 LTW: Spring은 AspectJ와 함께 로드 타임 위빙을 지원하여 원본 소스 코드를 수정하지 않고도 Aspect를 적용할 수 있습니다. 이는 서드 파티 라이브러리에 Aspect를 추가하거나 런타임에 조건부로 Aspect를 적용해야 할 때 특히 유용합니다.
  • LTW 설정: Spring에서 로드 타임 위빙을 활성화하려면 @EnableLoadTimeWeaving 애노테이션 또는 해당 XML 설정을 포함하도록 Spring 애플리케이션 컨텍스트를 설정해야 합니다. 또한, 전통적인 Java 애플리케이션을 사용하는 경우 AspectJ 에이전트를 JVM 옵션에 지정해야 합니다.

5. Spring과 AspectJ 사용

  • 컴파일 타임 위빙: AspectJ는 Spring 애플리케이션의 빌드 프로세스에 통합되어 컴파일 타임에 Aspect를 삽입할 수 있습니다. 이를 위해 표준 Java 컴파일러 대신 AspectJ 컴파일러(ajc)를 사용해야 합니다.
  • 런타임(로드 타임) 위빙: 또는, Spring의 로드 타임 위빙 기능을 사용하여 런타임에 Aspect를 적용할 수도 있습니다. 이는 유연성을 제공하지만 적절한 클래스 로더를 구성해야 합니다.

AspectJ와 Spring을 통합하면 Spring AOP의 한계를 넘어선 더 강력하고 유연한 AOP 프레임워크를 사용할 수 있습니다. 프로젝트에 spring-aspects.jar을 포함시킴으로써 사전 정의된 Aspect를 사용할 수 있으며, Spring IoC를 통해 AspectJ Aspect를 Spring 빈으로 관리할 수 있습니다. 필드나 생성자 인터셉션과 같은 더 고급 AOP 기능이 필요하거나 런타임에 동적으로 Aspect를 적용해야 하는 애플리케이션의 경우, 로드 타임 위빙이 효과적인 접근 방식이 될 수 있습니다. AspectJ와 Spring을 설정하여 애플리케이션의 모듈화 및 횡단 관심사 관리 기능을 강화할 수 있습니다.

Using AspectJ to Dependency Inject Domain Objects with Spring

Spring Framework에서 AspectJ를 사용하여 도메인 객체에 의존성을 주입하는 방법에 대해 설명하겠습니다. 이 기법은 Spring 컨테이너의 제어 밖에서 생성된 객체들에 의존성을 주입할 수 있도록 해줍니다. 특히, ORM 도구를 통해 생성되거나 new 연산자로 생성된 도메인 객체에 유용합니다.

Spring의 의존성 주입과 도메인 객체

Spring 컨테이너는 애플리케이션 컨텍스트에 정의된 빈(Bean)을 인스턴스화하고 설정하는 역할을 합니다. 일반적으로 Spring 컨테이너가 관리하는 객체들은 애플리케이션 컨텍스트에서 정의된 빈으로부터 생성됩니다. 그러나 도메인 객체들은 종종 프로그래밍 방식으로 new 연산자를 사용하여 생성되거나 ORM 도구를 통해 데이터베이스 쿼리 결과로 생성되기 때문에, Spring 컨테이너의 제어 밖에서 생성될 때가 많습니다.

이러한 경우, Spring은 @Configurable 애노테이션과 AspectJ를 활용하여 이러한 객체에 의존성을 주입할 수 있습니다.

@Configurable 애노테이션

@Configurable 애노테이션은 클래스가 Spring을 통해 설정될 수 있음을 나타냅니다. 이 애노테이션을 사용하면, 해당 클래스의 새로운 인스턴스가 생성될 때 Spring 컨테이너에서 해당 클래스의 빈 정의를 기반으로 의존성이 주입됩니다.

기본적인 사용법

가장 간단한 형태로, @Configurable 애노테이션은 마커(marker) 애노테이션으로 사용할 수 있습니다. 다음은 그 예시입니다:

package com.xyz.domain;

import org.springframework.beans.factory.annotation.Configurable;

// Spring IoC에 의해 관리되는 빈이 아님!!!
@Configurable
public class Account {
    // ...
    
    // MyService는 Spring IoC에 의해 관리되는 빈임.
    @Autowired
    MyService myService;
}

이 경우, Spring은 com.xyz.domain.Account라는 이름의 빈 정의를 찾아 새롭게 생성된 Account 객체에 이를 적용합니다.

명시적인 빈 이름 설정

빈 정의의 이름을 명시적으로 설정하고 싶다면, @Configurable 애노테이션에 이름을 지정할 수 있습니다:

package com.xyz.domain;

import org.springframework.beans.factory.annotation.Configurable;

@Configurable("account")
public class Account {
    // ...
}

이 경우 Spring은 account라는 이름의 빈 정의를 찾아 사용합니다.

Autowiring

빈 정의를 따로 만들지 않고 자동으로 의존성을 주입하려면, @Configurable 애노테이션의 autowire 속성을 사용하여 자동 주입(Autowiring)을 설정할 수 있습니다. 예를 들어, 다음과 같이 주입 방식을 지정할 수 있습니다:

@Configurable(autowire=Autowire.BY_TYPE)

또는

@Configurable(autowire=Autowire.BY_NAME)

이외에도, @Autowired 또는 @Inject 애노테이션을 필드나 메서드 레벨에 사용하여 명시적으로 의존성을 주입할 수 있습니다.

AspectJ와 @Configurable

@Configurable 애노테이션만으로는 아무런 동작도 하지 않습니다. 이 애노테이션이 실제로 동작하려면, AspectJ의 AnnotationBeanConfigurerAspect가 필요합니다. 이 애스펙트는 @Configurable이 적용된 클래스의 인스턴스가 생성된 후, 해당 객체를 Spring을 통해 구성하도록 합니다.

사후 초기화(After Initialization)

기본적으로 @Configurable을 사용하면, 객체가 생성된 후에 의존성이 주입됩니다. 즉, 객체 생성자의 본문에서 의존성을 사용할 수 없습니다. 만약 의존성을 생성자에서 사용하고 싶다면, @Configurable(preConstruction=true)를 사용하여 생성자 실행 전에 의존성을 주입할 수 있도록 설정할 수 있습니다:

@Configurable(preConstruction = true)

이렇게 하면 생성자 본문 실행 전에 의존성이 주입됩니다.

AspectJ Weaving

@Configurable이 적용된 클래스는 AspectJ를 통해 위빙(Weaving)되어야 합니다. 이를 위해, 빌드 타임에 Ant 또는 Maven 태스크를 사용하거나, 로드 타임 위빙을 사용할 수 있습니다.

AnnotationBeanConfigurerAspect 자체도 Spring에서 설정되어야 하며, 이를 위해 Java 기반 설정에서는 @EnableSpringConfigured를 사용하여 설정할 수 있습니다:

@Configuration
@EnableSpringConfigured
public class AppConfig {
}

XML 기반 설정을 선호하는 경우에는, Spring의 context:spring-configured 요소를 사용하여 설정할 수 있습니다:

<context:spring-configured/>

주의사항

@Configurable 처리를 활성화할 때는 해당 클래스가 일반적인 Spring 빈으로도 등록되지 않도록 주의해야 합니다. 그렇지 않으면 해당 클래스가 두 번 초기화되는 문제가 발생할 수 있습니다.

AspectJ와 Spring의 @Configurable 애노테이션을 활용하면, Spring 컨테이너의 제어 밖에서 생성된 객체에도 의존성을 주입할 수 있습니다. 이 방법은 특히 도메인 객체와 같은 Spring의 관리 대상이 아닌 객체에 대해 유용하게 사용될 수 있습니다. 그러나 이 기능을 사용할 때는 의존성 주입의 시점과 클래스 초기화 과정에서 발생할 수 있는 문제를 충분히 이해하고 사용하는 것이 중요합니다.

Unit Testing @Configurable Objects

@Configurable 지원의 목표 중 하나는 도메인 객체의 독립적인 단위 테스트를 어렵지 않게 수행할 수 있도록 하는 것입니다. @Configurable 타입이 AspectJ에 의해 위빙되지 않은 경우, 어노테이션은 단위 테스트 중에는 아무런 영향을 미치지 않습니다. 테스트 중에는 모의(mock)나 스텁(stub) 속성을 설정한 후 정상적으로 진행할 수 있습니다. 그러나 @Configurable 타입이 AspectJ에 의해 위빙된 경우, 단위 테스트를 컨테이너 외부에서 수행할 때마다 경고 메시지가 나타나며, 이는 객체가 Spring에 의해 구성되지 않았음을 나타냅니다.

Working with Multiple Application Contexts

@Configurable 지원을 구현하는 AnnotationBeanConfigurerAspect는 AspectJ 싱글톤 에스펙트입니다. 싱글톤 에스펙트의 범위는 정적 멤버의 범위와 동일합니다: 해당 타입을 정의하는 클래스 로더당 하나의 에스펙트 인스턴스가 존재합니다. 이는 동일한 클래스 로더 계층 구조 내에서 여러 애플리케이션 컨텍스트를 정의하는 경우, @EnableSpringConfigured 빈을 어디에 정의하고 spring-aspects.jar을 클래스패스에 어디에 배치할지 고려해야 함을 의미합니다.

일반적인 Spring 웹 애플리케이션 구성에서는 공유 부모 애플리케이션 컨텍스트가 공통 비즈니스 서비스와 이를 지원하는 모든 것을 정의하고, 각 서블릿에 대해 자식 애플리케이션 컨텍스트가 존재하는 경우가 많습니다. 이러한 컨텍스트는 동일한 클래스 로더 계층 내에서 공존하므로, AnnotationBeanConfigurerAspect는 그 중 하나에만 참조를 가질 수 있습니다. 이 경우, 공유(부모) 애플리케이션 컨텍스트에 @EnableSpringConfigured 빈을 정의하는 것이 좋습니다. 이는 도메인 객체에 주입할 가능성이 높은 서비스들을 정의하는 것입니다. 결과적으로, @Configurable 메커니즘을 사용하여 자식(서블릿 전용) 컨텍스트에 정의된 빈에 대한 참조로 도메인 객체를 구성할 수 없습니다(이는 원래 하고자 하는 일이 아닐 것입니다).

같은 컨테이너 내에서 여러 웹 애플리케이션을 배포할 때는, 각 웹 애플리케이션이 spring-aspects.jar을 자체 클래스 로더를 통해 로드하도록 해야 합니다(예: WEB-INF/lib에 spring-aspects.jar을 배치). spring-aspects.jar이 컨테이너 전체 클래스패스에만 추가된 경우, 모든 웹 애플리케이션이 동일한 에스펙트 인스턴스를 공유하게 되며, 이는 원하지 않는 상황일 것입니다.

Other Spring aspects for AspectJ

@Configurable 에스펙트 외에도, spring-aspects.jar에는 @Transactional 어노테이션이 적용된 타입과 메서드에 대해 Spring의 트랜잭션 관리를 사용할 수 있는 AspectJ 에스펙트가 포함되어 있습니다. 이 에스펙트는 주로 Spring 컨테이너 외부에서 Spring Framework의 트랜잭션 지원을 사용하려는 사용자를 위한 것입니다.

@Transactional 어노테이션을 해석하는 에스펙트는 AnnotationTransactionAspect입니다. 이 에스펙트를 사용할 때는 구현 클래스(또는 해당 클래스 내의 메서드 또는 둘 다)에 어노테이션을 달아야 하며, 클래스가 구현하는 인터페이스에는 어노테이션을 달지 않아야 합니다. 이는 AspectJ가 자바의 규칙을 따르기 때문인데, 인터페이스에 있는 어노테이션은 상속되지 않습니다.

클래스에 @Transactional 어노테이션을 적용하면, 그 클래스 내의 모든 public 메서드 실행에 대해 기본 트랜잭션 규칙이 적용됩니다.

클래스 내의 메서드에 @Transactional 어노테이션을 적용하면, 해당 메서드에 대해 클래스에 지정된 기본 트랜잭션 규칙을 재정의합니다(클래스 어노테이션이 있는 경우). 접근 제한자에 관계없이 모든 메서드에 어노테이션을 달 수 있으며, private 메서드에 트랜잭션 구역을 적용하려면 직접 어노테이션을 달아야 합니다.

Spring Framework 4.2부터, spring-aspects는 표준 jakarta.transaction.Transactional 어노테이션에 대해 동일한 기능을 제공하는 유사한 에스펙트를 제공합니다. 자세한 내용은 JtaAnnotationTransactionAspect를 참고하십시오.

AspectJ 프로그래머 중 Spring의 구성과 트랜잭션 관리 지원을 사용하고 싶지만 어노테이션을 사용하지 않거나 사용할 수 없는 경우, spring-aspects.jar에는 자체 포인트컷 정의를 제공할 수 있는 추상 에스펙트가 포함되어 있습니다. AbstractBeanConfigurerAspectAbstractTransactionAspect 에스펙트의 소스를 참조하십시오. 예를 들어, 다음은 도메인 모델에 정의된 객체의 모든 인스턴스를 클래스의 전체 경로 이름과 일치하는 프로토타입 빈 정의를 사용하여 구성하는 에스펙트를 작성하는 방법을 보여줍니다.

public aspect DomainObjectConfiguration extends AbstractBeanConfigurerAspect {

    public DomainObjectConfiguration() {
        setBeanWiringInfoResolver(new ClassNameBeanWiringInfoResolver());
    }

    // the creation of a new bean (any object in the domain model)
    protected pointcut beanCreation(Object beanInstance) :
        initialization(new(..)) &&
        CommonPointcuts.inDomainModel() &&
        this(beanInstance);
}

Configuring AspectJ Aspects by Using Spring IoC

Spring 애플리케이션에서 AspectJ 에스펙트를 사용하는 경우, 이러한 에스펙트를 Spring으로 구성하는 것은 자연스럽습니다. AspectJ 런타임 자체가 에스펙트 생성을 담당하며, AspectJ 생성 모델에 따라 Spring을 통해 에스펙트를 구성하는 방법이 달라집니다.

대부분의 AspectJ 에스펙트는 싱글톤 에스펙트입니다. 이러한 에스펙트를 구성하는 것은 쉽습니다. 일반적인 빈 정의를 생성하고 factory-method="aspectOf" 속성을 포함하면 됩니다. 이렇게 하면 Spring이 인스턴스를 생성하는 대신 AspectJ에 요청하여 에스펙트 인스턴스를 가져옵니다.

<bean id="profiler" class="com.xyz.profiler.Profiler"
      factory-method="aspectOf"> 
    <property name="profilingStrategy" ref="jamonProfilingStrategy"/>
</bean>

싱글톤이 아닌 에스펙트는 구성하기 어렵지만, @Configurable 지원을 사용하여 AspectJ 런타임에서 생성된 에스펙트 인스턴스를 구성할 수 있습니다.

일부 @AspectJ 에스펙트를 AspectJ로 위빙하고(예: 도메인 모델 타입에 대한 로드 타임 위빙 사용), 다른 @AspectJ 에스펙트를 Spring AOP와 함께 사용하려는 경우, Spring AOP @AspectJ 자동 프록시 지원이 구성에서 정의된 @AspectJ 에스펙트 중 어떤 부분을 자동 프록시 구성에 사용할지를 지정해야 합니다.

<aop:aspectj-autoproxy>
    <aop:include name="thisBean"/>
    <aop:include name="thatBean"/>
</aop:aspectj-autoproxy>

<aop:aspectj-autoproxy/> 요소의 이름에 속지 마십시오. 이를 사용하면 Spring AOP 프록시가 생성됩니다.

Spring Framework에서 AspectJ를 사용한 로드 타임 위빙

로드 타임 위빙(LTW)은 AspectJ 에스펙트를 애플리케이션의 클래스 파일에 위빙하는 과정을 의미합니다. 이 섹션에서는 Spring Framework를 사용하는 LTW 구성 및 사용법에 중점을 둡니다.

Spring Framework의 LTW 지원은 더 세밀한 위빙 제어를 가능하게 합니다. 기본 AspectJ LTW는 자바 에이전트를 사용하여 JVM 시작 시 전역 설정으로 작동합니다. 그러나 Spring은 LTW를 클래스 로더별로 활성화할 수 있는 기능을 제공하여, 다중 애플리케이션 환경에서 더 유용하게 사용할 수 있습니다.

Spring의 LTW 지원은 애플리케이션 서버의 시작 스크립트를 수정하지 않고도 로드 타임 위빙을 활성화할 수 있습니다. 애플리케이션 컨텍스트를 구성하여 로드 타임 위빙을 활성화하면 됩니다.

예제: Spring을 사용한 AspectJ LTW

성능 문제를 진단하기 위해 간단한 프로파일링 에스펙트를 활성화해 성능 메트릭을 수집하는 예제를 살펴봅니다.

간단한 시간 기반 프로파일러를 사용하는 ProfilingAspect 클래스는 다음과 같습니다.

package com.xyz;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.util.StopWatch;

@Aspect
public class ProfilingAspect {

    @Around("methodsToBeProfiled()")
    public Object profile(ProceedingJoinPoint pjp) throws Throwable {
        StopWatch sw = new StopWatch(getClass().getSimpleName());
        try {
            sw.start(pjp.getSignature().getName());
            return pjp.proceed();
        } finally {
            sw.stop();
            System.out.println(sw.prettyPrint());
        }
    }

    @Pointcut("execution(public * com.xyz..*.*(..))")
    public void methodsToBeProfiled(){}
}

이제 AspectJ 위버에게 ProfilingAspect를 클래스에 위빙하도록 지시하는 META-INF/aop.xml 파일을 생성합니다.

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>

    <weaver>
        <!-- 애플리케이션의 특정 패키지와 하위 패키지의 클래스에만 위빙 -->
        <include within="com.xyz..*"/>
    </weaver>

    <aspects>
        <!-- 이 에스펙트만 위빙 -->
        <aspect name="com.xyz.ProfilingAspect"/>
    </aspects>

</aspectj>

Spring-specific 부분의 설정은 다음과 같습니다. 로드 타임 위버를 구성하는 부분입니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 서비스 객체; 해당 메서드에 대해 프로파일링 -->
    <bean id="entitlementCalculationService"
          class="com.xyz.StubEntitlementCalculationService"/>

    <!-- 로드 타임 위빙 활성화 -->
    <context:load-time-weaver/>
</beans>

이제 Main 클래스에서 LTW를 실행해 봅니다.

package com.xyz;

// imports

public class Main {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

        EntitlementCalculationService service =
                ctx.getBean(EntitlementCalculationService.class);

        // 프로파일링 에스펙트가 이 메서드 실행에 '위빙'됩니다.
        service.calculateEntitlement();
    }
}

마지막으로 다음 명령어를 사용하여 Main 클래스를 실행합니다.

java -javaagent:C:/projects/xyz/lib/spring-instrument.jar com.xyz.Main

이 명령어는 Spring 프레임워크에서 제공하는 InstrumentationSavingAgent 에이전트를 활성화합니다.

실행 결과는 다음과 같습니다.

Calculating entitlement

StopWatch 'ProfilingAspect': running time (millis) = 1234
------ ----- ----------------------------
ms     %     Task name
------ ----- ----------------------------
01234  100%  calculateEntitlement

이 예제는 간단하지만, LTW의 기본 사항은 모두 포함되어 있으며, 각 구성 요소의 설명이 포함됩니다.

Spring Framework에서의 AspectJ LTW 설정

AspectJ LTW를 사용하려면 최소한 다음 라이브러리가 필요합니다:

  • spring-aop.jar
  • aspectjweaver.jar

또한, LTW 지원을 위해 Spring의 LoadTimeWeaver 인터페이스를 사용해야 합니다. 이는 @EnableLoadTimeWeaving 어노테이션을 사용하여 활성화할 수 있습니다.

@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}

XML 기반 구성을 사용하는 경우, <context:load-time-weaver/> 요소를 사용할 수 있습니다.

<context:load-time-weaver/>

기본적으로 Spring LTW 인프라는 클래스 로더에서 ClassFileTransformer를 추가할 수 있도록 합니다. 위빙 자체는 ClassPreProcessorAgentAdapter 클래스에 의해 수행됩니다.

Environment-specific Configuration

Spring LTW 지원을 Tomcat, JBoss, WildFly 등 다양한 환경에서 사용할 수 있으며, 각 환경에 맞게 설정이 가능합니다.

Tomcat과 JBoss/WildFly는 로컬 인스트루멘테이션을 지원하는 일반적인 애플리케이션 클래스 로더를 제공합니다. 따라서 Spring의 네이티브 LTW는 이 클래스 로더 구현을 활용하여 AspectJ 위빙을 제공합니다. Tomcat이나 JBoss/WildFly와 같은 서버에서는 JVM 시작 스크립트를 수정하지 않고 LTW를 활성화할 수 있습니다.

기타 환경에서는 Spring이 제공하는 spring-instrument.jar JVM 에이전트를 사용하여 LTW를 활성화할 수 있습니다. 이를 위해 다음과 같은 JVM 옵션을 사용하여 가상 머신을 시작해야 합니다.

-javaagent:/path/to/spring-instrument.jar

이 설정은 독립 실행형 Spring Boot 애플리케이션과

같은 1개의 애플리케이션만 있는 JVM 배포 환경에서 유용합니다.