728x90
반응형

스프링 AOP (Aspect Oriented Programming)

 

포인트 컷 & 조인포인트

 

 - 위빙 (Weaving) : 보조업무가 프록시(Proxy)를 통해서 주 업무에 주입되는 것. 즉, 타깃 객체에 애스펙트를 적용해서 새로운 프록시 객체를 생성하는 절차.
 - 조인포인트 (Joint Point) : 위빙하게 되는 함수. 그 지점.

 - 포인트 컷 (PointCuts) : 객체의 특정 함수만 조인포인트 역할을 하도록 하는 것. 

 

 

* 이전 포스팅에서의 스프링 AOP 는 특정 객체(클래스)를 대상으로 타깃(Target)을 설정하였는데, 그렇기 때문에 프록시 적용이 해당 객체 단위 전체에 적용이 됨. 즉 타깃에 속한 모든 조인포인트에 보조 업무(Proxy Class)가 적용됨. 하지만, 포인트 컷을 활용하면 특정 클래스의 특정 함수에만 보조 업무를 삽입할 수 있음.

 

 

* 예제 코드

 

<bean id="cal" class="aoptest.TestCalculator"></bean>

<bean id="logPrintAroundAdvice" class="aoptest.LogPrintAroundAdvice"/>

<bean id="logPrintBeforeAdvice" class="aoptest.LogPrintBeforeAdvice"/>

<bean id="logPrintAfterReturningAdvice" class="aoptest.LogPrintAfterReturningAdvice"/>

<bean id="logPrintAfterThrowingAdvice" class="aoptest.LogPrintAfterThrowingAdvice"/>

 

<bean id="namePointcut" class="org.springframework.aop.support.NameMatchMethodPointcut">

<proporty name="mappedNames">

         <list>

              <value>add</value>

              <value>subtract</value>

          </list>

     </property>

</bean>

 

<bean id="nameAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">

      <property name="pointcut" ref="namePointcut"></property>

      <property name="advice" ref="logPrintAroundAdvice"></property>

</bean>

 

<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">

    <property name="target" ref="cal"></property>

    <property name="interceptorNames">

        <list>

            <value>nameAdvisor</value>

            /* <value>logPrintAroundAdvice</value>

            <value>logPrintBeforAdvice</value>

            <value>logPrintAfterReturningAdvice</value>

            <value>logPrintAfterThrowingAdvice</value> */

        </list>

    </property>

</bean> 

 

 - 코드 설명 -

 

1)  특정 함수(add, subtract) 만 조인포인트 함수가 되도 록 포인트 컷 지정.
2) 특정 포인트 컷에 특정 어드바이스(핸들러) 지정.

 

어드바이저 = 포인트 컷 + 어드바이스


 

3) 특정 어드바이스(Advice) 대신 어드바이저(Advisor) 지정 가능.

 

TestCalculator : 주 업무(타깃) 클래스 (AOP처리가 필요한 클래스)
LogXXXAdvice : 어드바이스 클래스 (핸들러 클래스 – 주 업무 처리 이전, 이후 등 부가적인 업무를 처리할 클래스)
PointCut(mappedNames) : 주 클래스의 함수 중에 내가 어드바이스를 적용을 한정(포인트 컷 할)시킬 함수를 지정 (특정 함수에만  국한지어 적용)
Advisor(nameAdvisor) : 포인트컷과 어드바이스를 합친 어드바이저를 생성. 어드바이스 대신 어드바이저 사용가능.
Proxy : 타깃 클래스를 지정하고, 어드바이저(또는 어드바이스)를 적용할 것(interceptorNames)을 지정한 후, 대리 클래스 생성. ProxyFactoryBean을 통한 동적 프록시 생성.

참고로 어드바이저에는 여러 개의 어드바이스와 포인트 컷이 추가 될 수 있기 때문에 개별적으로 등록 시 어떤 어드바이스(부가기능)에 대해 어떤 포인트컷(메소드 선정)을 적용 할지 애매해진다. 그래서 어드바이스와 포인트 컷을 묶은 오브젝트를 어드바이저라고 부른다.

 

어드바이저 = 포인트컷(메소드 선정 알고리즘) + 어드바이스(부가 기능, 애스펙트의 한가지 형태)

 

  

