1). 왜 사용하나?
- 기존의 어노테이션들을 묶어서 지정하거나 값을 지정하고 재사용하게 만들어 코드의 양을 줄이고, 유지보수를 편하게 할 수 있다.
- AOP의 타겟으로 삼아 pointcut으로 지정하기 애매한 것들을 어노테이션으로 타겟팅 할 수도 있다.
다시한번 용어 설명 하고 Annotation 을 만들어보려고 한다.
공통 코드를 어드바이스(Advice)한다.
끼워넣을 곳을 조인포인터(Joinpoint) 라고 한다.
조인포인터 들을 모아둔것을 포인트컷(Pointcut)이라고 한다.
어드바이스를 조인포이트에 실제로 끼워넣는 작업을 위빙(Weaving) 이라고 한다.
스프링에만 있는 것으로 어드바이스와 포인트컷을 한데 묶어 다루는 어드바이저(Advisor)라고 한다.
2). Target과 Retention
2-1). @Target
어노테이션을 적용할 대상을 지정하기 위해 미리 적용 대상을 정해놓는 아이.
ElementType(Enum)타입으로 값을 지정한다
- @Target에 사용 할 수 있는 요소
- PACKAGE - 패키지에 적용
- CONSTRUCTOR - 생성자에 적용
- TYPE - Class, interface (annotation type포함), enum 에 적용
- FIELD - enum을 포함한 필드에 적용
- METHOD - 메소드에 적용
- PARAMETER - 파라미터에 적용
- LOCAL_VALIABLE - 지역변수에 적용
2-2). @Retention
어노테이션을 유지하는 기간을 나타낸다.
RetentionPolicy(Enum)타입으로 값을 지정한다.
- @Target에 사용 할 수 있는 요소
- RUNTIME - 어노테이션은 컴파일러에 의해 클레스 파일에 기록되고, VM이 도는 동안 유지
- CLASS - 어노테이션은 컴파일러에 의해 클레스 파일에 기록되지만, VM이 도는 동안 유지될 필요가 없다.(Default)
- SOURCE - 어노테이션은 컴파일러에 의해 삭제될 수 있다.
예시 :
//값 입력없이 사용
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface CustomAn {
}
//값을 입력하게 함.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface CustomAn {
int value(); default 0;
}
2-3). Advice의 적용시점 요소
Advice를 작성할때 적용시점을 선택하여 기재해주어야한다.
추가로 적용할 Pointcut을 지정해 줄 수 있다.
- Before Advice : Joinpoint 앞에서 실행
- Around Advice : Joinpoint 앞과 뒤에서 실행
- After Advice : Joinpoint 호출이 리턴되기 직전에 실행되
- After Returning Advice : Joinpoint 메서드 호출이 정상적으로 종료된 후에 실행
- After Throwing Advice : 예외가 발생했을때 실행
예시 :
@Before("@annotation(com.test.spring.aop.annotations.CustomAn))
public void checkUser(JoinPoint jp, CustomAn target) throws Exception {
......끼워넣고 싶은 코드들
}
//값을 받을때
@Before("@annotation(com.test.spring.aop.annotations.CustomAn) && @annotation(target))
public void checkUser(JoinPoint jp, CustomAn target) throws Exception {
......끼워넣고 싶은 코드들
}
<부가 설명>
이때 주의해야될 점이 있다.
값을 전달하고 받아서 사용할때와 값이 없을때의 차이점을 코드로 볼수 있을것이다.
@Before 안에
1번째 @annotation : 만들어놓은 annotation를 넣어준다.
<값을 전달받아 사용하고 싶을때>
2번째 @annotation : target으로 기재하고, 만들어놓은 annotation의 값을 사용하기 위해 타입을 맞춰준다.
이 target이라는 파라미터를 사용하여 interface를 만들때 지정해놓은 변수에 접근 할 수 있다. => target.value() = 0
JoinPoint를 어드바이스 메소드 매개변수로 선언해야한다. 이때 인자는 스프링 컨테이너가 넘겨준다. ex. 메소드명(JoinPoint jp)
@Before("@annotation(com.test.spring.aop.annotations.CustomAn))
public void checkUser(JoinPoint jp, CustomAn target) throws Exception {
......끼워넣고 싶은 코드들
}
이때 Around 어드바이스만 다른 어드바이스와 약간 다른데, ProceedingJoinPoint 객체를 인자로 선언해야한다.(proceed() 등이 추가로 구현되어있음) ProceedingJoinPoint 는 JoinPoint를 상속받는다
@Around("@annotation(com.test.spring.aop.annotations.CustomAn))
public void checkUser(ProceedingJoinPoint jp, CustomAn target) throws Exception {
......끼워넣고 싶은 코드들
}
PointCut의 Execution 표현 방법
execution(public void set*(..)) |
리턴타입이 void, 메소드 이름이 set으로 시작, 파라미터가 0개 이상인 |
execution(* chap07.*.*()) |
chap07 패키지의 타입에 속한 파라미터가 없는 모든 메소드 호출 |
execution(* chap07..*.*(..)) |
chap07 패키지 및 하위 패키지에 있는 파라미터가 0개 이상인 메소드 호출 패키지 부분의 '..' -> 하위 패키지 |
execution(Long chap02.Calculator.factorial(..)) |
리턴 타입이 Long인 Calculator 타입의 factorial() 메소드 호출 |
execution(* get*(*) |
이름이 get으로 시작하고 2개의 파라미터를 갖는 메소드 호출 |
execution(* get*(*,*) |
이름이 get으로 시작하고 2개의 파라미터를 갖는 메소드 호출 |
execution(* read*(Integer,..)) |
메소드 이름이 read로 시작하고, 첫 번째 파라미터 타입이 Integer이며 1개 이상의 파라미터를 갖는 메소드 호출 |
3). Custom Annotation을 만들어 적용까지.
3-1). annotation를 만들자.
설명 :
VM이 도는 동안 Annotation은 클래스파일로 컴파일 되어 유지된다.
타깃으로 잡은 아이는 Method만 적용하겠다.
@CustomAn 라는 어노테이션으로 사용하겠다.
int[] : 1,2,3 번에 따른 값을 담을 공간을 넣어준다. => 차후 사용한다.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface CustomAn {
int[] CustomAn(); //1 : 뚱땡이 , 2 : 날씬이 , 3 : 민간인
}
3-2). Advice를 만들자.
설명 :
타깃인 메서드가 실행되기 전에 Advice를 실행시킨다.
값을 전달받아서 사용할것이다.
HttpRequest에서 접속 정보를 가져와 list에 있는 타입인지 체크한다.
@Before("@annotation(com.test.spring.aop.annotations.CustomAn) && @annotation(target))
public void CustomAn(JoinPoint jp, CustomAn target) throws Exception {
......끼워넣고 싶은 코드들
HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
TestVo session = (TestVo)req.getAttribute("name");
int[] list = target.customAn();
if(StringUtils.isEmpty(list)) {
throw new TestException("","ERROR");
}
boolean result = false;
loop: for(int i = 0; i < list.length; i++) {
if(session.getTestTypeId() == list[i]) {
result = true;
break loop;
}
}
}
3-3). 적용
설명 :
우리가 만든 Annotation의 value에 값 1을 넣어준다.
그러면 지금 전달해 준 값 1이 들어간 List를 Advice에서 조회하여 해당 메서드에 접근한 사용자가 뚱땡이,날씬이,민간인 중에 맞는지 아닌지를 판단하여, 메서드를 실행 시킬지 말지를 결정한다.
@CustomAn(customAn = {1})
public HashMap<String,Object> getTestVoList(HttpServletRequest req, TestVo testVo) throws Exception {
System.out.print("날 쓸수 있어");
... Logic
}
'Spring-JSP' 카테고리의 다른 글
[Spring-JSP] 엄청쉬운 Apache Poi Excel 양식다운로드 기능구현 / 테이블 참조 (0) | 2021.02.23 |
---|---|
[Spring-JSP] 엄청쉬운Poi Excel 업로드 기능 구현 (0) | 2021.02.17 |
[Spring-JSP] 파일업로드 처리 / 파일(단,다중) + 추가정보 @ModelAttribute (0) | 2021.01.29 |
[Spring-JSP]문자열 출력시 공백 및 줄바꿈 적용 (0) | 2021.01.22 |
[Spring-JSP] AES-256 암호화 (0) | 2020.11.17 |