Spring 공부 전 기초

4 minute read



학습목표 : Spring을 이해하기 위해 하드코딩으로 Spring처럼 만들어보기

  • bean.properties 파일을 만들지 않아도 된다.

1. Annotation이란?

  • 프로그램 실행 중 해당 변수에 대해 속성을 설정해주는 역할을 함.(Override, …)
  • 주석에 대한 역할을 하면서 문법으로 체크함.
  • @Override, @WebServlet(value=”.do”) …

1-1. Annotation만들기

1. New-> Annotation (원하는 패키지, 원하는 파일명 )

2. String value(); 메소드 선언

  • @RequestMapping(value=”list.do”) // value()로 “list.do”를 추출할 수 있음
  • String uri();를 선언하면 @RequestMapping(uri=”list.do”)가 됨

3. @Target(ElementType.)

  • Annotation을 어디에 붙일지 설정할 수 있음
  • 종류
    • @Target(ElementType.METHOD) : 메소드 위에 붙일 수 있음
    • @Target(ElementType.TYPE) : 클래스위에 붙일 수 있음
    • @Target(ElementType.FIELD) : 멤버변수 위쪽에 붙일 수 있음

4. @Retention(RetentionPolicy.RUNTIME)

  • Annotation 정보를 언제까지 유지시킬지 지정
  • RUNTIME : 프로그램 실행되는동안까지

5. new Instance한 후, 메소드까지 실행하게 설정

  • DispatcherServlet 에서 @WebServlet("*.do") 주석처리후
  • web.xml설정
  <servlet>
  	<servlet-name>dispatcherServlet</servlet-name>
  	<servlet-class>kr.ac.kopo.framework.DispatcherServlet</servlet-class>
  <init-param>
  	<param-name>controllers</param-name>
  	<param-value>kr.ac.kopo.board.controller.BoardController
  				kr.ac.kopo.login.controller.LoginController</param-value>
  </init-param>
  </servlet>
  
   <servlet-mapping>
  	<servlet-name>dispatcherServlet</servlet-name>
  	<url-pattern>*.do</url-pattern>
  </servlet-mapping>

6. CtrlMethod

  • list.do -> BoardController –> list()
  • 어떤 컨트롤러인지 어떤메소드인지 받는 클래스 (get메소드만 필요)
import java.lang.reflect.Method;

public class CtrlAndMethod {
	
	private Object target;
	//어떤 메소드를 실행하는지 기억
	private Method method;

	public CtrlAndMethod(Object target, Method method) {
		super();
		this.target = target;
		this.method = method;
	}

	/**
	 * @return the target
	 */
	public Object getTarget() {
		return target;
	}

	/**
	 * @return the method
	 */
	public Method getMethod() {
		return method;
	}

}

7. HandlerMapping

  • 어떤 uri가 들어왔을때 어떤 메소드가 실행될지
  • import java.lang.reflect.Method;
package kr.ac.kopo.framework;

import java.lang.reflect.Method;
import java.util.Map;

public class HandlerMapping {
	
	private Map<String, CtrlAndMethod> mappings;
	//uri
	