<bean id="nameAdvisorclass="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">

    <property name="pointcutref="namePointcut"></property>

    <proporty name="mappedNames">

         <list>

              <value>add</value>

              <value>substract</value>

          </list>

     </property>

     <property name="advice" ref="logPrintAroundAdvice"></property>

</bean>

 

 

// JAVA 정규식 표현을 활용한 포인트 컷 패턴 설정.  

<bean id="nameAdvisorclass="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

    <property name="pointcutref="namePointcut"></property>

    <proporty name="patterns">

         <list>

              <value>.*a.*</value>

              <value>.*b</value>

          </list>

     </property>

     <property name="advice" ref="logPrintAroundAdvice"></property>

</bean>

 

 

* mappedName : 적용시킬 함수를 지정.
* mappedNames : 적용시킬 함수들을 지정.

예제 클래스에는 2개의 함수(add, substract) 밖에 없지만, 함수가 상당히 많은 경우, 패턴을 사용하는 것이 더 효율적일 수 있다. 그룹핑을 통해 어떤 어드바이스(Advice)만 실행되게 할 것인지도 한정할 수도 있다.

[정규식 특수 문자 의미]

. : 어떠한 문자든 상관없다. (한개의 문자가 옴)
* : 임의의 문자가 0개 이상 올 수 있다. (한개 이상의 문자가 옴)

 

 

* 위빙, 조인포인트, 포인트컷을 다시 한 번 상기시키자면

위빙 : 크로스 컷팅 되어 있는 관심사(Concern)를 다시 결합하는 것.
조인포인트 : 위빙하는 함수들이 있는 곳 지점, 시점 (스프링에서는 메소드만을 조인 포인트로 사용함.)

포인트 컷 : 부가 기능 적용 대상 메서드 선정 방법. 내가 원하는 메서드만 골라서 조인포인트 역할을 하도록 함. (단, 포인트 컷을 통해 특정 타깃 함수와 어드바이스를 매 하나씩 연결해야 한다면 설정 파일의 용량도 커지고, 함수가 많아지게 되면 관리도 어려워질 수 있음.)

 

 * 스프링에서의  조인 포인트 특징!
  - 분리하고 도킹하는 것이 자유로움.
  - 조인포인트를 쓸 수 있는 것이 Method에 한정.

 



출처: http://ooz.co.kr/216?category=818548 [이러쿵저러쿵]

728x90
반응형

'Web Programming > spring' 카테고리의 다른 글

Spring MVC Model2  (0) 2018.08.29
Spring 빈 후처리기  (0) 2018.08.29
Spring Advice  (0) 2018.08.29
Spring 디자인 패턴  (0) 2018.08.29
Spring 동적 Proxy  (0) 2018.08.29
728x90
반응형

스프링에서의 AOP (Aspect-Oriented Programming)

 

1) 개념

 

* 스프링은 4가지 형태의 핸들러(어드바이스, Advice)를 제공.
* 스프링에서는 핸들러를 어드바이스(Advice)라는 개념으로 사용.

 

 

 

 

 * JAVA 레벨이 아닌 XML을 통해 객체를 생성하고, 인젝션(Injection)함
 

 

[코드 간략 설명] 

 - 스프링 프레임워크에서 제공해주는 핸들러(어드바이스) : MethodeInterceptor

 - MethodeInterceptor : Around Advice 사용할 때, 구현해야 하는 인터페이스.

 - MethodeInterceptor 는 InvocationHandler 와 동일한 기능을 수행하나 차이점은 InvocationHandler 의 경우에는 프록시할 타깃 객체를 InvocationHandler 인터페이스 구현 객체가 알고 있어야 하나, MethodInterceptor 의 경우에는 MethodInvocation 객체가 타깃 객체와 호출된 메소드 정보를 모두 알고 있음.
 - 인터페이스(Interface)는 스프링 프레임워크에서 알아서 감지(Auto Detecting)하여 연결해줌. 그러므로 위의 Proxy Bean 객체 XML 코드에서 aoptest.Calculator 부분(파란색)은 제거해도 무방함.
 - 스프링 프레임워크는  Invoke 대신 proceed 메서드를 호출하여 주 업무 프로세스를 호출함. / 또한 스프링 프레임워크는 핸들러(Handler) 대신 어드바이스(Advice)를 생성.

 

