Spring Core/03. AOP

05. Spring 내부의 AOP들

  • -
반응형

스프링에서는 이미 많은 부분에서 AOP들이 적용되어있다. 이번 포스트에서는 여러가지 예를 통해서 어떤 면에서 스프링의 AOP가 활용되는지 살펴보자.

 

singleton의 비밀

스프링은 빈을 관리할 때 특별히 scope 속성을 변경하지 않는다면 singleton으로 관리해준다. 사실 @Bean의 메서드를 호출하기만 하면 언제나 새로운 빈이 만들어지는데 어떻게 그런일이 가능할까?

package com.doding.aoptest.internalusage;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfig {

  @Bean
  public Engine engine() {     // engine()가 불리면 Engine이 만들어졌을 것!
    return new Engine();
  }

  @Bean
  public Car car() {           // car()가 불리면 Car를 만드는데.
    return new Car(engine());  // 내부적으로 engine()을 부른다. 또 만드는 것일까?
  }

  class Engine {
  }

  class Car {
    Engine engine;

    public Car(Engine engine) {
      this.engine = engine;
    }
  }
}

 

@Configuration이 적용되어있는 클래스의 실제 타입을 살펴보자.

@SpringBootTest
@Slf4j
@ActiveProfiles("test")
public class InternalUsageTest {
  @Autowired
  private BeanConfig config;

  @Test
  public void configTest() {
    assertNotNull(config);
    log.debug("config: {}", config.getClass().getName());
  }
}
15:23:24 [DEBUG] c.d.a.i.InternalUsageTest.configTest.22 
     - config: com.doding.aoptest.internalusage.BeanConfig$$SpringCGLIB$$0

 

출력 결과 실제 타입은 원래의 클래스가 아닌 프록시 객체임을 알 수 있다.  즉 외부에서 빈을 요청할 때 @Configuration의 빈 생성 메서드를 요청하는것이 아니라 프록시에게 요청하는 것이고 프록시는 만약 해당 타입의 빈이 있을 경우 새롭게 빈을 생성하지 않고 기존 빈 객체를 리턴해주는 것이다.

 

스프링에서 비동기 작업의 처리

무거운 작업을 비동기로 병렬처리하기 위해 스레드나 스레드 풀을 만들고 관리하는 일은 상당히 번거롭다. 스프링에서는 간단한 애너테이션 몇 개로 편하게 비동기 처리를 구현한 수 있다. 미리 밝히지만 이것도 역시 AOP가 있기에 가능하다.

 

@Async

@Async는 비동기로 수행할 작업이 있는 메서드에 선언한다. 

class GPTEngine {
  @Async
  public void heavyWork(int i) throws InterruptedException {
    Thread.sleep(1000);
    log.debug("{}에 의해 {}번째 요청 처리 중", Thread.currentThread(), i);
  }
}

heavyWork는 무거운 작업을 수행하는 메서드이고 비동기로 처리할 계획이다. 그냥 처리한다면 별도의 Thread를 만들고 run 메서드를 override 하는 등 복잡한 절차를 거쳤을 것이다. 하지만 여기서는 @Async 가 전부이다.

application.properties 파일에서는 thread pool의 기본 thread 개수나 최대 thread 개수 등을 설정할 수 있다.

spring.task.execution.pool.core-size=8
spring.task.execution.pool.max-size=16

 

@EnableAsync

@Async를 활성화 하기 위해서는 @Configuration 클래스에서 @EnableAsync 를 선언해주어야 한다.

@SpringBootApplication
@EnableAsync
public class AoptestApplication {
  public static void main(String[] args) {
    SpringApplication.run(AoptestApplication.class, args);
  }
}

 

단위테스트

단위테스트를 통해 비동기 작업의 동작을 테스트 해보자.

@Autowired
private GPTEngine engine;

@Test
public void asyncTest() throws InterruptedException {
  log.debug("engine type: {}", engine.getClass().getName());
  for (int i = 0; i < 10; i++) {
    engine.heavyWork(i);
  }
}

테스트 코드에서는 단지 heavyWork를 실행시키기만 할 뿐 역시 Thread에 대한 언급은 없다.

16:20:35 [DEBUG] c.d.a.i.InternalUsageTest.asyncTest.34 
      - engine type: com.doding.aoptest.internalusage.BeanConfig$GPTEngine$$SpringCGLIB$$0
16:20:36 [DEBUG] c.d.a.i.BeanConfig.heavyWork.27 - Thread[task-1,5,main]에 의해 0번째 요청 처리 중
16:20:36 [DEBUG] c.d.a.i.BeanConfig.heavyWork.27 - Thread[task-5,5,main]에 의해 4번째 요청 처리 중
16:20:36 [DEBUG] c.d.a.i.BeanConfig.heavyWork.27 - Thread[task-3,5,main]에 의해 2번째 요청 처리 중
16:20:36 [DEBUG] c.d.a.i.BeanConfig.heavyWork.27 - Thread[task-4,5,main]에 의해 3번째 요청 처리 중
16:20:36 [DEBUG] c.d.a.i.BeanConfig.heavyWork.27 - Thread[task-6,5,main]에 의해 5번째 요청 처리 중
16:20:36 [DEBUG] c.d.a.i.BeanConfig.heavyWork.27 - Thread[task-2,5,main]에 의해 1번째 요청 처리 중
16:20:36 [DEBUG] c.d.a.i.BeanConfig.heavyWork.27 - Thread[task-7,5,main]에 의해 6번째 요청 처리 중
16:20:36 [DEBUG] c.d.a.i.BeanConfig.heavyWork.27 - Thread[task-8,5,main]에 의해 7번째 요청 처리 중
16:20:37 [DEBUG] c.d.a.i.BeanConfig.heavyWork.27 - Thread[task-1,5,main]에 의해 8번째 요청 처리 중
16:20:37 [DEBUG] c.d.a.i.BeanConfig.heavyWork.27 - Thread[task-5,5,main]에 의해 9번째 요청 처리 중

실행 결과를 살펴보면 빈의 타입이 proxy이며 Thread에 대한 코드 한줄 없이  ThreadPool이 잘 동작하고 있음을 알 수 있다.

즉 proxy 객체가 앞에 끼어들어서 무거운 작업을 thread pool에서 동작하도록 처리하는 것이다.

반응형

'Spring Core > 03. AOP' 카테고리의 다른 글

06. AOP 작성 주의 사항  (0) 2024.02.16
04. advice의 타입  (0) 2020.06.20
03. pointcut 작성  (0) 2020.06.20
02. Aspect 작성과 동작  (0) 2020.06.18
01. AOP 기본 컨셉  (0) 2020.06.18
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.