본문 바로가기

SpringFramework/Spring

Spring - Filter, InterCeptor, AOP

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 참고자료

- https://jojoldu.tistory.com/71

'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