* 어드바이스(Advice) 란?

 - 타깃 오브젝트(Target Object)에 적용할 부가 기능을 담은 오브젝트

 - 메인 업무에 보조적으로 추가될 보조 업무.

 

 

[XML 레벨로 보조 업무 연결 코드가 이동하면서 JAVA 코드가 단순해짐]

 

 * 위의 X 표시된 코드는 주석처리하고 AOP를 활용하여 Advice 를 적용한 JAVA 코드

 * 객체의 생성과 조립을 메인 함수에서 코드에서 관리하였지만, 부가적인 파트(객체 생성과 의존성)는 XML 파일로 이동시킴.
 * XML 빈 객체의 이름 및 속성을 변경함으로써, 기존 코드에 바로 적용 가능.
 * 부가 코드와 주 코드를 분리하여 필요할 때 도킹(Docking)하여 사용 가능.
 * 위와 같은 동적 프록시(Dynamic Proxy)는 ApplicationContext의 getBean 메서드를 통해서 개체를 얻어옴.

 

 

2) 스프링 프레임워크에서의 4가지 형태의 Advice

 

 



출처: http://ooz.co.kr/213?category=818548 [이러쿵저러쿵]

728x90
반응형

'Web Programming > spring' 카테고리의 다른 글

Spring 빈 후처리기  (0) 2018.08.29
Spring 포인트컷 조인포인트  (0) 2018.08.29
Spring 디자인 패턴  (0) 2018.08.29
Spring 동적 Proxy  (0) 2018.08.29
Spring AOP Proxy  (0) 2018.08.29
728x90
반응형

7) 패턴(Patterns)

 

패턴은 말 그대로 어떤 일정한 형태나 양식 또는 유형을 뜻함.

패턴이라는 개념은 스프링 프레임워크에 한정된 것이 아니라 개발 디자인(Development Design)에 대해 사용되는 일반적인 개념 중의 하나.

 

개발 디자인 패턴에는 다음의 2가지가 존재.

 

 * 데코레이터 패턴 :

 타깃의 코드에 손 대지 않고, 클라이언트가 호출하는 방법도 변경하지 않은 채로 새로운 기능을 추가할 때 유용한 방법. 
 핵심 코드에 부가적인 기능을 추가하기 위해서 런타임시 다이나믹하게 추가되는 프록시를 사용. 즉 동일한 인터페이스를 구현한 여러 개의 객체를 사용하는 것.
 주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴. 기능 확장이 필요할 때 서브클래싱(Subclassing) 대신 쓸 수 있는 유연한 대안이 될 수 있음.
 동적으로 객체의 추가적인 기능들을 가진 객체를 덧붙여 꾸밈.


 

 * 프록시 패턴 :

 타깃의 기능 자체에는 관여하지 않으면서 접근하는 방법을 제어해주는 프록시를 이용하는 방법.

위의 2가지 패턴의 차이점은 프록시의 경우는 실제 실행될 타깃을 확장하거나 기능을 추가하는 것이 아니라, 단지 타깃에 접근하는 방법 자체를 프록시를 통하여 가능하게 하는 것이고, 데코레이터는 실행 타깃의 확장을 의미함.

 

 

