[필독][기초] / 서블릿 / servlet [part 7]

2016. 1. 5. 15:00language/jsp

Filter
필터란 사용자의 요청이 서버 자원에 전달되기 전에 언제나 수행되어야 하는 코드 조각이 있을 때 사용한다.
필터는 web.xml 에서 선언과 매핑을 해야 한다.


web.xml 에 필터1 다음에 필터2 가 순서대로 선언되고 매핑되었다면
 필터1-필터2-서버 자원 순으로 실행될 것이다.
필터는 응답이 사용자의 웹브라우저에 도달되기 전에도 필터링 할 수 있다.
필터2-필터1-웹브라우저 순서로 응답이 도달된다.


여기서 서버 자원은 서블릿,JSP는 물론이고 HTML페이지와 이미지와 같은 정적인 자원를 포함하며, web.xml 에서 필터 관련 매핑 설정에서 URL 패턴에 부합하는 자원을 말한다.
필터 클래스를 작성하기 위해서는 javax.servlet.Filter 인터페이스를 구현해야 한다.



Filter 인터페이스

•init (FilterConfig filterConfig) throws ServletException
          서블릿 컨테이너에 의해 호출되면 필터는 서비스 상태가 됨
•doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,                     ServletException
          서블릿 컨테이너에 의해 호출되어 필터링 작업을 수행
•destroy()
          서블릿 컨테이너에 의해 호출되면 해당 필터는 더 이상 서비스를 할 수 없음. 주로 자원반납을 위해 사용


doFilter 메소드의 아규먼트를 보면, 필터가 요청이나 응답을 가로챌 때 ServletRequest 와 ServletResponse 그리고 javax.servlet.FilterChain 객체를 접근할 수 있음을 알 수 있다.
여기서 FilterChain 객체는 순서대로 호출되어야 하는 필터의 리스트를 담고 있다.
필터의 doFilter 메소드에서 FilterChain 의 doFilter 메소드를 호출하기 전까지가 요청전에 실행되는 필터링 코드이며 FilterChain의 doFilter 메소드 호출다음은 응답전에 호출되는 필터링 코드이다.
이와 같이 작동하는 이유가 궁금하면 아래 필터를 흉내낸 자바 순수 애플리케이션을 실행해 본다.



필터 매커니즘을 흉내낸 자바 순수 애플리케이션


ChainFilter.java


package net.java_school.filter;

import java.util.ArrayList;
import java.util.Iterator;

public class ChainFilter {
          private ArrayList<Filter> filters;
          private Iterator<Filter> iterator;

          public void doFilter() {
                    if (iterator.hasNext()) {
                              iterator.next().doFilter(this);
                    } else {
                              System.out.println("서버 자원을 호출한다.");
                    }
          }

          public ArrayList<Filter> getFilters() {
                    return filters;
          }

          public void setFilters(ArrayList<Filter> filters) {
                    this.filters = filters;
                    this.iterator = filters.iterator();
          }
         
}


Filter.java


package net.java_school.filter;

public interface Filter {
         
          public void doFilter(ChainFilter chain);

}


Filter1.java


package net.java_school.filter;

public class Filter1 implements Filter {

          @Override
          public void doFilter(ChainFilter chain) {
                    System.out.println("서버 자원 실행전에 필터1 실행부");
                    chain.doFilter();
                    System.out.println("서버 자원 실행후에 필터1 실행부");
          }

}


Filter2.java


package net.java_school.filter;

public class Filter2 implements Filter {

          @Override
          public void doFilter(ChainFilter chain) {
                    System.out.println("서버 자원 실행전에 필터2 실행부");
                    chain.doFilter();
                    System.out.println("서버 자원 실행후에 필터2 실행부");
          }

}


Tomcat.java


package net.java_school.filter;

import java.util.ArrayList;

public class Tomcat {

          public static void main(String[] args) {
                    ChainFilter chain = new ChainFilter();
                    ArrayList<Filter> filters = new ArrayList<Filter>();
                    Filter f1 = new Filter1();
                    Filter f2 = new Filter2();
                    filters.add(f1);
                    filters.add(f2);
                    chain.setFilters(filters);
                    chain.doFilter();
          }

}