	public HandlerMapping(String ctrlNames) throws Exception {
		//ctrlName -> kr.ac.kopo.board.controller.BoardController | kr.ac.kopo.login.controller.LoginController
		String[] ctrls = ctrlNames.split("\\|");
		for(String c: ctrls) {
			//System.out.println(c.trim()); | 뒤에 들여쓰기 공백있으니까 trim필요
			Class<?> clz = Class.forName(c.trim());
			//Object target = clz.newInstance(); // jdk 9부터 사용제한.
			Object target = clz.getDeclaredConstructor().newInstance();
			System.out.println(target);
			
			Method[] methods = clz.getMethods(); //사용할 수 있는 메소드 출력 (public만 나온다.)
			//Method[] methods = clz.getDeclaredMethods(); // 자신에 클래스에 선언한것 모두 나옴.(public, private ...)
			
			for(Method method : methods) {
				System.out.println(method);
			}
			System.out.println();
		}
	
	}
	
}
  • DispatcherServlet

	private HandlerMapping mappings; //밖에 선언
       
	/**
	 * @see Servlet#init(ServletConfig)
	 */
	public void init(ServletConfig config) throws ServletException {
		String ctrlNames = config.getInitParameter("controllers");
		System.out.println(ctrlNames);
		
		try {//init는 throws ServletException 이므로 에러남.그래서 try-catch필요
			mappings = new HandlerMapping(ctrlNames);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

8. 원하는 메소드 찾기 invoke

  • @RequestMapping 어노테이션이 붙은것만 찾고싶다.
  • RequestMapping reqAnno = method.getAnnotation(RequestMapping.class);
  • invoke() : Controller가 갖고있는 메소드 정보 동적으로 지정
// method.invoke(target, request, response);

try {
			CtrlAndMethod cam = mappings.getCtrlAndMethod(uri);
			/*
			 * System.out.println("ctrl : " + cam.getTarget());
			 * System.out.println("method : " + cam.getMethod());
			 */
			
			if(cam == null) {
				throw new ServletException("해당 URI는 존재하지 않습니다.");
			}
			
			Object target = cam.getTarget();
			Method method = cam.getMethod();
			
			/*
			 * 누군가 /board/list.do 요청	
			   target = new BoardController();
			   Method method = public String list ( ...req , ...res )....{}
			   String callPage = target.list(request,response);
			   target.method() 는 BoardController의 method()메소드를 사용하겠다는 의미임 우리가원하는것 아님.
			 */
			method.invoke(target, request, response);
}catch (Exception e) {
  e.printStackTrace();
}

9. DispatcherServlet이 공유영역에 객체 등록하게 하기

  • ModelAndView.java클래스 생성 : kr.ac.kopo.framwork패키지안
  • Model : JSP에서 사용한 데이터 저장 객체(request공유영역에 등록한 객체)
  • View : 응답화면(JSP) 정보를 저장.
package kr.ac.kopo.framework;

import java.util.HashMap;
import java.util.Map;

/*
 * Model : JSP에서 사용한 데이터 저장 객체(request공유영역에 등록한 객체)
 * View : 응답화면(JSP) 정보를 저장.
 */
public class ModelAndView {
	
	private String view;
	private Map<String, Object> model; // Object Generic은 안돼?
	//request.setAttribute("key", value)
	
	
	public ModelAndView() {
		model = new HashMap<>();
	}


	/**
	 * @return the view
	 */
	public String getView() {
		return view;
	}


	/**
	 * @param view the view to set
	 */
	public void setView(String view) {
		this.view = view;
	}

	/**
	 * @return the model
	 */
	public Map<String, Object> getModel() {
		return model;
	}

	/**
	 * @param model the model to set
	 */
	public void setModel(Map<String, Object> model) {
		this.model = model;
	}
	
	
	public void addAttribute(String key, Object value) {
		model.put(key,value);
	}
}




2. Filter

  • POST방식으로 파라미터 넘길경우 항상 인코딩해주어야함
  • 번거롭기 때문에 Filter을 Servlet앞에 두어 어떤 게시물이 들어오든 request.setCharacter(“utf-8”);수행하도록 함.
  • 필터클래스 생성 후, web.xml에 등록해야함

  • 방법1
 <filter>
  	<filter-name>encoding</filter-name>
  	<filter-class>kr.ac.kopo.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>encoding</filter-name>
  <url-pattern>*</url-pattern>
</filter-mapping>
package kr.ac.kopo.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 EncodingFilter implements Filter{
	/* POST방식은 항상 인코딩해줘야한다 번거롭다
		Servlet앞에 필터를 세워 request.setCharacterEncoding("utf-8");할지말지 판단.
		
	*/
	
	private String charset;
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out.println("필터처리~");
		request.setCharacterEncoding("utf-8");
		chain.doFilter(request,response);
		
	}
}
  • 방법2
<filter>
  <filter-name>encoding</filter-name>
  <filter-class>kr.ac.kopo.filter.EncodingFilter</filter-class>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>utf-8</param-value>
  </init-param>

</filter>
package kr.ac.kopo.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 EncodingFilter implements Filter{
	/* POST방식은 항상 인코딩해줘야한다 번거롭다
		Servlet앞에 필터를 세워 request.setCharacterEncoding("utf-8");할지말지 판단.
		
	*/
	
	private String charset;
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out.println("필터처리~");
		request.setCharacterEncoding(charset);
		chain.doFilter(request,response);
	}
	
	public void init(FilterConfig filterConfig) throws ServletException{
		charset = filterConfig.getInitParameter("encoding");
	}
}




3. Listener

  • 리스너는 서버가 시작될 때 딱 한번 실행된다.
  • close가 필요하다.
  • 비즈니스 관련된 DAO와 서비스 등을 한번 실행해서 계속 재사용하고 싶을 때 사용
  • Servlet끼리 값을 공유할 수 있는 영역 ( 객체 ) : servletContext
    • ServletContextEvent sce를 통해 가져올 수 있음
  • 만든 객체를 ServletContext 영역에 넣는다.
  • 먼저 인스턴스객체를 모두 만들어놓고 개발자가 가져다 쓸 수 있다.
  • Controller에서 ServletCOntext변수(sc)로 접근할 수 있다.
    • ServletContext sc = request.getServletContext();
      • service = (BoardService) sc.getAttribute(“boardService”);
  • Client 요청이 들어오기전에 만들어져야함
  • destroy가 존재하며, 자원관리를 한다.