Hyokyun Yim bio photo

Hyokyun Yim

Koreatech Computer Science Engineering undergraduate 4 grade.

Facebook Github

AOP(Aspect Oriented Programming) 에 대해 알아보자.

Overview

AOP(Aspect Oriented Programming)

AOP의 필요성

  • 프로그래밍을 하다보면 중간에 공통적인 기능이 많이 발생한다.
  • 대표적인 공통기능 Log , Security 등등 ..
  • 이러한 공통기능을 메인코드에 노가다로 하나하나 쳐주는것보다 class로 만들어 Inheritance(상속) 하면 효과적으로 구현이 가능하다.
  • 하지만 단점이 존재한다!! 바로 상속으로 인한 클래스간의 결합도가 높아지므로 코드의 재사용성이 떨어진다. -> 좋은 방법이 아님
  • 이를 해결하기 위해 나온 기법이 AOP 이다.

AOP란 그럼 무엇인가?

  • Business Logic 코드(핵심기능코드)와 공통기능 코드를 완벽하게 분리시킨다.
  • AOP는 OOP를 대체하기 위해서 등장한 기술이 아니다.
  • 오히려 AOP는 OOP를 더욱 OOP 답게 만들어 주는것이다.
  • 즉 객체지향을 좀더 객체지향답게 만들어 주는것이다.

AOP 구현

  • 기능을 핵심 비즈니스 로직과, 공통 모듈로 구분하고
  • 공통모듈을 코드 외부에서 이 모듈을 비즈니스로직에 삽입하는 것이 AOP 이다.
  • 위 그림은 Proxy 타입의 AOP 구현 방식이다.
  • proxy class 만 호출하면 proxy clss가 메인로직을 호출하면서 cross-cutting concern 기능을 필요에 따라 삽입시켜줌
  • Proxy class를 직접구현하는것은 매우어렵다. 다행히 스프링이 이를 만들어 제공해준다.

AOP 용어

  • Core Concern : 시스템 목적에 해당하는 주요 로직 부분을 AOP에서는 Core Concern 이라고 한다.
  • Cross-Cutting Concern : 부가기능(log,Security)코드를 AOP에서는 Cross-Cutting Concern 이라고 한다.
  • Joinpoint: Cross-Cutting concern 이 어디에 붙을 것인가를 결정 -> 후보장소 지정
  • Pointcut : 결정적으로 공통기능을 어디에 박을건지 선택하는 기능 -> Pointcut이 어떤 메서드에 어떤 Joinpoint를 집어넣을 것인가를 수행 , 즉 Joinpoint를 선정하는 기능
  • Advice : 각 Joinpoint에 삽입되어져 동작할 수 있는 코드, 즉 실제 공통기능이 수행되는 method 로 Pointcut에 의해서 결정된 Joinpoint에서 호출되어 사용됨
  • Weavng : Pointcut에 의해서 결정된 Jointpoint에 지정된 Advice를 삽입하는 과정을 Weaving 이라고 하며, Spring이 이 과정을 알아서 처리해줌

Spring에서 AOP 구현

  • Spring은 Core Concern중 method에만 공통 기능을 적용 할 수 있다.
  • Spring에서는 Proxy를 이용해 AOP 구현한다.
//main

package exam_03_01_AOP_Inheritance;

import org.springframework.context.support.GenericXmlApplicationContext;

import  exam_03_01_AOP_Inheritance.BookShelf;

public class Main {

	public static void main(String[] args) {

		String config = "classpath:applicationCtx.xml";
		
		//Spring Container 생성
		GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
		
		//xml문서 불러들이는 부분
		ctx.load(config);
		
		//load 메서드로 xml을 불러올 경우 반드시 refresh() 메서드를 수행해야함.
		//xml 적용
		ctx.refresh();
		
		//Bookshelf 클래스 타입인 shelf 객체를 끄집어냄
		BookShelf shelf = ctx.getBean("shelf",BookShelf.class);
		
		
		//어떤 메서드가 호출되는지를 화면에 출력하는 부가기능을 Core-Concern에 넣을거다.
		shelf.printShelfInfo();

		
		ctx.close();
	}

}


