AOP와 스프링

  □ AOP (Aspect Oriented Programming) 소개
    ■ 공통으로 사용되는 모듈에 대한 복잡한 의존 관계
      - 공통으로 사용되는 기능이 많아질 수록, 그리고 공통 모듈을 사용하는 클래스가 많아질 수록 의존 관계는 점점 복잡해짐.
      - 공통 모듈을 사용하는 코드가 여러 곳에서 중복되는 문제도 발생.
    ■ 정의
      - 공통의 관심 사항을 적용해서 발생하는 의존 관계의 복잡성과 코드 중복을 해소해 주는 프로그래밍 기법.
    ■ 특징
      - 각 클래스에서 공통 관심 사항을 구현한 모듈에 대한 의존 관계를 갖기보다는, Aspect를 이용하여 핵심 로직을 구현한 각 클래스에
        공통 기능을 적용.
      - 핵심로직구현 클래스에서 공통 모듈을 사용하는 코드를 포함하지 않음.
    ■ Aspect가 공통 모듈을 로직을 구현할 클래스에 적용

      - 핵심 로직을 구현한 클래스를 실행하기 전후에 Aspect를 적용.
      - 핵심 로직을 수행하면 그에 앞서 공통 모듈을 실행하거나 또는 로직 수행 이후에 공통 모듈을 수행하는 방식으로 공통 모듈을 적용.

    ■ 장점
      - Aspect가 핵심 로직 구현 클래스에 의존하지 않음.
      - 하나의 Aspect를 개발하면 Aspect를 수정할 필요 없이 여러 클래스에 적용할 수 있음.

  □ 스프링에서의 AOP

    ■ 장점
      - 간단한 설정만으로 공통 기능을 여러 클래스에 적용할 수 있음.
      - 일반적인 웹 어플리케이션을 구현하는데에 필요한 수준으로 AOP를 지원.
      - 핵심 로직 코드의 수정 없이 웹 어플리케이션에 보안, 로깅, 트랜잭션과 같은 공통 관심 사항을 AOP를 이용하여 간단하게 적용.
    ■ 예제
      - 메서드의 실행 시간을 출력해주는 코드.
      ○ AOP를 적용하지 않은 방식
 public void someMethod() {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();

    executeLogic();

    stopWatch.stop();
  
    long executionTime = stopWatch.getTotalTimeMillis();
 }
         - 요구가 변경될 때마다 많은 코드를 변경해야 함.
      ○ AOP를 적용한 방식
        ● LoggingAspect.java
          - 공통 관심 사항을 구현한 POJO 클래스.
 package kame.spring.chap01;

 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.springframework.util.StopWatch;

 // 오직 ProceedingJoinPoint에만 의존.
 public class LoggingAspect {
    private Log log = LogFactory.getLog(getClass());

    // Aspect가 적용되는 메서드의 실행 시간을 구한 뒤 Log를 통해 출력.
    // ProceedingJoinPoint 객체는 Aspect가 적용되는 객체 및 메서드에 대한 정보를 담고 있음.
    public Object logging(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("기록 시작");
        StopWatch stopWatch = new StopWatch();

        try {
            stopWatch.start();
            // 핵심 로직 실행.
            Object retValue = joinPoint.proceed();
            
            return retValue;
        } catch(Throwable e) {
            throw e;
        } finally {
            stopWatch.stop();
            log.info("기록 종료");
            log.info(joinPoint.getSignature().getName() + "메서드 실행 시간 : " 
                + stopWatch.getTotalTimeMillis());
        }
    }

 }
        ● commonsConcern.xml
          - Aspect를 어떤 클래스의 어떤 메서드에 적용할지를 설정. 
 <?xml version="1.0" encoding="UTF-8"?>

 <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
>

    <bean id="logging" class="kame.spring.chap01.LoggingAspect" />

    <aop:config>
        <aop:pointcut id="servicePointcut" expression="execution(* *..Service.*(..))" />

        <aop:aspect id="loggingAspect" ref="logging">
            <aop:around pointcut-ref="servicePointcut" method="logging" />
        </aop:aspect>
    </aop:config>

 </beans>
          - XML 스키마 확장(aop 네임스페이스)를 사용하여 AOP 설정.
          - 이름이 Service로 끝나는 인터페이스를 구현한 모든 클래스의 모든 메서드에 LoggingAspect가 적용.
        ● MainForAop.java
          - 실제로 Aspect가 적용되었는지 테스트.
 package kame.spring.chap01;

 import org.springframework.context.ApplicationContext;
 import org.springframework.context.support.ClassPathXmlApplicationContext;

 public class MainForAop {
    public static void main(String[] args) {
        String[] configLocations = 
           new String[] {"applicationContext.xml", "commonConcern.xml"};
        ApplicationContext context = 
           new ClassPathXmlApplicationContext(configLocations);
        WriteArticleService articleService =
           (WriteArticleService)context.getBean("writeArticleService");

        articleService.write(new Article());

    }
 }
        ● 결과
 INFO LoggingAspect [2010-05-06 20:54:02,953] - 기록 시작
 WriteArticleServiceImpl.write() 메서드 실행
 MysqlArticleDao.insert() 실행
 INFO LoggingAspect [2010-05-06 20:54:02,956] - 기록 종료
 INFO LoggingAspect [2010-05-06 20:54:02,957] - write 메서드 실행 시간 : 0
        ● LoggingAspect의 실행 순서
          - 스프링은 프록시를 이용하여 AOP를 구현.
          - MainForAop 클래스가 사용하는 객체는 WriteArticleServiceImpl 객체가 아니라 스프링이 런타임에 생성한 프록시 객체.
            (프록시가 내부적으로 다시 LoggingAspect의 logging() 메서드를 호출하여 공통 관심 사항이 실제 WriteArticleServiceImpl 객체
             에 적용.)


블로그 이미지

532

처음 프로그래밍을 시작한 개발자들은 Hello World!를 기억 할 것이다. Hello world가 Hell World가 될 줄 몰랐던 한 개발자의 이야기. 게시글의 출처를 표기하며, 출처를 남기지 않고 펌을 하는것에 대해 법적 책임을 묻지 않습니다.

,