1] Filter
2] InterCeptor
3] AOP
이 세가지 기능은 비슷한 역할을 한다. 이들을 작업할 때 공통되는 부분을 중복코드를 방지하기 위해 묶어주는 역할을 한다. 이 기능들은 Spring에서 제공해준다.
그렇다면, 차이점은 어디에 있을까? 흐름은 아래 그림과 같다.
차이점은 실행하는 위치가 다르다.
흐름: Filter -> InterCeptor -> AOP -> InterCeptor -> Filter
Filter
Filter는 Servlet이 실행되는 동안에 Init가 실행되고, doFilter가 실행된다.
[인코딩 변환 처리, XSS방어 등의 요청에 대한 처리부에 주로 사용]
// Filter 예제코드. Filter 인터페이스를 오버라이딩하여 구현한다.
import javax.servlet.Filter;
@SpringBootApplication
@ServletComponentScan(basePackages = "com.wmp.epdev")
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@WebFilter(filterName = "myFilter",urlPatterns = "/filter")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
PrintWriter writer = servletResponse.getWriter();
writer.print("this is filter");
servletResponse.setContentType("text/html;charset=UTF-8");
}
@Override
public void destroy() {
}
}
@Controller
public class MyController {
@ResponseBody
@RequestMapping("/**")
public String anyRequest() {
return "hello javaworld!";
}
}
=> MyFilter Class에 Filter를 구현하고, MainApplication에서 @ServletComponentScan을 설정하여 Filter를 적용하면, 실행된다.
- init() - 필터 인스턴스 초기화
- doFilter() - 전/후 처리
- destroy() - 필터 인스턴스 종료
InterCeptor
InterCeptor는 Controller를 호출하기전에 preHandler 메소드가 실행되며, Controller가 실행된 이후에 postHandler가 실행된다.
[인터셉터는 여러개를 사용할 수 있고 주로 로그인, 권한, 프로그램 실행시간 계산 로그확인 등에서 사용]
인터셉트를 구현하는 방법은.. HandlerInterceptor 인터페이스를 구현(implements)하여(이때, 아래에서 설명하는 메소드를 오버라이딩하여 구현) Bean에 등록(@Component)한다.
(org.springframework.web.servlet.HandlerInterceptor)
- preHandler() - Controller 실행 전
- postHandler() - Controller 실행 후 (view Rendering or Response) 실행 전
- afterCompletion() - (view Rendering or Response) 이후
인터셉터를 구현한 클래스를 사용하기 위해 설정 클래스를 생성하여 Bean에 등록한다. (@Configuration) 이때, Interceptor를 등록하기 위해서 WebMvcConfigurer를 이용한다.
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
@Qualifier(value = "httpInterceptor")
private HandlerInterceptor interceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) { // 등록할 인터셉터를 설정한다.
registry.addInterceptor(interceptor)
.addPathPatterns("/**")
.excludePathPatterns("/user/**");
}
}
AOP
AOP(Aspect Oriented Programming)는 컨트롤러 - 서비스(비즈니스 로직 수행 구간) 영역에서 주로 실행된다. 실제로 로그, 트랜젝션, 에러 처리 등에서 많이 사용된다. AOP는 Filter와 Interceptor와는 달리, 메소드 전후 지점에서 자유롭게 설정이 가능하다. 주소, 파라미터, 어노테이션 등 다양한 방법으로 대상을 지정할 수 있다.
- AOP는 앞서 설명했던 Filter나 Interceptor와는 달리 메소드 전후의 지점에 자유롭게 설정이 가능하다. 그리고 패키지 구조(=주소)로 대상을 구분해서 걸러내야하는 반면 AOP는 주소, 파라미터, 어노테이션 등의 다양한 방법으로 대상을 지정할 수 있다.
- AOP의 Advice의 경우, JoinPoint나 ProceedingJoinPoint 등을 활용해서 호출한다. 반면, Interceptor의 HandlerInterceptor는 Filter와 유사하게 HttpServletResponse를 파라미터로 사용한다.
AOP의 주요개념
- Aspect: 여러 곳에서 쓰이는 코드(공통부분)를 모듈화한 것
- Target: Aspect를 적용하는 곳 (클래스, 메소드)
- Advice: Aspect에서 실징적인 기능에 대한 구현체
- JoinPoint: Advice가 Target에 적용되는 시점, 메소드 진입할 때, 생성자 호출할 때, 필드에서 값을 꺼낼 때 등등
- PointCut: JoinPoint의 상세 스펙을 정의한 것
AOP는 의존성이 새로 추가되어야 한다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-aop'
}
PointCut 표현식
: PointCut을 이용하면 Advice메소드가 적용될 부분을 정확하게 필터링할 수 있다.
=================================================================
[@Around("execution(* com.test.epdev.api.exam1.controller.UserController(..))")]
=================================================================
-> @Around: 어드바이스. Aspect가 무엇을 언제 할지 정한다.
그리고 언제는 5가지 타입으로 나누어지고 사용할 수 있다.
- @Before (이전): 어드바이스 타겟 메소드가 호출되기 전에 어드바이스 기능을 수행
- @After (이후): 타겟 메소드의 결과에 관계없이(즉 성공, 예외 관계없이) 타겟 메소드가 완료 되면 어드바이스 기능을 수행
- @AfterReturning (정상적 반환 이후): 타겟 메소드가 성공적으로 결과값을 반환 후에 어드바이스 기능을 수행
- @AfterThrowing (예외 발생 이후): 타겟 메소드가 수행 중 예외를 던지게 되면 어드바이스 기능을 수행
- @Around (메소드 실행 전후): 어드바이스가 타겟 메소드를 감싸서 타겟 메소드 호출전과 후에 어드바이스 기능을 수행
-> execution: PointCut 지정자
-> * : 리턴 타입. *의 경우, 모든 타입 리턴가능
-> com.test.epdev.api.exam1.controller.UserController: 타겟이 되는 컨트롤러 지정
-> (..) : 인자 타입. 현재는 모든 타입 인자를 허용함
PointCut 표현식
execution 명시자
리턴타입 지정 | |
* | 모든 리턴타입 허용 |
void | 리턴타입이 void인 메소드 선택 |
!void | 리턴타입이 void가 아닌 메소드 선택 |
패키지 지정 | |
com.test.epdev | 정확하게 com.test.epdev를 선택 |
com.test.epdev.. | com.test.epdev 패키지로 시작하는 모든 패키지를 선택 |
클래스 지정 | |
UserController | 정확하게 UserController클래스를 선택 |
*Controller | 클래스명이 Controller로 끝나는 클래스를 선택 |
UserChildController+ | 클래스 이름 뒤에 '+'가 붙으면 해당 클래스로부터 파생된 모든 자식 클래스 선택, 인터페이스라면 해당 인터페이스를 구현한 모든 클래스 선택 |
메소드 지정 | |
*(..) | 모든 메소드 선택 |
test*(..) | test~로 시작하는 모든 메소드를 선택 |
매개변수 지정 | |
(..) | 모든 매개변수 |
(*) | 반드시 1개의 매개변수를 가지는 메소드만 선택 |
(com.test.epdev.api.exam1.dto.UserDto) | 매개변수로 UserDto를 가지는 메소드만 선택. (!) 반드시 풀패키지명을 기입해야함. |
(!com.test.epdev.api.exam1.dto.UserDto) | 매개변수로 UserDto를 가지지 않는 메소드만 선택. |
(Integer, ..) | 1개 이상의 변수 + 첫번째 매개변수 타입이 Integer인 메소드 선택 |
(Integer, *) | 반두시 2개의 매개변수 + 첫번째 매개변수 타입이 Integer인 메소드만 선택 |
- 가장 하위 단위는 메소드
- 조건에 충족하지 않으면 공통기능(Aspect)를 호출하지 않음. (오류가 발생하지는 않는다)
within 명시자
- within(com.test.epdev.api.service.UserService): com.test.epdev.api.service.UserService의 모든 메소드
- within(com.test.epdev.api.service.*): com.test.epdev.api.service 패키지의 모든 메소드
- within(com.test.epdev.api.service..*): com.test.epdev.api.service 패키지 및 하위 패키지의 모든 메소드
bean 명시자
- bean(exampleBean): 이름이 exampleBean인 빈의 모든 메소드
- bean(example*): 빈의 이름이 example로 시작하는 빈의 모든 메소드
코드로 살펴보는 AOP적용예제
@Component
@Aspect // AOP 적용
public class ExampleAspect {
@Around("execution(* com.test.epdev..*.*(..))") // com.test.epdev 패키지 및 하위 패키지에 있는 파라미터가 0개 이상인 모든 메소드를 표현.
public Object logTest(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object retVal = pjp.proceed(); // @Aspect가 선언되면, proceed() 메소드는 반드시 구현되어야 한다.
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
=> com.test.epdev 패키지 및 하위 패키지에 있는 파라미터가 0개 이상인 모든 메소드(조건이 충족)가 호출 되었을 경우에 위의 기능을 실행하게 된다.
AOP 참고자료
'SpringFramework > Spring' 카테고리의 다른 글
Spring - Validation (0) | 2021.10.20 |
---|---|
Spring - ErrorController (0) | 2021.10.15 |
Spring - Log4j2 (0) | 2021.10.13 |
Spring - Swagger (0) | 2021.10.08 |
Spring - RestTemplate (0) | 2021.10.08 |