이제 스프링 AOP에 대해 알아보자.
AOP란 Aspect-Oriented Programming으로 흩어진 Aspect들을 모듈화하는 프로그래밍 기법을 말한다.
자바에서는 AspectJ나 스프링AOP를 통해 구현하며, 컴파일시점 / 로드타임 / 런타임에 AOP를 적용시킬 수 있다.
AOP 관련 개념이 잘 설명된 포스팅을 좀 찾아보았다.
https://tecoble.techcourse.co.kr/post/2021-06-25-aop-transaction/
https://sabarada.tistory.com/97?category=803157
스프링의 AOP란?
1. 프록시 기반의 AOP의 구현체이다.
2. 스프링 BEAN에만 적용할 수 있으며
3. 모든 AOP 기능을 제공하는 것이 아니라 가장 빈번하게 발생하는 문제에 대한 해결책을 제공하는 것이 목적이다.
프록시 패턴을 사용하는 이유는, 기존의 코드를 변경하지 않고 접근을 제어하거나 부가 기능을 추가하기 위함이다.
클라이언트가 interface로 프록시 객체에 접근을 하는데, 이 프록시 객체는 타겟 객체를 참조하고 있으며 실제 클라이언트의 요청을 처리하게 된다.
그렇다면 한번 예제를 통해 알아보자
간단한 Event클래스를 만들고 이 이벤트를 Create, Publish할 때는 수행시간을 로깅하고 Delete할 때는 로깅하지 않고 싶다.
//#1. EventService라는 인터페이스를 만든다.
public interface EventService {
void createEvent();
void publishEvent();
void deleteEvent();
}
//#2. EventService 인터페이스를 상속받는 SimpleEventService 객체를 만든다.
// 여기엔 비지니스로직이 작성된다.
@Service
public class SimpleEventService implements EventService{
@Override
public void createEvent() {
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("Created an Event");
}
@Override
public void publishEvent() {
try{
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("Published an Event");
}
@Override
public void deleteEvent(){
System.out.println("Delete event");
}
}
이 Service 객체 안에 create, publish 메소드에 각각 로그를 추가해줘도 되지만, 그렇게 하지 않고 Proxy 객체를 생성한다.
//#3. Proxy객체 생성.
@Primary // 같은 타입의 Bean이 있을 때 얘를 우선으로 선택
@Service // 얘도 Service Bean으로 등록
public class ProxySimpleEvent implements EventService{
// #3.1 target객체(SimpleEventService)가 구현한 interface와 형 일치해야한다.
@Autowired
SimpleEventService simpleEventService;
// #3.2 타겟 객체를 주입받는다.
@Override
public void createEvent() {
long begin = System.currentTimeMillis();
simpleEventService.createEvent();
// #3.3 타겟객체의 함수를 호출, 추가하고 싶은 기능을 추가한다.
System.out.println("create 수행시간 : " + (System.currentTimeMillis() - begin));
}
@Override
public void publishEvent() {
long begin = System.currentTimeMillis();
simpleEventService.publishEvent();
System.out.println("create 수행시간 : " + (System.currentTimeMillis() - begin));
}
@Override
public void deleteEvent(){
simpleEventService.deleteEvent();
}
}
하지만 이렇게 구현하는 경우는, 매번 프록시 객체를 생성해줘야 하고 매번 타겟객체를 주입받고, 매번 타겟 객체의 메소드를 호출해야하는 번거로움이 존재한다. 스프링에서는 이를 간결하게 처리하기 위한 기능을 제공한다. 바로 AbstractAutoProxyCreator 인터페이스를 사용하면 되는데 이는 BeanPostProcessor를 상속받는다.
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
그러면 스프링에서 Annotation을 사용하여 AOP를 구현하는 방법을 코드로 알아보자. 이 방법에서 ProxySimpleEvent객체는 삭제한다.
// #1. Aspect를 구현해주는 객체를 하나 생성한다.
@Component // #1.1 Bean으로 등록되어있어야 하며
@Aspect // #1.2 especially Aspect로 등록이 되어야 한다.
public class PerfAspect {
// #2. pointcut을 설정하는 여러가지 방법
// #2.1 상대 경로를 입력해준다.
//@Around("execution(* com.going..*.*(..))")
// #2.2 PerfLogging이라는 애노테이션이 붙은 메소드에 적용시킨다.
@Around("@annotation(PerfLogging)")
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable{
long begin = System.currentTimeMillis();
// #2.2.1 메소드 자체를 실행시켜주는 행위를 proceed라고 보면 됨.
Object retVal = pjp.proceed();
System.out.print("실행시간 : ");
System.out.println(System.currentTimeMillis()-begin);
return retVal;
}
// #2.3 simPleEventService Bean의 생성전에 pointcut을 설정
@Before("bean(simpleEventService)")
public void hello(){
System.out.println("=====Start Event Service====");
}
}
// #4. PefrLogging이라는 애노테이션을 만들어준다.
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
// class 파일까지 이 애노테이션을 유지하겠다.
// source로 지정해주면 컴파일시 사라지고, Runtime으로 설정해주면 런타임까지 유효함. Class가 default
public @interface PerfLogging {
}
// #5. Service Bean에서 Logging을 하고 싶은 메소드에 에노테이션을 붙여준다.
@Service
public class SimpleEventService implements EventService{
@Override
@PerfLogging
public void createEvent() {
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("Created an Event");
}
@Override
@PerfLogging
public void publishEvent() {
try{
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("Published an Event");
}
@Override
public void deleteEvent(){
System.out.println("Delete event");
}
}
'Study > Spring' 카테고리의 다른 글
[스프링 웹 MVC 설정] 01. @EnableWebMvc와 WebMvcConfigurer (0) | 2022.03.01 |
---|---|
[스프링 웹 MVC 동작원리] Servlet과 DispatcherServlet의 동작원리 (0) | 2022.03.01 |
스프링 핵심기술 05. 추상화 (0) | 2022.02.13 |
스프링 핵심기술 04. ApplicationContext가 상속받는 인터페이스들 (0) | 2022.02.06 |
스프링 핵심기술 03. 스프링 IoC 컨테이너 - Bean의 Scope (0) | 2022.02.03 |