DevLogs/LearningSpring

Spring AOP Introduction 기능을 활용하여 Mixin을 구현해 보자

밥먹고해요 2024. 11. 7. 17:07

1. 우선, Robot 타입의 bean을 정의한다.

@Component
public class Robot {
    public void performTask() {
        System.out.println("Performing a task.");
    }
}

 

 

2. Robot 타입의 bean의 기능을 추가할 인터페이스들과 구현 클래스 하나씩을 정의한다.

public interface Flyable {
    void fly();
}

public class FakeFlyable implements Flyable {
    @Override
    public void fly() {
        System.out.println("Flying high in the sky!");
    }
}

public interface Walkable {
    void walk();
}

public class FakeWalkable implements Walkable {
    @Override
    public void walk() {
        System.out.println("Walking on the ground.");
    }
}

 

 

3. @org.aspectj.lang.annotation.DeclareParents 어노테이션으로, Robot 타입의 bean에 Flyable과 Walkable 인터페이스의 구현체를 합체(?)시키도록 정의한다. 이때, 각 인터페이스의 구현체는 defaultImpl 속성으로 기술한다.

@Aspect
@Component
public class RobotIntroductionAspect {
    @DeclareParents(value = "com.example.Robot", defaultImpl = FakeFlyable.class)
    public Flyable flyable;

    @DeclareParents(value = "com.example.Robot", defaultImpl = FakeWalkable.class)
    public Walkable walkable;
}

 

 

4. main 메서드를 실행하면, 마치 Robot이 FakeFlyable과 FakeWakable 클래스를 다중 상속한 것처럼 동작한다.

@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainApp.class);
        Robot robot = context.getBean(Robot.class);

        robot.performTask(); // 기본 메서드 호출
        ((Flyable) robot).fly(); // fly 메서드가 호출됨!
        ((Walkable) robot).walk(); // walk 메서드가 호출됨!
    }
}

 

 

하지만, 위 코드는 명시적 캐스팅이 항상 필요하다. AOP 기술도 필요하다. 그래서, 런타임에서의 성능저하는 필연적이다.

동일한 기능을 인터페이스의 디폴트 메서드를 활용한 방식으로 변경해 본 코드는 다음과 같다.

interface Flyable {
    default void fly() {
        System.out.println("Flying high in the sky!");
    }
}

interface Walkable {
    default void walk() {
        System.out.println("Walking on the ground.");
    }
}

class Robot implements Flyable, Walkable {
    public void performTask() {
        System.out.println("Performing a task.");
    }
}

public class MainApp {
    public static void main(String[] args) {
        Robot robot = new Robot();

        robot.performTask(); // 기존 메서드 호출
        robot.fly(); // Flyable의 디폴트 메서드 호출
        robot.walk(); // Walkable의 디폴트 메서드 호출
    }
}