<?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:util="http://www.springframework.org/schema/util"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">

	
	
	<context:annotation-config />
	
	
	<!-- Spring 컨테이너에 의해 3개의 책 DTO가 생성. -->
	<!-- scope는 디폴트로 singleton, 싱글톤이란 객체가 딱 하나만 존재하는것 -->
	<bean id = "book1"
	 class = "exam_03_01_AOP_Inheritance.dto.BookDTO"
	 scope="singleton">
	 <!--  init-method는 초기화메서드를 추가로 지정 -->
	
		<property name ="btitle"> 
			<value> 젊은 베르테르의 슬픔</value>
		</property>
		
		<property name="bauthor">
			<value>괴테</value>
		</property>
	</bean>

	<bean id = "book2"
	 class = "exam_03_01_AOP_Inheritance.dto.BookDTO">
	
		<property name ="btitle"> 
			<value>개미</value>
		</property>
		
		
		<property name="bauthor">
			<value>베르나르 베르베르</value>
		</property>
	</bean>
	
	
		
	<bean id = "book3"
	 class = "exam_03_01_AOP_Inheritance.dto.BookDTO">
	
		<property name ="btitle"> 
			<value> 레미제라블</value>
		</property>
		
		
		<property name="bauthor">
			<value>빅토르 위고</value>
		</property>
	</bean>
	
	
	<!--  XML로 ArrayList , HashMap 만들기 -->
	<!--  NamesSpace 에서 util 체크 필요함  -->
	<!--  ArrayList 안에 BookDTo를 넣음 -->
	<util:list id = "bookList" value-type="exam_03_01_AOP_Inheritance.dto.BookDTO">
	
		<ref bean = "book1"></ref>
		<ref bean = "book2"></ref>
		<ref bean = "book3"></ref>
	
	</util:list>
	
	<util:map id ="bookMap" map-class="java.util.HashMap"
			  key-type="java.lang.Integer"
			  value-type="exam_03_01_AOP_Inheritance.dto.BookDTO">
			  <entry key="1" value-ref="book1"></entry>
			  <entry key="2" value-ref="book2"></entry>
			  <entry key="3" value-ref="book3"></entry>
	</util:map>
	
	
	
	<bean id ="shelf"
			class="exam_03_01_AOP_Inheritance.BookShelf">
			<constructor-arg value="만화책장" />
			
			<constructor-arg ref="bookList" />
			
			
			
			<!-- setter로 injection 하기 -->
			<property name="price" value = "10000"> </property>

			<property name="map">
				<ref bean="bookMap"/>
			</property>
	</bean>
	
	
	<bean id = "myCCC"
		  class="exam_03_01_AOP_Inheritance.MyCrossCuttingConern"/>
		  <aop:config>
		  
			  <!--  aspect는 pointcut과 advice를 합친것, 쉽게말해 aspect에서 pointcut과 advice를 설정하겠다. -->
			  <aop:aspect id="test" ref="myCCC">
			  
				<!--  내가만든 advice를 어디에 넣을건지 pointcut이 수행해줌 -->	  
			  	<aop:pointcut expression="within(exam_03_01_AOP_Inheritance.BookShelf)" id="MyALL"/>
			  
			  
			  <!-- joint cut -->
			  	<aop:before method = "printMethodName"
			  				pointcut-ref="MyALL"/>
			  	
			  
			  </aop:aspect>
		
		</aop:config>
</beans>




package exam_03_01_AOP_Inheritance;

import org.aspectj.lang.JoinPoint;

public class MyCrossCuttingConern {

	//advice 역할을 하는 method를 작성
	//부가적인 역할을 하는 코드를 작성
	public void printMethodName(JoinPoint joinpoint) {
		
		String name = joinpoint.getSignature().toShortString();
		System.out.println("호출된 method : " + name);
		
	}
	
}


package exam_03_01_AOP_Inheritance.dto;

public class BookDTO {

	private String btitle;
	private String bauthor;
	
	public BookDTO()
	{
	
	}
	
	
	//private 필드에 대한 getter/setter
	public String getBtitle() {
		return btitle;
	}

	public void setBtitle(String btitle) {
		this.btitle = btitle;
	}

	public String getBauthor() {
		return bauthor;
	}

	public void setBauthor(String bauthor) {
		this.bauthor = bauthor;
	}

}