프록시를 학습하기 위한 스프링 프로젝트의 의존성은 아래와 같다.
plugins {
id 'org.springframework.boot' version '2.5.5'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'hello'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//테스트에서 lombok 사용
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
}
test {
useJUnitPlatform()
}
예제를 3가지 상황으로 만들어 볼 것이다.
V1 - 인터페이스와 구현 클래스 - 스프링 빈으로 수동 등록
V2 - 인터페이스 없는 구체 클래스 - 스프링 빈으로 수동 등록
V3 - 컴포넌트 스캔으로 스프링 빈 자동 등록
V1 - 인터페이스와 구현 클래스 - 스프링 빈으로 수동 등록
주문 서비스를 위한 Controller, Service, Repository를 만들었다. 특히 Controller도 인터페이스를 만들었다는 것이 특이점이다. 그 외에는 모두 이전과 같다.
Controller Interface
@RequestMapping//스프링은 @Controller 또는 @RequestMapping이 있어야 스프링 컨트롤러로 인식.
@ResponseBody
public interface OrderControllerV1 {
@GetMapping("/v1/request")
String request(@RequestParam("itemId") String itemId);
@GetMapping("/v1/no-log")
String noLog();
}
Controller
public class OrderControllerV1Impl implements OrderControllerV1{
private final OrderServiceV1 orderService;
public OrderControllerV1Impl(OrderServiceV1 orderService) {
this.orderService = orderService;
}
@Override
public String request(String itemId) {
orderService.orderItem(itemId);
return "ok";
}
@Override
public String noLog() {
return "ok";
}
}
Service Interface
public interface OrderServiceV1 {
void orderItem(String itemId);
}
Service
public class OrderServiceV1Impl implements OrderServiceV1{
private final OrderRepositoryV1 orderRepository;
public OrderServiceV1Impl(OrderRepositoryV1 orderRepository) {
this.orderRepository = orderRepository;
}
@Override
public void orderItem(String itemId) {
orderRepository.save(itemId);
}
}
Repository Interface
public interface OrderRepositoryV1 {
void save(String itemId);
}
Repository
public class OrderRepositoryV1Impl implements OrderRepositoryV1{
@Override
public void save(String itemId) {
//저장로직
if(itemId.equals("ex")) throw new IllegalStateException("예외 상황 발생");
sleep(1000);
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
그리고 현재 @Controller, @Service, @Repository와 같은 어노테이션을 달아주지 않았으므로, 수동으로 이들을 등록해주어야 한다.
@Configuration
public class AppV1Config {
@Bean
public OrderControllerV1 orderControllerV1(){
return new OrderControllerV1Impl(orderServiceV1());
}
@Bean
public OrderServiceV1 orderServiceV1() {
return new OrderServiceV1Impl(orderRepositoryV1());
}
@Bean
public OrderRepositoryV1 orderRepositoryV1() {
return new OrderRepositoryV1Impl();
}
}
그리고 추가로 아래 메인 함수가 있는 코드를 보면 이전과 꽤나 다르다.
@Import(AppV1Config.class)
@SpringBootApplication(scanBasePackages = "hello.proxy.app") //주의
public class ProxyApplication {
public static void main(String[] args) {
SpringApplication.run(ProxyApplication.class, args);
}
}
@Import(Appv1Config.class) : 클래스를 스프링 빈으로 등록한다. 여기서는 AppV1Config.class를 스프링 빈으로 등록한다. 일반적으로 @Configuration 같은 설정 파일을 등록할 때 사용한다. (물론 스프링 빈을 등록할 때도 사용된다.)
@SpringBootApplication(scanBeansPackages = "hello.proxy.app") : @ComponentScan의 기능과 같다. 컴포넌트 스캔을 시작할 위치를 지정한다. 이 값을 설정하면 해당 패키지와 그 하위 패키지를 컴포넌트 스캔한다. 이 값을 설정하지 않으면 현재 ProxyApplication이 있는 패키지와 그 하위 패키지를 스캔한다.
위와 같이 @Import와 @SpringBootApplication(scanBeansPackages="")를 설정한 이유는 각 V1, V2, V3버전 마다 다른 Config 클래스를 빈으로 떼었다가 붙였다가를 하기 위함이다.
V2 - 인터페이스 없는 구체 클래스 - 스프링 빈으로 수동 등록
Controller
@Slf4j
@ResponseBody
@RequestMapping
public class OrderControllerV2{
private final OrderServiceV2 orderService;
public OrderControllerV2(OrderServiceV2 orderService) {
this.orderService = orderService;
}
@GetMapping("/v2/request")
public String request(String itemId) {
orderService.orderItem(itemId);
return "ok";
}
@GetMapping("/v2/no-log")
public String noLog() {
return "ok";
}
}
Service
public class OrderServiceV2{
private final OrderRepositoryV2 orderRepository;
public OrderServiceV2(OrderRepositoryV2 orderRepository) {
this.orderRepository = orderRepository;
}
public void orderItem(String itemId) {
orderRepository.save(itemId);
}
}
Repository
public class OrderRepositoryV2{
public void save(String itemId) {
//저장로직
if(itemId.equals("ex")) throw new IllegalStateException("예외 상황 발생");
sleep(1000);
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
메인 함수
@Import({AppV1Config.class, AppV2Config.class})
//@Import(AppV1Config.class)
@SpringBootApplication(scanBasePackages = "hello.proxy.app") //주의
public class ProxyApplication {
public static void main(String[] args) {
SpringApplication.run(ProxyApplication.class, args);
}
}
V3 - 컴포넌트 스캔으로 스프링 빈 자동 등록
Controller
@Slf4j
@RestController
public class OrderControllerV3 {
private final OrderServiceV3 orderService;
public OrderControllerV3(OrderServiceV3 orderService) {
this.orderService = orderService;
}
@GetMapping("/v3/request")
public String request(String itemId) {
orderService.orderItem(itemId);
return "ok";
}
@GetMapping("/v3/no-log")
public String noLog() {
return "ok";
}
}
Service
@Service
public class OrderServiceV3 {
private final OrderRepositoryV3 orderRepository;
public OrderServiceV3(OrderRepositoryV3 orderRepository) {
this.orderRepository = orderRepository;
}
public void orderItem(String itemId) {
orderRepository.save(itemId);
}
}
Repository
@Repository
public class OrderRepositoryV3 {
public void save(String itemId) {
//저장로직
if(itemId.equals("ex")) throw new IllegalStateException("예외 상황 발생");
sleep(1000);
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
위의 클래스들은 @Controller, @Service, @Repository를 사용하여 빈으로 등록해주고 있으므로 따로 Config클래스가 필요하지 않다.
다음부터 각 V1, V2, V3에 프록시를 도입해보자.
김영한님의 인프런 강의와 PDF를 바탕으로 정리하였습니다.
'Spring > spring AOP' 카테고리의 다른 글
스프링 코어2 - 인터페이스 기반 프록시 (0) | 2022.01.08 |
---|---|
스프링 코어2 - 프록시 패턴과 데코레이터 패턴 (0) | 2022.01.06 |
스프링 코어2 - 템플릿 콜백 패턴 (0) | 2022.01.05 |
스프링 코어2 -전략 패턴 (0) | 2022.01.05 |
스프링 코어2 - 템플릿 메서드 패턴(로그 추적기에 패턴 도입) (0) | 2022.01.04 |