만약 회원 등급 기능을 구현시 로깅, 보안, 트랜잭션 기능 등의 보조기능을 일일히 구현해야했다. 이 경우 규모가 있는 웹 애플리케이션의 경우 클래스의 메소드 마다 이런 작업을 수작업으로 하나하나 하기에는 시간도 많이 걸리고 소스 코드가 복잡해졌고, 즉 유지관리에 문제가 생길 수 있었다.
-> 이에 관점 지향 프로그래밍(Aspect oriented Programming, AOP) 를 이용해 주 기능과 보조 기능을 분리해서 메소드에 적용
각각의 보조 기능을 미리 만들어 놓고, 설정만 해주면 각각의 주기능을 수행하는 메소드나 클래스에 선택적으로 보조 기능이 적용
스프링 프레임 워크에서 AOP 기능을 구현하는 방법
1. 스프링 프레임워크에서 제공하는 API를 이용하는 방법
2. @Aspect 어노테이션을 이용하는 방법
관련 용어
API를 이용해 AOP 기능을 구현하는 과정은 이렇다.
새 프로젝트를 만들고 lib폴더를 생성해 라이브러리 클래스 패스부터 설정
AOP를 설정하는 xml 작성
-> 스프링의 ProxyFactoryBean의 interceptorNames 속성에 logAdvice를 어드바이스 빈으로 설정하여, 타깃 클래스의 메소드를 호출했을 시 logAdvice를 실행하도록 했다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<!-- 타깃 클래스 빈 지정 -->
<bean id="calcTarget" class="com.spring.ex01.Calculator" />
<!-- 로그 기능을 하는 어드바이스 빈 지정 -->
<bean id="logAdvice" class="com.spring.ex01.LoggingAdvice" />
<!-- 스프링 프레임워크에서 제공하는 ProxyFactoryBean을 이용해 타깃과 어드바이서 엮어주기 -->
<bean id="proxyCal"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 타깃 빈을 calcTarget 빈으로 지정 -->
<property name="target" ref="calcTarget"/>
<!-- 스프링의 프록시팩토리빈의 interceptorNames 속성에 -->
<property name="interceptorNames">
<!-- logAdvice를 어드바이스 빈으로 설정하여
타깃 클래스의 메소드 호출시 실행되게끔한다 -->
<list>
<value>logAdvice</value>
</list>
</property>
</bean>
</beans>
package com.spring.ex01;
public class Calculator {
public void add(int x, int y) {
int result=x+y;
System.out.println("결과:"+ result);
}
public void subtract(int x, int y) {
int result=x - y;
System.out.println("결과:"+ result);
}
public void multiply(int x, int y) {
int result=x * y;
System.out.println("결과:"+ result);
}
public void divide(int x, int y) {
int result=x / y;
System.out.println("결과:"+ result);
}
}
package com.spring.ex01;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
// 인터페이스MethodInterceptor을 구현해 어드바이스 클래스 만들기
public class LoggingAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
// 메소드 호출 전에 수행하는 구문으로
System.out.println("[메서드 호출 전 : LogginAdvice");
System.out.println(invocation.getMethod() + "메서드 호출 전");
// invocation을 이용해 메소드를 호출
Object object = invocation.proceed();
// 메소드 호출 후에 수행하는 구문
System.out.println("[메서드 호출 후 : loggingAdvice");
System.out.println(invocation.getMethod() + "메서드 호출 후");
return object;
}
}
package com.spring.ex01;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CalcTest {
public static void main(String[] args) {
// xml 불러오기
ApplicationContext context = new ClassPathXmlApplicationContext("AOPTest.xml");
// id가 proxyCal인 빈에 접근하기
Calculator cal = (Calculator) context.getBean("proxyCal");
// 메소드를 호출 전.후에 어드바이스 빈 적용
cal.add(100, 20);
System.out.println();
cal.subtract(100, 20);
System.out.println();
cal.multiply(100, 20);
System.out.println();
cal.divide(100, 20);
}
}