일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 생성자오버로드
- 메소드오버로딩
- 대덕인재개발원
- abstract
- 자바
- 제네릭
- 어윈 사용법
- 객체 비교
- 참조형변수
- 예외처리
- 추상메서드
- Java
- NestedFor
- 집합_SET
- 사용자예외클래스생성
- 자동차수리시스템
- oracle
- 인터페이스
- cursor문
- 한국건설관리시스템
- 예외미루기
- 컬렉션 타입
- 정수형타입
- 다형성
- 컬렉션프레임워크
- exception
- EnhancedFor
- GRANT VIEW
- 오라클
- 환경설정
- Today
- Total
거니의 velog
(5) 스프링 AOP 기능 본문
1. 관점 지향 프로그래밍의 등장
* 가끔 어떤 사이트가 해킹을 당해 피해를 입었다는 소식을 듣곤 한다. 그러다 보니 요즘은 웹 애플리케이션 개발 시 해킹에 대비한 보안 기능 구현은 필수가 되고 있다. 그리고 모든 웹 애플리케이션은 로깅 기능을 적용해 사용자의 접속 내역을 로그로 기록한다. 그 외 트랜잭션, 예외처리, 이메일 통보 기능은 모든 웹 애플리케이션에서 공통으로 사용하는 기능이다. 따라서 웹 애플리케이션에 주기능을 추가할 때마다 앞에서 언급한 공통 기능도 일일이 구현해 주어야 한다. 하지만 이는 결국 배보다 배꼽이 더 큰 결과를 초래하게 된다. 스프링에서는 이런 문제를 관점 지향 프로그래밍(AOP, Aspect Oriented Programming)으로 해결할 수 있다.
* 예를 들어 살펴보자. 다음 코드는 회원 관련 기능을 수행하는 Service 클래스이다.
package com.spring.ex03;
import java.util.Date;
import java.util.List;
import org.apache.commons.logging.Log;
import org.omg.IOP.TransactionService;
public class MemberService {
MemberDAO memberDAO;
Log logger;
TransactionService transactionService;
SecurityService securityService;
NotificationPolicy notiPolicy;
EmailService emailService;
public void upgradeLevel(List<MemberVO> memberList, int newLevel) {
if(logger.isDebug()) { // 로깅 기능
logger.debug("회원 등급 ", memberList, newLevel);
}
if(!securityService.checkAuthorityForModify(MemberVO)) {
if(!logger.isWarnEnabled()) {
logger.warn("보안 등급 위반:회원 정보 수정 권한 없음", new Date());
}
throw new SecurityException(MemberVO);
} // 보안 기능
Transaction tx;
try {
tx = transactionService.beginTransaction(); // 트랜잭션 기능
for(MemberVO member : memberList) {
member.changeLevel(newLevel);
memberDAO.updateMember(member);
} // 회원 등급 기능(주기능)
tx = transactionService.endTransaction(); // 트랜잭션 기능
} catch (Exception e) {
e.printStackTrace();
}
...
}
}
* 여기서 upgradeLevel() 메서드는 회원 등급을 관리하는 메서드로, 실제 회원 등급을 관리하는 부분은 for문을 이용해 수행한다. 그러나 회원 등급 기능을 구현하더라도 로깅 기능, 보안 기능, 트랜잭션 같은 보조 기능을 같이 구현해 주어야 한다.
* 이런 상황에서 회원 정보나 등급이 수정되면 해당 회원에게 수정된 사항을 이메일로 보내는 기능을 추가해 달라고 해야 한다. 그러면 upgradeLevel() 메서드에 직접 코드를 작성해서 추가해야 한다.
* 그런데 규모가 있는 웹 애플리케이션일 경우 클래스의 메서드마다 이런 작업을 일일이 수작업으로 하기에는 시간도 많이 걸리고 소스 코드도 복잡해 진다. 즉, 유지관리에 문제가 생길 수 있다. 그래서 필요한 것이 관점 지향 프로그래밍(Aspect Oriented Programming, AOP)이다.
* AOP는 메서드 안의 주기능과 보조 기능을 분리한 후 선택적으로 메서드에 적용해서 사용한다는 개념이다. AOP를 사용하면 전체 코드에 흩어져 있는 보조 기능을 하나의 장소에 모아서 관리할 수 있다. 또 보조 기능을 자신이 원하는 주기능에 선택적으로 적용할 수 있어 코드가 단순해지고 가독성도 향상된다.
* 따라서 앞의 회원 등급 관리 기능처럼 메서드마다 보조 기능을 일일이 구현할 필요 없이 한 번의 설정으로 메서드마다 보조 기능을 적용할 수 있다. 일단 보조 기능을 만들어 놓고 원할 때 설정 한번으로 부품을 가져다 쓰듯이 사용하면 되는 것이다. 당연히 코드가 단순해지고 가독성도 좋아질 수밖에 없다.
* 다음 그림은 AOP를 이용해 쇼핑몰의 주기능에 대해 여러 가지 보조 기능들이 적용된 쇼핑몰 구조를 나타낸 것이다. 각각의 보조 기능을 미리 만들어 놓고 설정만 해주면 각각의 주기능을 수행하는 메서드나 클래스에 선택적으로 보조 기능이 적용된다.
2. 스프링에서 AOP 기능 사용하기
* 이번에는 실제 스프링에서 제공하는 AOP 기능을 사용해 보자.
* 다음 표는 AOP와 관련된 여러 가지 용어에 대한 설명이다. 입문자에게는 용어의 개념을 이해하기가 다소 어려울 수 있지만 실습을 하고 나면 확실히 이해할 수 있을 것이다. 따라서 지금은 용어 자체가 잘 이해되지 않더라도 그냥 읽고 넘어가도 된다.
<여러 가지 AOP 관련 용어>
용어 | 설명 |
aspect | 구현하고자 하는 보조 기능을 의미한다. |
advice | aspect의 실제 구현체(클래스)를 의미한다. 메서드 호출을 기준으로 여러 지점에서 실행된다. |
joinpoint | advice를 적용하는 지점을 의미한다. 스프링은 method 결합점만 제공한다. |
pointcut | advice가 적용되는 대상을 지정한다. 패키지이름/클래스이름/메서드이름을 정규식으로 지정하여 사용한다. |
target | advice가 적용되는 클래스를 의미한다. |
weaving | advice를 주기능에 적용하는 것을 의미한다. |
* 스프링 프레임워크에서 AOP 기능을 구현하는 방법으로는 스프링 프레임워크에서 제공하는 AOP 관련 API를 이용하는 방법과 애너테이션을 이용하는 방법이 있다.
- 스프링 프레임워크에서 제공하는 API를 이용하는 방법
- @Aspect 애너테이션을 이용하는 방법
(1) 스프링 API를 이용한 AOP 기능 구현 과정
* 두 가지 방법 중 먼저 스프링 프레임워크에서 제공하는 API를 구현한 AOP 기능을 살펴보자.
* AOP 기능을 구현하는 과정은 다음과 같다.
(1) 타깃(target) 클래스를 지정한다.
(2) 어드바이스(Advice) 클래스를 지정한다.
(3) 설정 파일에서 포인트컷(Pointcut)을 설정한다.
(4) 설정 파일에서 어드바이스와 포인트컷을 결합하는 어드바이저를 설정한다.
(5) 설정 파일에서 스프링의 ProxyFactoryBean 클래스를 이용해 타깃에 어드바이스를 설정한다.
(6) getBean() 메서드로 빈 객체에 접근해 사용한다.
* 그리고 스프링 프레임워크에서 제공하는 메서드를 호출했을 때 AOP 기능을 수행하는 어드바이스(Advice) 인터페이스들의 추상 메서드 기능을 다음 표로 정리했으니 참고하기 바란다.
<스프링 API에서 제공하는 여러 가지 Advice 인터페이스>
인터페이스 | 추상 메서드 | 설명 |
MethodBeforeAdvice | void before(Method method, Object[] args, Object target) throws Throwable |
해당 메서드가 실행되기 전 실행 |
- Method method : 대상 객체에서 실행된 메서드를 나타내는 메서드 객체 - Object[] args : 메서드 인자 목록 - Object target : 대상 객체 |
||
AfterReturningAdvice | void afterReturning( Object returnvalue, Method method, Object[] args, Object target) throws Throwable |
해당 메서드가 실행된 후 실행 |
- Object returnValue : 대상 객체의 메서드가 반환하는 값 - Method method : 대상 객체에서 실행된 메서드를 나타내는 메서드 객체 - Object[] args : 메서드 인자 목록 - Object target : 대상 객체 |
||
ThrowsAdvice | void afterThrowing( Method method, Object[] args, Object target, Exception ex) |
해당 메서드에서 예외 발생 시 실행 |
- Method method : 대상 객체에서 메서드를 나타내는 메서드 객체 - Object[] args : 메서드 인자 목록 - Object target : 대상 객체 - Exception ex : 발생한 예외 타입 |
||
MethodInterceptor | Object invoke( MethodInvocation invocation) throws Throwable |
해당 메서드의 실행 전/후와 예외 발생 시 실행 |
- MethodInvocation invocation : 대상 객체의 모든 정보를 담고 있는 객체(호출된 메서드, 인자 등) |
* 이 중 인터페이스 MethodInterceptor는 invoke() 메서드를 이용해 다른 세 가지 인터페이스의 기능을 동시에 수행할 수 있다. 다음에 이를 직접 실습해 보자.
(2) 스프링 API를 이용한 AOP 기능 실습
1. 새 프로젝트 pro20을 만들고 lib 폴더를 만들어 라이브러리 클래스 패스를 설정한다. 그리고 AOP 설정 파일인 AOPTest.xml을 src 패키지에 생성한다.
2. AOP를 설정하는 AOPTest.xml을 다음과 같이 작성한다. <bean> 태그를 이용해 타깃 빈과 어드바이스 빈을 생성한 후 스프링의 ProxyFactoryBean 클래스 빈 생성 시 <property> 태그를 이용해 타깃 빈과 어드바이스 빈을 엮어준다. 그리고 사용할 어드바이스가 여러 개이면 <value> 태그로 추가하면 된다.
<?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" /> <!-- 로그 기능을 하는 어드바이스 빈을 지정한다. -->
<bean id="proxyCal" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 스프링 프레임워크에서 제공하는 ProxyFactoryBean을 이용해 타깃과 어드바이스를 엮어준다. -->
<property name="target" ref="calcTarget"/> <!-- 타깃 빈을 calcTarget 빈으로 지정한다. -->
<property name="interceptorNames">
<list>
<value>logAdvice</value>
</list>
</property> <!-- 스프링의 ProxyFactoryBean의 interceptorNames 속성에 logAdvice를 어드바이스 빈으로 설정하여 타깃 클래스의 메서드 호출 시 logAdvice를 실행한다. -->
</bean>
</beans>
3. 이번에는 타깃 클래스인 Calculator 클래스를 작성한다.
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);
}
}
4. 어드바이스 클래스인 LoggingAdvice를 다음과 같이 작성한다. 먼저 인터페이스 MethodInterceptor를 구현하고 invocation.proceed() 메서드를 기준으로 메서드 호출 전과 후를 분리하여 로그 메시지를 출력한다. proceed() 메서드 호출 전 구문은 타깃 메서드 호출 전에 실행하는 기능이고, 호출 후 구문은 타깃 메서드 호출 후에 실행하는 기능이다.
package com.spring.ex01;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class LoggingAdvice implements MethodInterceptor { // 인터페이스 MethodInterceptor를 구현해 어드바이스 클래스를 만든다.
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("[메서드 호출 전 : LogginAdvice");
System.out.println(invocation.getMethod() + "메서드 호출 전"); // 메서드 호출 전에 수행하는 구문이다.
Object object = invocation.proceed(); // invocation을 이용해 메서드를 호출한다.
System.out.println("[메서드 호출 후 : loggingAdvice");
System.out.println(invocation.getMethod() + "메서드 호출 후"); // 메서드 호출 후에 수행하는 구문이다.
return object;
}
}
5. 실행 클래스인 CalcTest를 다음과 같이 작성한다. AOPTest.xml을 읽어 들여 빈을 생성한 후 타깃 클래스의 메서드를 호출하면 결과 출력 전후에 어드바이스에서 설정한 로그가 출력된다.
package com.spring.ex01;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CalcTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("AOPTest.xml"); // AOPTest.xml를 읽어 들여 빈을 생성한다.
Calculator cal = (Calculator) context.getBean("proxyCal"); // id가 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); // 메서드 호출 전후에 어드바이스 빈이 적용된다.
}
}
6. main() 메서드가 있는 실행 클래스(CalcTest.java)가 보이는 상태에서 실행 버튼을 클릭해 실행한다.
* 이상으로 스프링 프레임워크에서 제공하는 API를 이용해 AOP 기능을 알아보았다. 지금 실습한 AOP 기능은 타깃 클래스인 Calculator의 모든 메서드에 적용된다. 실제 스프링에서는 특정 패키지 이름이나 특정 클래스 이름 또는 특정 메서드 이름에만 AOP 기능을 적용할 수도 있다.
* 이런 세부 기능은 다른 기능을 학습하면서 조금씩 적용해 보도록 하자. 지금은 AOP의 개념을 확실히 이해해 두길 바란다.
[퍼스펙티브 변경하기]
* 다음부터 다시 웹 프로젝트에서 실습하므로 사용의 편리성을 위해 이클립스 오른쪽 상단 아이콘들 중 Java EE 아이콘을 클릭해 Java EE Perspective로 변경하자.
'Java_Spring Framework part1' 카테고리의 다른 글
(7) 스프링 MVC 기능 2 (0) | 2023.10.04 |
---|---|
(6) 스프링 MVC 기능 1 (0) | 2023.10.03 |
(4) 스프링 의존성 주입과 제어 역전 기능 3 (0) | 2023.10.03 |
(3) 스프링 의존성 주입과 제어 역전 기능 2 (0) | 2023.10.02 |
(2) 스프링 의존성 주입과 제어 역전 기능 1 (0) | 2023.09.27 |