본문 바로가기

프레임워크/Spring

리플랙션(Reflection)

@WebServlet("/")
public class DispatcherServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	private String envFileName = "control.properties";
	private Properties env;

	public DispatcherServlet() {
		super();
	}

	@Override
	public void init() throws ServletException {
		super.init();
		env = new Properties();
		ServletContext sc = this.getServletContext();
		// 여기까지 서블릿 context 찾는 객체
		String realPath = sc.getRealPath("WEB-INF\\classes\\com\\my\\env\\" + envFileName);
		System.out.println("in DispatcherServlet의 init(): realPath=" + realPath);
		try {
			env.load(new FileInputStream(realPath)); // 의존성 주입
//			env.getProperty(key); // cf)key에 해당하는 value 꺼내오는 메소드
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 기존 Servlet 다 없애고 DispatcherServlet 혼자서 모든 요청을 처리하도록 수정할 것
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 여기에 넣으면 다른 controller에 이거 다 빼줘도 됨. 코드의 중복 막음. 응답형식은 놔둠.
		response.setHeader("Access-Control-Allow-Origin", "http://192.168.1.19:5500");
		response.setHeader("Access-Control-Allow-Credentials", "true");

		System.out.println("request.getServletPath()=" + request.getServletPath());
		// URL 매핑에 따라 properties파일로 dispatcherServlet에서 활용할 것

		/*
		 * if(request.getServletPath().equals("/productjson")) { ProductJsonController
		 * control = new ProductJsonController(); control.execute(request, response); }
		 * else if(request.getServletPath().equals("/productlistjson")) {
		 * ProductListJsonController control = new ProductListJsonController();
		 * control.execute(request, response); }
		 */
		String className = env.getProperty(request.getServletPath());
		System.out.println(className);
		try {
			Class clazz = Class.forName(className); // 클래스이름에 해당하는 .class파일 찾아서 JVM으로 로드
//			clazz.newInstance();//객체생성작업 아래꺼 사용
			// Object가 반환인데 controller로 다운캐스팅

			Controller controller = (Controller) clazz.getDeclaredConstructor().newInstance();
			// 인터페이스를 구현한 실제 생성된 객체 controller의 오버라이딩된 execute 메서드가 호출
			controller.execute(request, response);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	// controller 조차 싱글톤으로 만드는 메서드
	protected void service2(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		System.out.println("request.getServletPath()=" + request.getServletPath());
		// /signup --> SignupController클래스 찾기 -> 객체생성 -> 객체의 재정의된 execute() 호출
		// /login --> LoginController클래스 찾기 -> 객체생성 -> 객체의 재정의된 execute() 호출
		// /a --> Acontroller
		// if(request.getServletPath().equals("/signup")) {
		// else if 리플랙션 기술이 없으면 a라는 컨트롤러가 추가되면 else if가 하나 더 추가되어야함
		// else if 서비스를 새로 배포도 다시 해야함. 배포 하루종일 걸리고 아무도 못씀
		// 리플랙션을 쓰면 객체지향의 기능을 담고있는 것부터 만들면 재사용성을 높일 수 있다. 결합도를 떨어뜨릴 수 있다.

		// 주입된 클래스를 Reflection 기법으로 찾아 실행
		String className = env.getProperty(request.getServletPath());
		try {
			Class<?> clazz = Class.forName(className);// 클래스이름에 해당하는 .class파일 찾아서 JVM으로 로드

			Controller controller;
			try {
				Method method = clazz.getMethod("getInstance");
				controller = (Controller) method.invoke(null);// static인 getInstance()메서드호출. Controller로 다운캐스팅함. 결합도를
																// 떨어뜨린다.
				// constroller = (SignupController) method.invoke(null); 이렇게 쓰면 login할때는 다른 변수를
				// 선언해야해서
				// 일반화된 인터페이스로 다운캐스팅. 구체화된 클래스 타입으로는 안한다
				// dispatcherServlet에서 사용할 클래스를 자기가 결정하지 않고 외부파일(control.properties)에서 결정해서 주입
				// -> 의존성 주입(dependency injection)
			} catch (NoSuchMethodException e) {
				controller = (Controller) clazz.getDeclaredConstructor().newInstance();
			} // getInstance 있으면 호출받고 아니면 객체생성해서 얻어와라
			String path = controller.execute(request, response);
			if (path != null) { // jsp로 한다하면
				RequestDispatcher rd = request.getRequestDispatcher(path);
				rd.forward(request, response);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

리플랙션 기술을 통해 위에서 아래의 코드블럭 부분으로

/signup --> SignupController클래스 찾기 -> 객체생성 -> 객체의 재정의된 execute() 호출
/login --> LoginController클래스 찾기 -> 객체생성 -> 객체의 재정의된 execute() 호출

위의 주석처리한 과정 처리 가능. 재사용성을 높인 것

String className = env.getProperty(request.getServletPath());
try {
    Class<?> clazz = Class.forName(className);
    Controller controller;
    try {
        Method method = clazz.getMethod("getInstance");
        controller = (Controller) method.invoke(null);
    } catch (NoSuchMethodException e) {
        controller = (Controller) clazz.getDeclaredConstructor().newInstance();
    }
    String path = controller.execute(request, response);
    if (path != null) {
        RequestDispatcher rd = request.getRequestDispatcher(path);
        rd.forward(request, response);
    }
} catch (Exception e) {
    e.printStackTrace();
}

* 의존성 주입

dispatcherServlet에서 사용할 클래스를 자기가 결정하지 않고 외부파일(control.properties)에서 결정해서 주입
-> 의존성 주입(dependency injection)

	@Override
	public void init() throws ServletException {
		super.init();
		env = new Properties();
		ServletContext sc = this.getServletContext();
		// 여기까지 서블릿 context 찾는 객체
		String realPath = sc.getRealPath("WEB-INF\\classes\\com\\my\\env\\" + envFileName);
		System.out.println("in DispatcherServlet의 init(): realPath=" + realPath);
		try {
			env.load(new FileInputStream(realPath)); // 의존성 주입
//			env.getProperty(key); // cf)key에 해당하는 value 꺼내오는 메소드
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

 

'프레임워크 > Spring' 카테고리의 다른 글

라이브러리 dependency로 추가하고 setting 하기  (0) 2023.10.24
Maven Project 생성  (0) 2023.10.24
web module version  (0) 2023.10.24
Filter  (0) 2023.10.24
Listener  (0) 2023.10.24