Spring/스프링 프레임워크 핵심 원리

[Spring] #19. 스프링 AOP: 프록시 기반 AOP

sujin7837 2020. 12. 26. 14:22
반응형

스프링 AOP 특징

-프록시 기반의 AOP

-스프링 빈에만 AOP를 적용할 수 있습니다.

-모든 AOP 기능을 제공하는 것이 목적이 아니라, 스프링 IoC와 연동하여 엔터프라이즈 애플리케이션에서 가장 흔한 문제에 대한 해결책을 제공하는 것이 목적입니다.

 

프록시 패턴

-프록시에는 인터페이스가 있고, 클라이언트는 인터페이스를 통해 프록시 객체를 사용하게 됩니다. 프록시 객체는 같은 타입의 타겟 객체를 가지고 있고, 타겟 객체는 원래 해야할 일을 가지고 있으며 프록시 객체가 타겟 객체를 감싸서 클라이언트의 요청을 처리하는 형태입니다.

-목적: 접근 제어 또는 부가 기능 추가

프록시 패턴을 사용하지 않은 경우

 

//EventService.java

public interface EventService {
    void createEvent();
    void publishEvent();
    void deleteEvent();
}

//subject
//SimpleEventService.java

@Service
public class SimpleEventService implements EventService {
    @Override
    public void createEvent() {
        long begin=System.currentTimeMillis();    //Crosscutting Concerns(흩어진 관심사)
        
        try {
            Thread.sleep(millis: 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Created an event");
        System.out.println(System.currentTimeMillis()-begin);    //Crosscutting Concerns(흩어진 관심사)
    }

    @Override
    public void publishEvent() {
        long begin=System.currentTimeMillis();    //Crosscutting Concerns(흩어진 관심사)

        try {
            Thread.sleep(millis: 2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Published an event");
        System.out.println(System.currentTimeMillis()-begin);    //Crosscutting Concerns(흩어진 관심사)
    }

    public void deleteEvent() {
        System.out.println("Delete an event");
    }
}

//real subject
//AppRunner.java

@Component
public class AppRunner implements ApplicationRunner {    //인터페이스가 있는 경우, 인터페이스로 주입받는 것이 가장 좋다!
    @Autowired
    EventService eventService;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        eventService.createEvent();
        eventService.publishEvent();
        eventService.deleteEvent();
    }
}

 

프록시 패턴을 사용한 경우

//ProxySimpleEventService.java

@Primary
@Autowired
public class ProxySimpleEventService implements EventService {    
    @Autowired
    SimpleEventService simpleEventService;    //1. 프록시 타입은 subject의 빈을 주입받아서 사용해야 하므로, SimpleEventService를 명시해주어 사용 가능합니다.

//2. EventService simpleEventService;    EventService 타입을 받지만 빈의 이름에 기반해서 simpleEventService를 주입받을 수 있습니다.

    @Override
    public void createEvent() {
        long begin=System.currentTimeMillis();    
        simpleEventService.createEvent();
        System.out.println(System.currentTimeMillis()-begin);
    }

    @Override
    public void publishEvent() {
        long begin=System.currentTimeMillis();
        simpleEventService.publishEvent();
        System.out.println(System.currentTimeMillis()-begin);
    }

    @Override
    public void deleteEvent() {
        simpleEventService.deleteEvent();
    }
}

//프록시 타입을 사용하지 않을 때는 시간을 재는 기능(부가 기능)을 real subject인 SimpleEventService.java에서 했으나 프록시 타입을 사용할 때는 여기서 넣어줍니다.
//SimpleEventService.java

@Service
public class SimpleEventService implements EventService {
    @Override
    public void createEvent() {
   // long begin=System.currentTimeMillis();    

        try {
            Thread.sleep(millis: 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Created an event");

   //  System.out.println(System.currentTimeMillis()-begin);    
    }
    
    @Override
    public void publishEvent() {
   // long begin=System.currentTimeMillis();    

        try {
            Thread.sleep(millis: 2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Published an event");
        
   // System.out.println(System.currentTimeMillis()-begin); 
    }

    public void deleteEvent() {
        System.out.println("Delete an event");
    }
}

//real subject
//AppRunner.java

@Component
public class AppRunner implements ApplicationRunner {    //인터페이스가 있는 경우, 인터페이스로 주입받는 것이 가장 좋다!
    @Autowired
    EventService eventService;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        eventService.createEvent();
        eventService.publishEvent();
        eventService.deleteEvent();
    }
}

 

스프링 부트를 사용하는 경우

@SpringBootApplication
public class Demospring51Application {
    public static void main(String[] args) {
        SpringApplication app=new SpringApplication(Demospring51Application.class);
        app.setWebApplicationType(WebApplicationType.NONE);
        app.run(args);
    }
}

*속도도 개선되고 훨씬 편리함

프록시 기반 AOP의 문제점

-매번 프록시 클래스를 작성해야 합니다.

-클래스마다 중복 코드가 발생합니다.

-여러 클래스와 메소드에 적용하기 번거롭습니다.

스프링 AOP

-스프링 IoC 컨테이너의 기반 시설과 동적 프록시를 혼합하여 위의 문제를 해결할 수 있습니다.

-동적 프록시: 애플리케이션이 동작하는 중에 동적으로 어떤 객체를 감싸는 프록시 객체를 만드는 방법입니다.

-스프링 IoC : 기존의 빈을 대체하는 동적 프록시 빈을 만들어 등록시켜 줍니다.

·BeanPostProcessor: 어떤 빈이 등록되면, 그 빈이 가공할 수 있는 life cycle interface

·AbstractAutoProxyCreator implements BeanPostProcessor

ex) 위의 코드에서 simpleEventService라는 빈이 등록되면, 스프링이 AbstractAutoProxyCreator라는 BeanPostProcessor로 simpleEventServie 빈을 감싸는 프록시 빈을 만들어서 그 빈을 simpleEventService 빈 대신에 등록해줍니다.

반응형