AOP (관점지향형프로그래밍)
AOP
AOP (Aspect-Oriented-Programing) - 관점지향형프로그래밍
객체지향프로그래밍(OOP)은 많은 장점들을 가지고 있다.
코드를 작성하다 보면 중복되는 method나 field가 존재한다.
이러한 코드를 재사용성과 사용에 더 용이하게 하기 위해 bean class로 만든다.
하지만 bean class와 그 안에 구현되있는 method나 field는 bean class에 의존성을 가지게된다.
이러한 의존관계를 디커플링 관계로 만들어 주는 것, 제3자의 관점에서 본다는 개념, 핵심기능과 부가기능을 분리된 모듈로써 나눠주는 것을 AOP라 한다.
AOP 용어정리
타겟(Target) - Core Concerns(핵심기능)
- 핵심 기능을 담고 있는 모듈로, 타겟은 부가 기능을 부여할 대상이다. getBoards 혹은 getUsers를 하는 Service 들을 얘기합니다.
- 구현된 method와 field를 가지는 Bean Class이다.
어드바이스(Advice) - Cross-cutting-Concerns(부가기능)
- 어드바이스는 타겟에 제공할 부가기능을 담고 있는 모듈이다.
- Bean Class안에 구현되어 사용되는 method나 field
조인 포인트(JointPoint)
- 어드바이스가 적용될 수 있는 위치를 말함
- 즉, 타겟 객체가 구현한 인터페이스의 모든 메서드는 조인 포인트가 된다.
- Adivce를 적용해야 되는 부분(EX: 필드, 메소드/ 스프링에서는 메소드)
포인트컷(PointCut)
- 어드바이스를 적용할 타겟의 메서드를 선별하는 정규표현식
- 포인트컷 표현식은 execution으로 시작하고, 메서드의 Signature를 비교하는 방법을 주로 이용
- Joinpoint의 부분으로 실제로 Advice가 적용된 부분 -PointCut은
애스펙트(Aspect) - 일반적인 명칭
- 애스펙트는 AOP의 기본 모듈
- 애스펙트 = 어드바이스 + 포인트컷
- 애스펙트는 싱글턴 형태의 객체로 존재.
어드바이저(Advisior)
- 어드바이저 = 어드바이스 + 포인트컷
- 어드바이저는 Spring AOP에서만 사용되는 특별한 용어.
- 어드바이저와 에스펙트는 같은 개념을 갖는다.
위빙(Weaving)
- 위빙은 포인트컷에 의해서 결정된 타겟의 조인 포인트에 부가기능 (어드바이스)을 삽입하는 과정을 뜻한다.
- 위빙은 AOP가 핵심기능(타겟)의 코드에 영향을 주지 않으면서 필요한 부가기능(어드바이스)을 추가할 수 있도록 해주는 식심적인 처리과정
- 위빙은 Target와 Aspect를 합치는 과정이다. (Proxy)
- Target과 Advice를 연결하는 class(?)
프록시(Proxy)
- 개발을 분리하여 진행하면 그것을 연결하는 매개체
- Proxy : 내가 만든 것
- Dynamic-Proxy : 프레임워크에서 제공하는 것
AOP Spring 적용
AOP개념은 스프링에서 적용이 가능하며 스프링에서도 AOP를 지원하는 프레임워크를 제공해준다.
다른 프레임워크도 AOP를 지원하지만 스프링을 Bean/IoC를 지원하여 많은 선택을 받고 있다.
Spring에서 사용하는 방법은 2가지로 .xml과 @어노테이션을 이용하는 방법이 있다.
.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<!-- 생성 -->
<bean id="message" class="spring.service.aop.impl.MessageImpl"/>
<!-- 생성 -->
<bean id="testAdvice" class="spring.service.aop.advice.TestAdvice"/>
<!-- Pointcut -->
<!-- <bean id="testPointcut" class="org.springframework.aop.aspectj.AspectJExpressionPointcut">
<property name="expression" value="execution(public !void get*(..))"/>
</bean>
-->
<!-- 예외처리 -->
<bean id="testAdvisor" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
<property name="advice" ref="testAdvice"/>
<property name="expression" value="execution(* *.getMessage(..))"/>
<!-- <property name="pointcut" ref="testPointcut"/> -->
</bean>
<!-- AutoProxy (weaving) -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<!-- Proxy (weaving) -->
<!-- <bean id="message" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="spring.service.aop.Message"/> #Target이 인터페이스를 구현하기에 생략가능
<property name="interceptorNames" value="testAdvisor"/>
<property name="target" ref="messageTarget"/>
</bean>
-->
</beans>
Target
public class MessageTestAppUseSpringAOP02 {
///Main Method
public static void main(String[] args) throws Exception{
ApplicationContext context =
new ClassPathXmlApplicationContext("/config/messageservice.xml");
//==> IoC Container 로 부터 Look Up 한 인스턴스는 ProxyFactoryBean 객체가 생성해준
//==> Message interface 구현한 Dynamic Proxy
Message message = (Message)context.getBean("message");
//==> 실행방법
//::1. 인자 : String 전달
message.setMessage("Hello");
//::2. 인자 : null 전달 : ThrowsAdvice 동작확인
//message.setMessage(null);
System.out.println("\n\n+++++++++++++++++++++++++++++++++++++++++++++\n\n");
System.out.println("\n리턴 받은 메세지 : "+message.getMessage());
}
}//end of class
Aspect(Advice + pointcut)
public class TestAdvice implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice, MethodInterceptor {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("[before Log]" +getClass()+".before() start....");
System.out.println("[before Log] targetObject call Method : " +method);
if( args.length != 0) {
System.out.println("[before Log]targetObject Method 전달 agument : "+args[0]);
}
System.out.println("[before Log]" +getClass()+".before() end....");
}
public void afterReturning ( Object returnValue, Method method, Object[] args, Object target) throws Throwable{
System.out.println("[after LOG] "+getClass()+".afterReturning() start....");
System.out.println("[after LOG] targetObject call Method : "+method);
System.out.println("[after LOG] 타겟 객체의 호출후 return value : "+returnValue);
System.out.println("[after LOG] "+getClass()+".afterReturning() end....");
}
public Object invoke(MethodInvocation invocation) throws Throwable{
System.out.println("[Around before] "+getClass()+".invoke() start.....");
System.out.println("[Around before] targetObject call Method : "+invocation.getMethod());
if(invocation.getArguments().length != 0) {
System.out.println("[Around before] targetObject method 전달 argument : "
+ invocation.getArguments()[0]);
}
Object obj = invocation.proceed();
System.out.println("[Around after] 타겟 객체의 호출후 return value : "+obj);
System.out.println("[Around after] "+getClass()+".invoke() end.....");
return obj;
}
public void afterThrowing(Throwable throwable) {
System.out.println("[exception] "+getClass()+".afterThrowing() start.....");
System.out.println("[exception] Exception발생");
System.out.println("[exception] Exception Message : "+throwable.getMessage());
System.out.println("[exception] "+getClass()+".afterThrowing() end.....");
}
}
Leave a comment