AOP와 스프링
□ AOP (Aspect Oriented Programming) 소개
■ 공통으로 사용되는 모듈에 대한 복잡한 의존 관계
□ AOP (Aspect Oriented Programming) 소개
■ 공통으로 사용되는 모듈에 대한 복잡한 의존 관계
- 공통으로 사용되는 기능이 많아질 수록, 그리고 공통 모듈을 사용하는 클래스가 많아질 수록 의존 관계는 점점 복잡해짐.
- 공통 모듈을 사용하는 코드가 여러 곳에서 중복되는 문제도 발생.
■ 정의
- 공통의 관심 사항을 적용해서 발생하는 의존 관계의 복잡성과 코드 중복을 해소해 주는 프로그래밍 기법.
■ 특징
- 각 클래스에서 공통 관심 사항을 구현한 모듈에 대한 의존 관계를 갖기보다는, Aspect를 이용하여 핵심 로직을 구현한 각 클래스에
- 공통 모듈을 사용하는 코드가 여러 곳에서 중복되는 문제도 발생.
■ 정의
- 공통의 관심 사항을 적용해서 발생하는 의존 관계의 복잡성과 코드 중복을 해소해 주는 프로그래밍 기법.
■ 특징
- 각 클래스에서 공통 관심 사항을 구현한 모듈에 대한 의존 관계를 갖기보다는, Aspect를 이용하여 핵심 로직을 구현한 각 클래스에
공통 기능을 적용.
- 핵심로직구현 클래스에서 공통 모듈을 사용하는 코드를 포함하지 않음.
■ Aspect가 공통 모듈을 로직을 구현할 클래스에 적용
- 핵심로직구현 클래스에서 공통 모듈을 사용하는 코드를 포함하지 않음.
■ Aspect가 공통 모듈을 로직을 구현할 클래스에 적용
- 핵심 로직을 구현한 클래스를 실행하기 전후에 Aspect를 적용.
- 핵심 로직을 수행하면 그에 앞서 공통 모듈을 실행하거나 또는 로직 수행 이후에 공통 모듈을 수행하는 방식으로 공통 모듈을 적용.
■ 장점
- Aspect가 핵심 로직 구현 클래스에 의존하지 않음.
- 하나의 Aspect를 개발하면 Aspect를 수정할 필요 없이 여러 클래스에 적용할 수 있음.
□ 스프링에서의 AOP
■ 장점
- 간단한 설정만으로 공통 기능을 여러 클래스에 적용할 수 있음.
- 일반적인 웹 어플리케이션을 구현하는데에 필요한 수준으로 AOP를 지원.
- 핵심 로직 코드의 수정 없이 웹 어플리케이션에 보안, 로깅, 트랜잭션과 같은 공통 관심 사항을 AOP를 이용하여 간단하게 적용.
■ 예제
- 메서드의 실행 시간을 출력해주는 코드.
○ AOP를 적용하지 않은 방식
- 요구가 변경될 때마다 많은 코드를 변경해야 함.
○ AOP를 적용한 방식
● LoggingAspect.java
- 공통 관심 사항을 구현한 POJO 클래스.
● commonsConcern.xml
- Aspect를 어떤 클래스의 어떤 메서드에 적용할지를 설정.
- XML 스키마 확장(aop 네임스페이스)를 사용하여 AOP 설정.
- 이름이 Service로 끝나는 인터페이스를 구현한 모든 클래스의 모든 메서드에 LoggingAspect가 적용.
● MainForAop.java
- 실제로 Aspect가 적용되었는지 테스트.
● 결과
● LoggingAspect의 실행 순서
- Aspect가 핵심 로직 구현 클래스에 의존하지 않음.
- 하나의 Aspect를 개발하면 Aspect를 수정할 필요 없이 여러 클래스에 적용할 수 있음.
□ 스프링에서의 AOP
■ 장점
- 간단한 설정만으로 공통 기능을 여러 클래스에 적용할 수 있음.
- 일반적인 웹 어플리케이션을 구현하는데에 필요한 수준으로 AOP를 지원.
- 핵심 로직 코드의 수정 없이 웹 어플리케이션에 보안, 로깅, 트랜잭션과 같은 공통 관심 사항을 AOP를 이용하여 간단하게 적용.
■ 예제
- 메서드의 실행 시간을 출력해주는 코드.
○ AOP를 적용하지 않은 방식
public void someMethod() { StopWatch stopWatch = new StopWatch(); stopWatch.start(); executeLogic(); stopWatch.stop(); long executionTime = stopWatch.getTotalTimeMillis(); } |
○ AOP를 적용한 방식
● LoggingAspect.java
- 공통 관심 사항을 구현한 POJO 클래스.
package kame.spring.chap01; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.aspectj.lang.ProceedingJoinPoint; import org.springframework.util.StopWatch; // 오직 ProceedingJoinPoint에만 의존. public class LoggingAspect { private Log log = LogFactory.getLog(getClass()); // Aspect가 적용되는 메서드의 실행 시간을 구한 뒤 Log를 통해 출력. // ProceedingJoinPoint 객체는 Aspect가 적용되는 객체 및 메서드에 대한 정보를 담고 있음. public Object logging(ProceedingJoinPoint joinPoint) throws Throwable { log.info("기록 시작"); StopWatch stopWatch = new StopWatch(); try { stopWatch.start(); // 핵심 로직 실행. Object retValue = joinPoint.proceed(); return retValue; } catch(Throwable e) { throw e; } finally { stopWatch.stop(); log.info("기록 종료"); log.info(joinPoint.getSignature().getName() + "메서드 실행 시간 : " + stopWatch.getTotalTimeMillis()); } } } |
- Aspect를 어떤 클래스의 어떤 메서드에 적용할지를 설정.
<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <bean id="logging" class="kame.spring.chap01.LoggingAspect" /> <aop:config> <aop:pointcut id="servicePointcut" expression="execution(* *..Service.*(..))" /> <aop:aspect id="loggingAspect" ref="logging"> <aop:around pointcut-ref="servicePointcut" method="logging" /> </aop:aspect> </aop:config> </beans> |
- 이름이 Service로 끝나는 인터페이스를 구현한 모든 클래스의 모든 메서드에 LoggingAspect가 적용.
● MainForAop.java
- 실제로 Aspect가 적용되었는지 테스트.
package kame.spring.chap01; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainForAop { public static void main(String[] args) { String[] configLocations = new String[] {"applicationContext.xml", "commonConcern.xml"}; ApplicationContext context = new ClassPathXmlApplicationContext(configLocations); WriteArticleService articleService = (WriteArticleService)context.getBean("writeArticleService"); articleService.write(new Article()); } } |
INFO LoggingAspect [2010-05-06 20:54:02,953] - 기록 시작 WriteArticleServiceImpl.write() 메서드 실행 MysqlArticleDao.insert() 실행 INFO LoggingAspect [2010-05-06 20:54:02,956] - 기록 종료 INFO LoggingAspect [2010-05-06 20:54:02,957] - write 메서드 실행 시간 : 0 |
- 스프링은 프록시를 이용하여 AOP를 구현.
- MainForAop 클래스가 사용하는 객체는 WriteArticleServiceImpl 객체가 아니라 스프링이 런타임에 생성한 프록시 객체.
(프록시가 내부적으로 다시 LoggingAspect의 logging() 메서드를 호출하여 공통 관심 사항이 실제 WriteArticleServiceImpl 객체
- MainForAop 클래스가 사용하는 객체는 WriteArticleServiceImpl 객체가 아니라 스프링이 런타임에 생성한 프록시 객체.
(프록시가 내부적으로 다시 LoggingAspect의 logging() 메서드를 호출하여 공통 관심 사항이 실제 WriteArticleServiceImpl 객체
에 적용.)
'프레임워크 > Spring' 카테고리의 다른 글
[Spring] sitemesh 설정하기 (0) | 2015.12.22 |
---|---|
[Java] Spring 동작 순서 (0) | 2015.12.22 |
[Spring 3 - @MVC] @Controller #2 - 파라미터와 리턴 타입의 종류 (0) | 2015.12.22 |
뷰에 모델(Model) 전달 (0) | 2015.12.22 |
스프링이란 무엇인가? (0) | 2015.12.22 |