ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스프링4.0 - AOP 기초
    웹프로그래밍/spring 2018. 7. 15. 19:23

    AOP란?

    어플리케이션의 공통 관심 사항과 핵심 관심 사항을 기준으로 프로그래밍 하므로써 공통 모듈을 여러 코드에 쉽게 적용하는 것


    핵심 로직에서 공통 기능을 직접 호출 하지는 않는다.

    공통 기능은 컴파일하거나 클래스를 로딩할때, 또는 클래스의 객체를 생성할때 AOP가 적용된다. 

    즉, 핵심 로직 구현 코드에  공통 기능이 삽입된다.


    ※spring API 문서:

    https://docs.spring.io/spring-framework/docs/2.0.x/reference/aop.html




    AOP 용어

    Joinpoint - 실행지점, 즉 Advice가 적용될 메소드


    Pointcut - JoinPoint와 Advice를 연결해주는 설명어. 예를들어, Pointcut이 가리키는 특정 이름의 메소드에 Advice가 적용된다.

     

    Advice - 공통 관심 기능을 JoinPoint에서 언제 적용할 것인지를 정의한다. ex) around, before, after Advice 등..


    Weaving - 핵심로직과 Aspect를 연결하는 것.  Spring AOP에서는 runtime 단계에서 수행된다.


    Aspect - 공통 관심 모듈(ex. 트랜잭션 관리, 보안)




    • 3가지 Weaving 방식


    컴파일 시에 weaving - 자바 코드를 컴파일 하는 단계에서 핵심 로직이 구현된 자바 코드에 공통 코드를 삽입하면 AOP가 적용된 클래스 파일이 생성된다.


    클래스 로딩 시에 weaving - JVM이 로딩한 클래스의 바이너리 정보를 토대로 알맞은 위치에 공통 코드를 삽입한 새로운 클래스 바이너리 코드를 만든다.


    런타임 시에 weaving - 소스 코드나 클래스 정보를 직접 변경하지 않고 프록시를 이용하여 AOP를 적용한다. 오직 핵심 로직의 메소드가 실행되기 전이나 후에 프록시 객체를 이용하여 Advice를 적용한다. (필드 값 변경과 같은 Joinpoint는 적용할 수 없다.)



    • Advice 종류

    Before Advice - 대상 객체의 메서드 호출 전

    After Returning Advice - 대상 객체의 메서드가 익셉션 업이 실행 된 이후

    After Throwing Advice - 대상 객체의 메서드 실행 도중 익셉션이 발생한 경우

    After Advice - 익셉션 여부에 관계없이 대상 객체의 메서드 실행 후

    Around Advice - 대상 객체의 메서드 실행 전, 후







    스프링에서의 AOP


    스프링은 프록시 기반의 AOP를 지원한다. 오직 메소드 호출 Joinpoint만 지원하며 자바 언어 기반이다.

    필드 값 변경과 같은 다양한 Joinpoint를 사용하려면 AspectJ를 사용해야 한다.



    • 프록시를 이용한 AOP

    스프링 컨테이너를 초기화하는 과정에서 xml 설정 정보에 저장된 bean 객체에 대한 프록시 객체를 생성해서

    원본 bean 객체 대신 프록시 객체를 사용한다. 그 방식은 2가지로 나뉜다.


    대상 객체가 인터페이스를 구현한 경우 - java.lang.reflect.Proxy를 이용하여 프록시 객체를 생성한다. 이때 생성된 프록시 객체는 대상 객체와 동일한

    인터페이스를 구현하므로 인터페이스의 정의되지 않은 메서드에 대해서는 AOP가 적용되지 않는다.


    대상 객체가 인터페이스를 구현하지 않은 경우 - 스프링이 CGLIB를 이용하여 대상 객체에 대한 프록시 객체를 생성한다. 즉, CGLIB가 대상 클래스를 상속받아 프록시를 구현한다. 그러므로 대상 클래스 또는 그 메서드가 final 인 경우 AOP를 적용할 수 없다.




    • XML 스키마 기반 AOP 구현


    1. pom.xml에 스프링 AOP를 사용하기 위한 의존 설정 추가


    <dependencies>

    <dependency>

    <groupId>org.springframework</groupId>

    <artifactId>spring-aop</artifactId>

    <version>4.0.4.RELEASE</version>

    </dependency>


    <dependency>

    <groupId>org.aspectj</groupId>

    <artifactId>aspectjweaver</artifactId>

    <version>1.7.4</version>

    </dependency>

    </dependencies>



    2. 공통 기능을 제공할 클래스 구현

    먼저, 클래스에서 org.aspectj.lang.ProceedingJoinPoint 클래스를 import한다.

     그리고 Object 타입을 리턴하고 ProcedingJoinPoint 타입을 파라미터로 갖는 public 메소드를 만든다.

    ProcedingJoinPoint 타입의 joinPoint 파라미터에 의해서 메소드를 공통 기능으로 구현할 수 있다.


    import org.aspectj.lang.ProceedingJoinPoint;



    public class Profiler {

    public Object trace(ProceedingJoinPoint joinPoint) throws Throwable {

    String signatureString = joinPoint.getSignature().toShortString();

    System.out.println(signatureString + "시작");

    long start = System.currentTimeMillis();

    try {

    Object result = joinPoint.proceed();

    return result;

    } finally {

    long finish = System.currentTimeMillis();

    System.out.println(signatureString + "종료");

    System.out.println(signatureString + "실행 시간:"+(finish - start)+"ms");

    }

    }

    }




    3. XML 설정 파일에서 <aop:config>로 Aspect 설정 후 Advice가 적용될 Pointcut을 정한다.

    <aop:pointcut>에서 expression은 spring.chap06 패키지 및 하위패키지에 모든 public 메서드를 Pointcut으로 설정한다.




    <?xml version="1.0" encoding="UTF-8"?>


    <beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans.xsd

    http://www.springframework.org/schema/aop

    http://www.springframework.org/schema/aop/spring-aop.xsd">


    <!-- 공통 기능 제공 클래스 등록 -->

    <bean id="profiler" class="spring.chap06.aop.Profiler"/>


    <!-- Aspect 설정:Advice를 어떤 Pointcut에 적용할지 설정 -->

    <aop:config>

    <aop:aspect id="traceAspect" ref="profiler">

    <aop:pointcut id="publicMethod" 

    expression="execution(public * spring.chap06..*(..))"/>

    <aop:around method="trace" pointcut-ref="publicMethod"/>

    </aop:aspect>

    </aop:config>


    <!-- 핵심 기능 제공 클래스 -->

    <bean id="writeArticleService" class="spring.chap06.board.WriteArticleServiceImpl">

    <constructor-arg>

    <ref bean="articleDao" />

    </constructor-arg>

    </bean>


    <bean id="articleDao" class="spring.chap06.board.MemoryArticleDao"/>


    <bean id="memberService" class="spring.chap06.member.MemberServiceImpl"/>

    </beans>