데코레이터 패턴에 대한 예제 코드 : 출처 - 위키피디아 (http://en.wikipedia.org/wiki/Decorator_pattern)

 

// Windows Interface & Simple Window Class

interface Window {

    public void draw();   // draws the Window

    // returns a description of the Window

    public String getDescription();   

}

class SimpleWindow implements Window {

    public void draw() {

        // draw window

    }

    public String getDescription() {

        return "simple window";

    }

}

 

// Decorators

abstract class WindowDecorator implements Window {

    protected Window decoratedWindow; // the Window being decorated

public WindowDecorator (Window decoratedWindow)  {

        this.decoratedWindow = decoratedWindow;

    }

}

class VerticalScrollBarDecorator extends WindowDecorator {

    public VerticalScrollBarDecorator (Window decoratedWindow) {

        super(decoratedWindow);

    }

    public void draw() {

        drawVerticalScrollBar();

        decoratedWindow.draw();

    }

    private void drawVerticalScrollBar() { // draw the vertical scrollbar

    }

    public String getDescription() {

        return decoratedWindow.getDescription() + ", including vertical scrollbars";

    }

}

class HorizontalScrollBarDecorator extends WindowDecorator {

    public HorizontalScrollBarDecorator (Window decoratedWindow) {

        super(decoratedWindow);

    }

    public void draw() {

        drawHorizontalScrollBar();

        decoratedWindow.draw();

    }

    private void drawHorizontalScrollBar() { // draw the horizontal scrollbar

    }

    public String getDescription() {

        return decoratedWindow.getDescription() + ", including horizontal scrollbars";

    }

}

 

 

// Decorator Pattern Example

public class DecoratedWindowTest {

    public static void main(String[] args) {

        // create a decorated Window with horizontal and vertical scrollbars

        Window decoratedWindow = new HorizontalScrollBarDecorator (

                new VerticalScrollBarDecorator(new SimpleWindow()));

 

        // print the Window's description

        System.out.println(decoratedWindow.getDescription());

    }

}

 

윈도우에 대한 인터페이스(interface Window)와 클래스(class SimpleWindow),

그리고 수직 스크롤바가 있는 윈도우 클래스(class VerticalScrollBarDecorator), <- 데코레이터

수평 스크롤바가 있는 윈도우 클래스(class HorizontalScrollBarDecorator) <- 데코레이터

 

기존 윈도우(Simple Window) 클래스를 감싸(implements) 스크롤이 추가된 클래스를 새로 재정의함.

(기존 클래스에 장식(데코레이팅)된 형태로 클래스를 정의)

 

데코레이터 패턴의 단점 :

- 잡다한 클래스가 많아지고, 겹겹이 에워싼 형태의 구조로 구조가 복잡해지면 객체의 정체를 알기 어려움.

 

데코레이터 패턴의 장점 :

- 기존 코드는 수정하지 않고, 확장 및 추가가 가능함.

 

 

패턴의 종류에는 옵저버 패턴, 데코레이터 패턴, 프록시 패턴, 팩토리 패턴, 싱글턴 패턴, 커맨드 패턴, 어댑터 패턴, 퍼사드 패턴, 템플릿 패턴 등등 다양하며 좀 더 자세히 알고 싶은 경우에 Head First Design Patterns (저자 에릭 프리먼) 서적을 추천함.

데코레이터 패턴 위키피디아 : http://en.wikipedia.org/wiki/Decorator_pattern



출처: http://ooz.co.kr/206?category=818548 [이러쿵저러쿵]

728x90
반응형

'Web Programming > spring' 카테고리의 다른 글

Spring 포인트컷 조인포인트  (0) 2018.08.29
Spring Advice  (0) 2018.08.29
Spring 동적 Proxy  (0) 2018.08.29
Spring AOP Proxy  (0) 2018.08.29
Spring AOP 란  (0) 2018.08.29
728x90
반응형

6) Dynamic Proxy (동적 프록시)

 

앞선 강좌에서 설명한 대로, AOP를 구현하기 위해 사용되는 프록시(Poxy) 개념은 상당히 유용한 면이 있지만, 단점도 가지고 있음.

 

* 프록시의 단점.

 

 1. 매 번 새로운 클래스 정의 필요.

  - 실제 프록시 클래스(Proxy Class)는 실제 구현 클래스와 동일한 형태를 가지고 있기 때문에 구현 클래스의 Interface를 모두 구현해야 함.

 

 2. 타깃의 인터페이스를 구현하고 위임하는 코드 작성의 번거로움.

  - 부가기능이 필요없는 메소드도 구현해서 타깃으로 위임하는 코드를 일일이 만들어줘야 함.

  - 복잡하진 않지만 인터페이스의 메소드가 많아지고 다양해지면 상당히 부담스러운 작업이 될 수 있음.

  - 타깃 인터페이스의 메소드가 추가되거나 변경될 때마다 함께 수정해줘야 한다는 부담도 있음.

 

 3. 부가기능 코드의 중복 가능성.

  - 프록시를 활용할 만한 부가기능, 접근제어 기능 등은 일반적으로 자주 활용되는 것들이 많기 때문에 다양한 타깃 클래스와 메소드에 중복되어 나타날 가능성이 높음. (특히 트랜잭션(Transaction) 처리는 데이터베이스를 사용하는 대부분의 로직에서 적용되어야 할 필요가 있음.)

  - 메서드가 많아지고 트랜잭션 적용의 비율이 높아지면 트랜잭션 기능을 제공하는 유사한 코드가 여러 메서드에 중복돼서 나타날 수 있음.

 

 

