4. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
Spring AOP와 JDK 1.5 이상인 경우 어노테이션을 이용하여 어드바이스를 선언 할 수 있다.
@AspectJ 방식은 AspectJ 5 버전에서 소개되었으며, Spring은 2.0 버전부터 AspectJ 5 어노테이션을 지원한다
타겟 메소드에 어드바이스를 적용할 때는 AspectJ의 위빙 메커니즘이 아니라 자체 프록시 메커니즘을 이용한다.
@AspectJ를 사용하기 위해서 XML설정 파일에 <aop:aspectj-autoproxy/> 태그를 설정에 추가해야하며 클래스에@Aspect 어노테이
션을 추가하여 Aspect를 생성해야 한다.
@Pointcut으로 포인트컷을 정의하거나 @Before, @After등 충고를 정의할 때 AspectJ Expression을 정의하여 포인트컷을 지정할 수
있다.
3-8-1. @AspectJ Annotation을 이용한 AOP – Aspect 선언
자바설정을 이용하여 AspectJ를 사용하는 방법
@Configuration
@EnableAspectJAutoProxy
public class AppConfig { }
@Aspect
public class UserAspect {
// USerService+ : 인스턴스의 타입이 UserService 이거나 하위타입을 의미
@Before("execution(* x.y.z.UserService+.*(..))")
public void userAdvice(){
System.out.println("find my advice before your task.");
}
}
@Configuration
@EnableAspectJAutoProxy
public class AspectJAutoProxyConfig {
@Bean
public UserService userService(){
return new UserService();
}
@Bean
public UserAspect userAspect(){
return new UserAspect();
}
}
5. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-1. @AspectJ Annotation을 이용한 AOP – Aspect 선언
XML설정을 이용하여 AspectJ를 사용하는 방법<aop:aspectj-autoproxy/>
<aop:aspectj-autoproxy/>
<bean id="myAspect" class=“ojc.aop.OjcAspect“/>
package ojc.aop;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class OjcAspect { …… }
위 XML절정은 @Component를 이용하면 아래처럼 가능하다.
(XML에서 빈으로 정의하지 않을 경우 @Component, @Service, @Named와 같은 Annotation을
기술하면 된다.)
<aop:aspectj-autoproxy/>
package ojc.aop;
import org.aspectj.lang.annotation.Aspect;
@Aspect
@Component
public class OjcAspect { …… }
6. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-2. @AspectJ Annotation을 이용한 AOP – Advice 선언
Before advice : Before advice는 @Before 어노테이션을 사용한다.
After returning advice : After returing 충고는 정상적으로 메소드가 실행될 때 수행된다. After returning 충고는
@AfterReturing 어노테이션을 사용한다.
After throwing advice : After throwing 충고는 메소드가 수행 중 예외사항을 반환하고 종료하는 경우 수행된다.
After throwing 충고는 @AfterThrowing 어노테이션을 사용한다.
After (finally) advice : After (finally) 충고는 메소드 수행 후 무조건 수행된다. After (finally) 충고는 @After 어노테
이션을 사용한다.
Around advice : Around 충고는 메소드 수행 전후에 수행된다. Around 충고는 @Around 어노테이션을 사용한다.
@Around 충고는 대상 메소드의 반환 값(return value)를 변경 가능하지만, After returning 충고는 반환 값을 참조
가능하지만 변경할 수는 없다.
7. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-2. @AspectJ Annotation을 이용한 AOP – Advice 선언
사전충고(@Before)
@Before 어노테이션을 사용하며 포인트컷 메소드가 실행되기 전에 충고가 적용된다.
@Aspect
public class BeforeExample {
@Before(“x.y.MyClass.dataAccessOperation()")
public void doAccessCheck1() {
// ...
}
@Before("execution(* x.y.MyClass.*(..))")
public void doAccessCheck2() {
// ...
}
}
8. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-2. @AspectJ Annotation을 이용한 AOP – Advice 선언
예외충고(@AfterThrowing)
@AfterThrowing 어노테이션을 사용하며 포인트컷 메소드에서 예외가 발생할 때 충고
가 적용된다.
@Aspect
public class AfterThrowingExample {
@AfterThrowing(“x.y.MyClass.dataAccessOperation()")
public void doRecoveryActions1() {
// ...
}
// 리턴값을 retVal로 받음, throwing 속성의 값은 어드바이스 메소드의 파라미터와
이름이
// 같아야 하며 예외가 그 변수로 넘어온다.
@AfterThrowing(
pointcut=“x.y.MyClass.dataAccessOperation()",
throwing="ex")
public void doRecoveryActions2(DataAccessException ex) {
// ...
}
}
9. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-2. @AspectJ Annotation을 이용한 AOP – Advice 선언
사후충고(@After)
@After 어노테이션을 사용한다. 포인트컷 메소드가 실행된 후(정상종료 여부와 관계없
이) 충고가 적용된다.
@Aspect
public class AfterExample {
@After(“x.y.MyClass.dataAccessOperation()")
public void doReleaseLock() {
// ...
}
}
10. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-2. @AspectJ Annotation을 이용한 AOP – Advice 선언
주변충고(@Around)
@Around 어노테이션을 사용하며 포인트컷 메소드가 실행 되기 전, 리턴 된 후에 충고가 적용된다.
어드바이스 메소드의 첫번째 파라미터는 ProceedingJoiinPoint가 되어야 한다. proceed() 메소드를 통해 타겟 클래스의 원래 메소드
를 호출하고, proceed() 메소드를 호출하면서 Object[] 형태로 파라미터를 전달할 수도 있다. proceed() 메소드는 기술 안할수도 있고,
한번, 여러 번 호출할 수도 있으므로 원래 메소드에 대한 여러 형태의 제어가 가능하다. 물론 원래 메소드의 리턴 값을 바꿔치기 하는
것도 가능하다.
@Aspect
public class AroundExample {
@Around(“x.y.MyClass.businessService()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
// start stopwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}
}
11. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-2. @AspectJ Annotation을 이용한 AOP – Advice 선언
[참고] StopWatch 사용법
1.System 클래스의 currentTimeMillis() 메소드 이용
1970년 1월 1일 자정부터 현재까지 카운트된 시간을 ms(milliseconds) 단위로 표시한다.
long startTime = System.currentTimeMillis();
// 특정 로직이나 메소드 호출
long elapsedTime = System.currentTimeMillis() - startTime;
System.out.println(elapsedTime + " ms");
2. System 클래스의 nanoTime() 메소드 이용
nanoTime 메서드는 현재 Java 가상머신의 high-resolution 시간값을 ns(nano sec.) 단위로 반환한다.
long startTime = System.nanoTime();
// 특정 로직이나 메소드 호출
long endtime = System.nanoTime();
long elapsedTime = startTime - endtime;
System.out.println(elapsedTime + " ns");
3. Common-lang의 StopWatch 클래스 start(), stop() 메소드 이용
import org.apache.commons.lang.time.StopWatch;
stopWatch.start();
doSomeTask(5000);
stopWatch.stop();
System.out.println("Time: " + stopWatch.getTime()); // Time: 5000
4. 스프링 프레임워크의 StopWatch 클래스의 start(), stop() 이용
import org.springframework.util.StopWatch;
stopWatch.start();
List<Tuple> emps = empService.getEnameDnameTop5(deptno);
stopWatch.stop();
log.info(">>>>> getEnameDnameTop5(Time) : " + stopWatch.getTotalTimeSeconds());
12. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-3. @AspectJ Annotation을 이용한 AOP – Pointcut 선언
@Pointcut 어노테이션을 사용한다.
@Pointcut("execution(* onj.aop.*.*(..))") // pointcut expression
private void mypointcut() { } // pointcut signature
@Before("mypointcut ()")
public void myBeforeAdvice(JoinPointjoinPoint, int intValue) { ... }
13. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-3. @AspectJ Annotation을 이용한 AOP – Pointcut 선언
포인트컷 지정자(Pointcut Designators)
execution: 메소드 실행 결합점(join points)과 일치시키는데 사용된다.
within: 특정 타입에 속하는 결합점을 정의한다.
this: 빈 참조가 주어진 타입의 인스턴스를 갖는 결합점을 정의한다.
target: 대상 객체가 주어진 타입을 갖는 결합점을 정의한다.
args: 인자가 주어진 타입의 인스턴스인 결합점을 정의한다.
@target: 타겟 클래스가 주어진 타입의 어노테이션을 갖는 결합점을 정의한다.
@args: 전달된 인자의 런타임 타입이 주어진 타입의 어노테이션을 갖는 결합점을 정의한다.
@within: 주어진 어노테이션을 갖는 타입 내 결합점을 정의한다.
@annotation: 실행 메소드가 인자로 주어진 어노테이션을 갖는 결합점을 정의한다.
14. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-3. @AspectJ Annotation을 이용한 AOP – Pointcut 선언
포인트컷 지정자(Pointcut Designators)
//메소드가 어떤 패키지에 있던지 public 메소드라
면 충고적용
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}
//메소드가 onj 패키지 또는 부 패키지안의 타입이
라면 충고적용
@Pointcut("within(onj..*)")
private void inTrading() {}
//위 두포인트컷의 AND 조건을 만족하는 경우 충
고가 적용
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}
//메소드가 onj.dao 패키지 또는 부 패키지안의 타
입이라면 충고적용
@Pointcut("within(onj.dao..*)")
public void inDataAccessLayer() {}
[Aspect 클래스 작성 예]
@Aspect //애스팩트 클래스임을 표시
public class MyAdvice {
@Pointcut("execution(* onj.edu.aop11..hello*(int)) && args(intValue)")
public void helloExec(int intValue) {}
@Pointcut("bean(myDependency*)")
public void inMyDependency() {}
//@AspectJ 는 &&, aop 네임스페이스는 and
@Before("helloExec(intValue) && inMyDependency()")
public void myBeforeAdvice(JoinPointjoinPoint, int intValue) { ... }
//Around Advice
@Around("helloExec(intValue)")
public myAroundAdvice(ProceedingJoinPoint joinPoint, int intValue) throws Throwable { ... }
//......
}
15. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-4. @AspectJ Annotation을 이용한 AOP – aspectj expression
• execution(public * *(..)) : public 메소드가 포인트 컷
• execution(* onj.spring.aop.*.*(..)) : onj.spring.aop 패키지의 모든 메소드가 포인트 컷
• execution(* onj.spring.aop..*.*(..)) : onj.spring.aop 패키지와 하위 패키지의 모든 메소드가 포인트 컷
• execution(public void insert*(..)) : public에 리턴값, 패키지명 없고 메서드 이름은 insert로 시작, 인자 값은 0개 이상인 메서드가
포인트 컷
• execution(* onj.spring.aop.*.*()) : 리턴형 관계없고 onj.spring.aop 패키지의 모든 클래스, 인자 값이 없는 모든 메서드가 포인트 컷
• execution(* onj.spring.aop..*.*(..)) : 리턴형 관계없고 onj.spring.aop 패키지 및 하위 패키지에 있는 모든 클래스, 인자값이 0개 이
상인 메서드가 포인트 컷
• execution(* delete*(*)) : 메서드 이름이 delete으로 시작하는 인자 값이 1개인 메서드가 포인트 컷
• execution(* delete*(*,*)) : 메서드 이름이 delete로 시작하는 인자 값이 2개인 메서드가 포인트 컷
• execution(* onj*(Integer, ..)) : 메서드 이름이 onj로 시작하고 첫번째 인자 값의 타입이 Integer, 1개 이상의 매개변수를 갖는 메서
드가 포인트 컷
• within(onj.spring.aop.*) : onj.spring.aop 패키지 내의 모든 메소드가 포인트 컷
• within(onj.spring.aop..*) : onj.spring.aop패키지 및 하위 패키지의 모든 메소드가 포인트 컷
16. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-4. @AspectJ Annotation을 이용한 AOP – aspectj expression
• bean(oraclejava*) : 이름이 oraclejava로 시작되는 모든 빈의 메소드가 포인트 컷
• bean(*dataSource) || bean(*DataSource) : 빈 이름이 “dataSource” 나 “DataSource” 으로 끝나는 모든 빈의 메소드가 포인트 컷
• !bean(onjoraclejava) : onjoraclejava빈을 제외한 모든 빈의 메소드가 포인트
• this(onj.aop.SmallMartInterface) : 현재 실행중인 인스턴스가 SmallMartInterface 이름의 빈과 타입이 같은 경우 포인트컷,
SmallMart인터페이스를 구현했다면 모든 메소드가 포인트컷
• target(onj.aop.SmallMartInterface) : 타겟 오브젝트가 SmallMartInterface를 구현했다면 모든 메소드가 포인트컷
• args(java.io.Serializable) : 메소드가 매개변수가 하나이고 Serializable인터페이스를 타입이라면 포인트컷
• @target(org.springframework.transaction.annotation.Transactional) : 타겟 오브젝트가
org.springframework.transaction.annotation.Transactional 어노테이션(@Transactional)을 가진다면 포인트컷
• @within(org.springframework.transaction.annotation.Transactional) : 타겟 오브젝트의 선언된 타입이 @Trtansactional 어노테
이션을 가진다면 포인트컷
• @annotation(org.springframework.transaction.annotation.Transactional) : 실행 메소드가 @Transactional 어노테이션을 가진
다면 포인트컷
• @args(x.y.AnnotationRequired) : 파라미터를 하나 가지며 넘어오는 아규먼트의 런타임 타입이 @AnnotationRequired 타입이라
면 포인트컷
17. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-5. @AspectJ Annotation을 이용한 AOP – 예제
: 이전에 작성한 AOP NameSpace를 이용한 SmallMart 예제를 @AspectJ Annotation을 이용하여 변경해 보자.
STS에서
1. File -> New -> Spring Starter Project
Name : demo-smallmart3
Type : MAVEN
Package : onj.hello.aop3
다음화면에서 Core -> AOP 체크
2. @Inject를 사용하기 위해 의존성 추가
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
[SmallMartInterface.java]
package onj.hello.aop3;
public interface SmallMartInterface {
public String getProducts(String
productName) throws Exception;
}
18. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-5. @AspectJ Annotation을 이용한 AOP – 예제
[SmallMart.java]
package onj.hello.aop3;
@Component
public class SmallMart implements
SmallMartInterface {
public String getProducts(String
productName) throws Exception {
return "[Target
Method]getProduct()..." + productName;
//throw new
Exception("error...");
}
}
19. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-5. @AspectJ Annotation을 이용한 AOP – 예제
[SmallMartAspect.java]
package onj.hello.aop3;
@Component
@Aspect
public class SmallMartAspect {
@Pointcut("execution(* onj.hello.aop3.SmallMartInterface.getProducts(..))")
public void getProduct1() { }
@Pointcut("args(String)")
public void getProduct2() { }
// 사전충고 : 타겟클래스의 메소드 실행 전 충고실행
@Before("getProduct1() && getProduct2()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before Advice --> logBefore()...");
joinPoint.getSignature().getName();
}
// 사후충고(after) : 타겟클래스의 메소드 실행후 충고 실행, 오류가 발생해도 실행
@After("getProduct1()")
public void logAfter(JoinPoint joinPoint) {
System.out.println("After Advice --> logAfter()...");
joinPoint.getSignature().getName();
}
// 사후충고(after returning) : 타겟클래스의 메소드 정상 리턴된 후 실행
// returning 속성으로 리턴값을 받을 수 있다.
@AfterReturning(pointcut = "getProduct1()", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
System.out.println("AfterReturning Advice --> logAfterReturning()...");
joinPoint.getSignature().getName();
System.out.println("return value is " + result);
}
20. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-5. @AspectJ Annotation을 이용한 AOP – 예제
[SmallMartAspect.java]
// 주변충고 : 타겟클래스의 메소드 실행 전후에 충고 실행
@Around("getProduct1()")
public String logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Around Advice[전] --> logAround()...");
joinPoint.getSignature().getName();
Arrays.toString(joinPoint.getArgs());
// 타겟 객체의 원래 메소드 실행
String s = (String) joinPoint.proceed();
System.out.println("Around Advice[후] --> logAround()...");
// 원래 타겟클래스의 메소드 리턴값을 대체시킴
return "이문장으로원래 타겟메소드(getProducts) 리턴이 대체됨!!";
}
// 예외충고 : 예외가 발생될때 충고 실행
@AfterThrowing(pointcut = " getProduct1()", throwing = "error")
// error는 실행되는 메소드에서 던지는 예외객체
public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
System.out.println("예외충고 --> logAfterThrowing()...");
joinPoint.getSignature().getName(); System.out.println("Exception " + error);
}
}
21. 3. Spring AOP((Spring Aspect Oriented Programming)
3-8. @AspectJ Annotation을 이용한 AOP
3-8-5. @AspectJ Annotation을 이용한 AOP – 예제
[DemoSmallMart3Application.java]
package onj.hello.aop3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
public class DemoSmallmart3Application implements CommandLineRunner {
public static void main(String[] args) throws Exception {
SpringApplication.run(DemoSmallmart3Application.class, args);
}
@Inject
private SmallMartInterface smallMart;
public void run(String... args) throws Exception {
this.smallMart.getProducts("과자");
}
}
22. 3. Spring AOP((Spring Aspect Oriented Programming)
3-9. 스프링 선언적 AOP에 대한 고려사항(@AspectJ vs XML)
aop 네임스페이스와 @AspectJ 어노테이션의 차이
포인트컷 구문이 조금 다르다. (aop 네임스페이스는 “and”, @AspectJ 어노테이션에서는 “&&”)
aop 네임스페이스에서는 ‘싱글톤’ 방식의 애스펙트 인스턴스화 모델만 지원
@AspectJ 어노테이션 방식에서는 두 개의 포인트컷 정의 (helloExec(intValue) && inMyDependency()) 를 사전충고, 주변 충고에서 조합할 수
있지만 aop 네임스페이스에서는 조건을 조합한 포인트 컷을 새로 생성해야 한다.
}
}
• 스프링 애플리케이션이 XML 기반이라면 aop 네임스페이스를 이용 하는것이 적절하다. 이렇게 하면 DI, AOP 설
정 방식을 일관되게 유지할 수 있기 때문이다.
• 애플리케이션이 어노테이션 기반이라면 @AspectJ 어노테이션을 사용 한다. @AspectJ 어노테이션을 사용하는
경우 모듈 안에 어스펙트 관련 정보를 캡슐화 할 수 있기 때문에 유지보수가 용이하다.
• AspectJ or XML for Spring AOP?
AspectJ Annotation 방식
@Pointcut(execution(* get*()))
public void a() {}
@Pointcut(execution(org.xyz.Account+ *(..))
public void b() {}
@Pointcut(a() && b())
public void complex() {}
aop NameSpace를 이용한 XML 방식()
위에서 정의한 complext Pointcut은 정의가 불가능 하다.
<aop:pointcut id=“a" expression="execution(* get*())"/>
<aop:pointcut id=“b" expression="execution(org.xyz.Account+ *(..))"/>
23. Any Questions ?
THANK YOU
FOR YOUR ATTENTION
무료국비지원교육 / 구로자바학원 _ 탑크리에듀 교육센터
http://www.topcredu.co.kr