콘솔 :

서버 자원 실행전에 필터1 실행부
서버 자원 실행전에 필터2 실행부
서버 자원을 호출한다.
서버 자원 실행후에 필터2 실행부
서버 자원 실행후에 필터1 실행부



Filter 예제
다음은 모든 요청에 대해 ServletRequest 의 setCharacterEncoding("UTF-8"); 가 먼저 수행되도록 하려 한다.
아래와 같이 CharsetFilter.java 파일을 작성한다.


/WEB-INF/src/net/java_school/filter/CharsetFilter.java


package net.java_school.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class CharsetFilter implements Filter {

          private String charset = null;
         
          @Override
          public void init(FilterConfig config) throws ServletException {
                    this.charset = config.getInitParameter("charset");
          }
         
          @Override
          public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
                    throws IOException, ServletException {
                   
                    if (req.getCharacterEncoding() == null) {
                              req.setCharacterEncoding(charset);
                              chain.doFilter(req,resp);
                    }
          }

          @Override
          public void destroy() {
                    //반납할 자원이 있다면 작성한다.
          }

}


/WEB-INF/src/net/java_school/filter/ 폴더로 이동해서 아래와 같이 컴파일한다.
javac -d C:/www/myapp/WEB-INF/classes ^
-cp "C:/Program Files/Apache Software Foundation/Tomcat 7.0/lib/servlet-api.jar" ^
CharsetFilter.java


다음 web.xml 파일을 열고 아래를 추가한다. 이때 기존 엘리먼트와의 순서에 주의한다.
context-param 엘리먼트와 listener 엘리먼트 사이에 아래 코드가 위치해야 한다.


web.xml


<filter>
   <filter-name>CharsetFilter</filter-name>
   <filter-class>net.java_school.filter.CharsetFilter</filter-class>
   <init-param>
      <param-name>charset</param-name>
      <param-value>UTF-8</param-value>
   </init-param>
</filter>

<filter-mapping>
   <filter-name>CharsetFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>


테스트
위에서 실행했던 회원가입 예제의 RegisterServlet.java 소스에서
req.setCharacterEncoding( "UTF-8" );을 주석처리 한 후 RegisterServlet 서블릿을 재컴파일한다.
http://localhost:8989/example/join.jsp를 방문하여 아이디에 한글을 입력하고 좋아하는 운동을 선택한 후 전송을 클릭한다.
이때 RegisterServlet 이 사용자가 입력한 한글 아이디 값을 제대로 출력하는지 테스트한다.


소스 설명
필터의 초기화 파라미터를 읽기 위해서는 FilterConfig의 getInitParameter()메소드나 getInitParameters()메소드를 이용한다.
filter-mapping엘리먼트를 이용해서 URL패턴이나 서블릿 이름으로 이들 자원에 앞서 필터링 작업을 수행할 필터를 정의한다.
필터는 배치 정의자에 나와 있는 순으로 FilterChain에 추가된다.
이때 서블릿 이름과 매핑된 필터는 URL 패턴에 매칭되는 필터 다음에 추가된다.
필터 클래스 코드내에서 FilterChain.doFilter() 메소드를 이용하면 FilterChain의 다음 아이템을 호출하게 된다.

 

 

 

참고
http://docs.oracle.com/javaee/7/api/index.html?overview-summary.html
http://www.mkyong.com/servlet/a-simple-httpsessionlistener-example-active-sessions-counter/
http://commons.apache.org/proper/commons-fileupload/download_fileupload.cgi
http://commons.apache.org/proper/commons-io/download_io.cgi
http://commons.apache.org/proper/commons-fileupload/using.html
http://www.albumbang.com/board/board_view.jsp?board_name=free&no=292
http://www.docjar.com/docs/api/javax/servlet/GenericServlet.html
http://www.java-school.net/jsp/Servlet

 

 

도움이 되셨다면 공감을 부탁드립니다. ^^