2021. 10. 14. 16:17ㆍ개인노트
오늘은 Spring boot에서 AOP사용 및 커스텀 태그를 사용하여 적용시키는 부분을 공부해봤다.
이유는…
이전 프로젝트를 진행하던 당시에 특정 프로세스가 수행될때 로그처럼 DB에 접근정보를 남겨달라는 요구사항이 있었는데(Insert).. 그 당시에는 AOP를 제대로 모르던 시기라서 해당 서비스Impl 로직 앞뒤로 직접 추가하여 적용시켰다…
그게 이후에 마음에 걸려 새로이 공부하게되었다.
일단 sts툴에서 아래와같이 프로젝트를 생성하였다.
위와같이 최초 설정 후 aop사용을 위해 아래를 pom.xml에 추가했다.
<!-- AspectJ 디펜던시 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
그리고 아래와 같이 컨트롤러를 간단하게 생성진행!
@RestController
public class SampleController {
@GetMapping(value = "/test1")
public String test1() {
return "test1";
}
@MyCustom
@RequestMapping(value = "/test2")
public String test2() {
return "test2";
}
}
하나는 GetMapping일때 AOP가 적용되도록 하였고, 나머지 하나는 MyCustom이라는 태그가 존재할때 aop를 적용시켜서 log를 찍게 셋팅하였다.
customAnnotation패키지에 MyCustom 어노테이션 생성
package com.test.sampleAOP.customAnnotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyCustom{
}
그리고 AOP를 적용시킬 SampleAdvice 클래스를 생성하였다.
package com.test.sampleAOP.advice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class SampleAdvice {
private static final Logger logger = LoggerFactory.getLogger(SampleAdvice.class);
/**
* @GetMapping import org.springframework.web.bind.annotation.GetMapping
* getMapping일때 적용시키도록 포인트컷 설정(어디에)
*/
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
public void GetMapping() {
}
/*
* @Pointcut("@annotation(com.test.sampleAOP.customAnnotation.MyCustom)")
* public void CustomAnnotation() {
*
* }
*/
//@Before("GetMapping()")
@Before("@annotation(com.test.sampleAOP.customAnnotation.MyCustom)")
public void before(JoinPoint joinPoint) {
logger.info("=====================AspectJ TEST : Before Logging Start=====================");
logger.info("=====================AspectJ TEST : Before Logging End=====================");
}
@AfterReturning(pointcut = "GetMapping()", returning = "result")
//@AfterReturning("CustomAnnotation()")
public void AfterReturning(JoinPoint joinPoint, Object result) {
logger.info("=====================AspectJ TEST : AfterReturning Logging Start=====================");
logger.info("=====================AspectJ TEST : AfterReturning Logging END=====================");
}
@After("GetMapping()")
//@After("CustomAnnotation()")
public void after(JoinPoint joinPoint) {
logger.info("=====================AspectJ TEST : After Logging Start=====================");
logger.info("=====================AspectJ TEST : After Logging END=====================");
}
@Around("GetMapping()")
//@Around("CustomAnnotation()")
public Object Around(ProceedingJoinPoint joinPoint) throws Throwable {
logger.info("=====================AspectJ TEST : Around Logging Start=====================");
try {
Object result = joinPoint.proceed();
logger.info("=====================AspectJ TEST : Around Logging END=====================");
return result;
}catch (Exception e) {
logger.error("=====================AspectJ Around Exception=====================");
logger.error(e.toString());
return null;
}
}
}
여기서 주의할 점은 Around를 사용할경우 Object를 return 해야한다는 것이다.
joinPoint.proceed(); << 이 부분에서 프록시형태로 진행되기때문에..! 자세한 사항은 다른 블로그들에 잘 설명이 되어있다!
##실행된 로그##
##추가사항##
수행시간이 얼마나되는지 테스트가 필요하다거나 할때 심플하게 AOP를 적용하는 경우
LogExecuteTime 커스텀태그를 아래와 같이 생성한 뒤
package com.test.sampleAOP.customAnnotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogExecuteTime {
}
Controller에도 해당부분을 추가
@LogExecuteTime
@RequestMapping(value = "/test3")
public String test3() {
return "test3";
}
그리고 Advice에서 사용할 부분도 선언해주면..!
@Around("@annotation(com.test.sampleAOP.customAnnotation.LogExecuteTime)")
public Object AroundForTimeLog(ProceedingJoinPoint joinPoint) throws Throwable {
logger.info("=====================AspectJ TEST : AroundForTimeLog Logging Start=====================");
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object result = joinPoint.proceed();
logger.info("=====================AspectJ TEST : AroundForTimeLog Logging END=====================");
stopWatch.stop();
logger.info(stopWatch.prettyPrint());
return result;
}
시간이 얼마나 걸렸는지 심플하게 볼 수 있다..!
포인트컷을 사용하는방법은 아래처럼 다양한 종류가 존재하는데 원하는 방식으로 사용하거나 프로젝트에서 사용하자고 명시된 방법에 맞춰서 적용시키는게 베스트방법 일듯하다.
지난 번에 이어서 Advice가 어떤 JoinPoint에 사용될 것인지를 지정하는 PointCut 표현식을 정리하겠습니다.
포인트컷에는 다양한 명시자를 이용할 수 있습니다.
execution | Advice를 적용할 메서드를 명시할 때 사용합니다. |
within | 특정 타입에 속하는 메서드를 JoinPoint로 설정되도록 명시할 때 사용합니다. |
bean | 스프링 버전 2.5 버전부터 지원하기 시작했으며, 스프링 빈을 이용하여 JoinPoint를 설정합니다. |
execution 명시자
execution([수식어] 리턴타입 [클래스이름].이름(파라미터)
- 수식어 : public, private 등 수식어를 명시합니다. (생략 가능)
- 리턴타입 : 리턴 타입을 명시합니다.
- 클래스이름 및 이름 : 클래스이름과 메서드 이름을 명시합니다. (클래스 이름은 풀 패키지명으로 명시해야합니다. 생략도 가능)
- 파라미터 : 메서드의 파라미터를 명시합니다.
- " * " : 모든 값을 표현합니다.
- " .. " : 0개 이상을 의미합니다.
Ex)
execution(public Integer com.edu.aop.*.*(*))
- com.edu.aop 패키지에 속해있고, 파라미터가 1개인 모든 메서드
execution(* com.edu..*.get*(..))
- com.edu 패키지 및 하위 패키지에 속해있고, 이름이 get으로 시작하는 파라미터가 0개 이상인 모든 메서드
execution(* com.edu.aop..*Service.*(..))
- com.edu.aop 패키지 및 하위 패키지에 속해있고, 이름이 Service르 끝나는 인터페이스의 파라미터가 0개 이상인 모든 메서드
execution(* com.edu.aop.BoardService.*(..))
- com.edu.aop.BoardService 인터페이스에 속한 파마리터가 0개 이상인 모든 메서드
execution(* some*(*, *))
- 메서드 이름이 some으로 시작하고 파라미터가 2개인 모든 메서드
within 명시자
Ex)
within(com.edu.aop.SomeService)
- com.edu.aop.SomeService 인터페이스의 모든 메서드
within(com.edu.aop.*)
- com.edu.aop 패키지의 모든 메서드
within(com.edu.aop..*)
- com.edu.aop 패키지 및 하위 패키지의 모든 메서드
bean 명시자
Ex)
bean(someBean)
- 이름이 someBean인 빈의 모든 메서드
bean(some*)
- 빈의 이름이 some으로 시작하는 빈의 모든 메서드
참고사이트
https://icarus8050.tistory.com/8
'개인노트' 카테고리의 다른 글
(인터셉터+어노테이션)로그인 체크하기(session) (0) | 2021.10.19 |
---|---|
Java 리플렉션(reflection)으로 만들어보는 dispatcher-servlet,컨트롤러 및 커스텀 어노테이션 (0) | 2021.10.15 |
개발자 채용 과제 도전 및(+) 후기(....) (0) | 2021.10.12 |
Hyper-V 사용안할때 끄는법 (0) | 2021.09.26 |
Localhost https적용을 위한 key파일생성 (0) | 2021.09.16 |