Spring AOP와 Aspectj를 사용하는 방법이다.
AspectJ로 사용할 수 있는 Annotation은 다음과 같다.
@Before – Run before the method execution @After – Run after the method returned a result @AfterReturning – Run after the method returned a result, intercept the returned result as well. @AfterThrowing – Run after the method throws an exception @Around – Run around the method execution, combine all three advices above. |
AspectJ를 사용하기 위해서는 spectjrt.jar, aspectjweaver.jar and spring-aop.jar
를 추가해 주어야 한다.
(spring-aop.jar의 경우, spring-context.jar or spring-webmvc.jar 에 포함되어 있어서 없는 경우만 별도 추가한다.)
Mavenj Pom.xml에 다음과 같이 추가 해 주어야 한다.
<!-- AspectJ --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>${org.aspectj.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectjweaver.version}</version> </dependency> <!-- AOP --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> |
main 함수
public class App { public static void main(String[] args) { ApplicationContext appContext = new ClassPathXmlApplicationContext( new String[] { "Spring-Context.xml" });
CustomerService cust = (CustomerService) appContext.getBean("customAspect");
System.out.println("*************************"); cust.printName(); System.out.println("*************************"); } } |
실행할 Service 구현
package com.sncap.service; public class CustomerService { private String name; private String url;
public void setName(String name) { this.name = name; }
public void setUrl(String url) { this.url = url; }
public void printName() { System.out.println("Customer name : " + this.name); }
public void printURL() { System.out.println("Customer website : " + this.url); }
public void printThrowException() { throw new IllegalArgumentException(); } } |
Aspcet Code
package com.sncap.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class LoggingAspect {
public void logBefore(JoinPoint joinPoint) { System.out.println("logBefore() is running!"); System.out.println("hijacked : " + joinPoint.getSignature().getName()); System.out.println("******"); } public void logAfter(JoinPoint joinPoint) { System.out.println("logAfter() is running!"); System.out.println("hijacked : " + joinPoint.getSignature().getName()); System.out.println("******"); }
public void logAfterReturning(JoinPoint joinPoint, Object result) { System.out.println("logAfterReturning() is running!"); System.out.println("hijacked : " + joinPoint.getSignature().getName()); System.out.println("Method returned value is : " + result); System.out.println("******"); } public void logAfterThrowing(JoinPoint joinPoint, Throwable error) { System.out.println("logAfterThrowing() is running!"); System.out.println("hijacked : " + joinPoint.getSignature().getName()); System.out.println("Exception : " + error); System.out.println("******"); } public void logAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("logAround() is running!"); System.out.println("hijacked method : " + joinPoint.getSignature().getName()); System.out.println("hijacked arguments : " + Arrays.toString(joinPoint.getArgs())); System.out.println("Around before is running!"); joinPoint.proceed(); //continue on the intercepted method System.out.println("Around after is running!"); System.out.println("******"); } } |
위와 같은 Aspect code를 생성한 후 xml을 통해서 load한다.
method위에 annotation 형태로도 가능하다.
ex)
@Aspect public class LoggingAspect { @Around("execution(* com.sncap.aspect.LoggingAspect.logAround(..))") public void logAround(ProceedingJoinPoint joinPoint) throws Throwable { } |
하지만, 변경시 마다 code를 수정해야 하는 불편함 때문에, xml에 정의하는 것을 (본좌는)선호한다.
[Encable AspectJ]
(spring-context.xml)
<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-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<bean id="customAspect" class="com.sncap.aspec.LoggingAspect " /> <aop:aspectj-autoproxy />
<!-- Aspect --> <aop:config> <aop:aspect id="before" ref="customAspect"> <aop:pointCut id="pointCutBefore" expression="expression(public * com.sncap..*Service.*(..))" />
<aop:before method="logBefore" pointcut-ref="before" /> <aop:after method="logAfter" pointcut-ref="before" /> <aop:afterr-returning method="logAfterReturning" pointcut-ref="before" /> <aop:after-throwing method="logAfterThrowing" pointcut-ref="before" /> <aop:around method="logAround" pointcut-ref="before" /> </aop:aspect> </aop:config> </beans> |
sncap 폴더 하위의 모든 package 중 *Service class의 모든 method에 대해서 Aspect를 수행한다.
실행시는 Asjpect code 부분의 호출 없이 아래와 같이 Service를 실행하면 같이 수행된다.
예는 around만 수행함.
Run it
CustomerService service = (CustomerService) appContext.getBean("customAspect"); service.logAround("sncap"); |
Output
************************* logAround() is running! hijacked method : logAround hijacked arguments : sncap Around before is running sncap Around after is running! ************************* |