* 해결책은?

 - 다이내믹 프록시(Dynamic Prox)를 이용하는 것! (JDK Dynamic Proxy)

 

 - 다이내믹 프록시란?

    > 런타임 시 동적으로 만들어지는 오브젝트
    > 리플렉션 기능을 이용해서 프록시 생성 (java.lang.reflect)
    > 타깃 인터페이스와 동일한 형태로 생성.
    > 팩토리빈(FactoryBean)을 통해서 생성.

 

  @ 스프링의 빈은 기본적으로 클래스 이름(Class name)과 프로퍼티(Property)로 정의. 
  @ 스프링은 지정된 클래스 이름을 가지고 리플렉션을 이용해서 해당 클래스의 오브젝트를 생성.

 

참고로 스프링은 지정된 클래스 이름을 가지고 리플렉션을 이용하여 해당 클래스의 오브젝트를 생성함.

이 외에도 팩토리빈(FactoryBean)과 프록시 팩토리빈(Proxy FactoryBean)을 통해 오브젝트를 생성할 수 있음. 팩토리빈 이란 스프링을 대신해서 오브젝트의 생성 로직을 담당하도록 만들어진 특별한 빈을 뜻함.

 

[추가 정리]

데코레이터(Decorator) 또는 프록시 패턴(Proxy Pattern)을 적용하는 데에 따른 어려움은 부가기능을 구현할 클래스가 부가기능과 관계없는 메서드들도 인터페이스에 선언된 메서드라면 전부 구현해야 하는 번거로움과 부가기능과 관계된 메소드들에 구현되는 코드 중복을 들 수 있음.

 

 -> 이는 자바의 리플렉션(Reflection)에서 제공하는 다이내믹 프록시(Dynamic Proxy)를 활용하여 해결할 수 있음.

 

1. Proxy.newProxyInstance() 를 통한 프록시 생성.

2. Proxy.newProxyInstance() 를 호출할 때 전달하는 InvocationHandler 인터페이스의 단일 메소드인 invoke()에 부가 기능을 단 한번 만 구현함으로써 코드 중복 해결.


=> 다이나믹 프록시 오브젝트는 클래스 파일 자체가 존재하지 않음.

=> 빈 오브젝트로 등록 불가.

=> 팩토리빈 인터페이스 활용. (팩토리빈 인터페이스를 구현한 클래스를 빈으로 등록)



출처: http://ooz.co.kr/205?category=818548 [이러쿵저러쿵]

728x90
반응형

'Web Programming > spring' 카테고리의 다른 글

Spring Advice  (0) 2018.08.29
Spring 디자인 패턴  (0) 2018.08.29
Spring AOP Proxy  (0) 2018.08.29
Spring AOP 란  (0) 2018.08.29
Spring IOC 컨테이너 란  (0) 2018.08.29
728x90
반응형


import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;


public class Test {
    public Test() {
    }

    public String getTestString(String test) {
        return test;
    }

    public String execute() throws SecurityException, NoSuchMethodException

                                                , InvocationTargetException, IllegalArgumentException,

                                                IllegalAccessException {
        Class[] types = {String.class};
        Object[] args = {"박한나입니다."};

       

        //getTestString함수를 호출

        Method method = this.getClass().getMethod("getTestString", types);          

        String test = (String)method.invoke(this, args);
        
        return test;
    }

 

 

    /**
     * main
     *
     * @param args String[]
     */
    public static void main(String[] args) {

 

        try {
           Test test = new Test();
           System.out.println(test.execute());

        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }


    }


728x90
반응형

+ Recent posts