728x90
300x250

[JSP] 26. JSP/Servlet - InvocationHandler와 Proxy를 이용하여 관점 구현하기


Spring Framework가 아닌 경우에는 AOP 프로그래밍과 같은 프로그래밍을 할 수 있는지 방법에 대해서 소개하려고 한다.

참고로 AOP는 Spring Framework 안에서만 동작한다.


5단계 관점은 아니어도, "전 단계", "핵심 결과" , "후 단계" 이렇게 구현하는 것은 가능하다.



* 관점 프로그래밍의 시작 배경


이전의 로직 구현 방법에 관한 것이다.



그림 1. 이전의 개발 로직 방법


그림 1의 방법은 현재에도 사용되고 있지만, 흔히 로직 하나 구현하면, 이렇게 매서드 내에 다 넣어보는 것이 일반적인 프로그래밍을 할 때 사용하는 방법이다.



그림 2. 문제 인식


문제는 공통 로직을 가지고도 요구사항이 시점에 따라서 달라질 수도 있다.

이럴 때 문제가 생길 수도 있다.




그림 3. Proxy 탈취방법 - 자바


그림 3처럼 핵심 메서드는 그대로 두고, 시점의 시야만 분리하여 프로그래밍을 하는 방법에 대해서도 생각해볼 수도 있다.

이 개념이 추후 Spring AOP에도 확장되어 적용이 된다. (3단계에서 5단계로 확장됨)




1. 결과


작업할 결과물의 결과이다.



그림 4. Proxy 구현으로 관점 프로그래밍



그림 5. 프로젝트 구성도



2. 프로젝트 생성하기


Maven Project로 작업하면 된다.



그림 6. 프로젝트 생성하기


File->New->Maven Project로 생성할 수 있다.




그림 7. 프로젝트 생성하기


org.apache.maven.archetypes    |  maven-archetype-webapp    | 1.4를 선택한다.

Next를 누른다.




그림 8. 프로젝트 생성하기


Group Id, Artifact Id를 입력한다.

Finish를 누른다.



3. Project의 Build Path, Project Factes


Project 선택한 후, 마우스 오른쪽 버튼을 눌러서 Properties에 들어간다.




그림 9. Build Path 설정하기


JRE System Library 버전을 JavaSE-14로 변경한다.




그림 10. Project Factes


Java 버전을 14버전으로 변경한다.





4. 환경설정 - pom.xml 수정


<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>


  <groupId>com.webedu</groupId>

  <artifactId>proxy</artifactId>

  <version>0.0.1-SNAPSHOT</version>

  <packaging>war</packaging>


  <name>proxy Maven Webapp</name>

  <!-- FIXME change it to the project's website -->

  <url>http://www.example.com</url>


  <properties>

    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <maven.compiler.source>1.7</maven.compiler.source>

    <maven.compiler.target>1.7</maven.compiler.target>

  </properties>


  <dependencies>

    <dependency>

      <groupId>junit</groupId>

      <artifactId>junit</artifactId>

      <version>4.11</version>

      <scope>test</scope>

    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->

<dependency>

    <groupId>javax.servlet</groupId>

    <artifactId>javax.servlet-api</artifactId>

    <version>4.0.1</version>

    <scope>provided</scope>

</dependency>

  </dependencies>


  <build>

    <finalName>proxy</finalName>

    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->

      <plugins>

        <plugin>

          <artifactId>maven-clean-plugin</artifactId>

          <version>3.1.0</version>

        </plugin>

        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->

        <plugin>

          <artifactId>maven-resources-plugin</artifactId>

          <version>3.0.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-compiler-plugin</artifactId>

          <version>3.8.0</version>

        </plugin>

        <plugin>

          <artifactId>maven-surefire-plugin</artifactId>

          <version>2.22.1</version>

        </plugin>

        <plugin>

          <artifactId>maven-war-plugin</artifactId>

          <version>3.2.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-install-plugin</artifactId>

          <version>2.5.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-deploy-plugin</artifactId>

          <version>2.8.2</version>

        </plugin>

      </plugins>

    </pluginManagement>

  </build>

</project>



파일명: pom.xml


[첨부(Attachments)]

pom.zip




5. /src/main/webapp/index.jsp 수정


<%@ page language="java" contentType="text/html; charset=UTF-8"

    pageEncoding="UTF-8"%>

<jsp:forward page="index.do" />


파일명: index.jsp


[첨부(Attachments)]

index.zip



6. /src/main/webapp/WEB-INF/views/index.jsp 만들기


폴더를 만들어줘야 한다. views 폴더

views 폴더 내에 index.jsp 파일도 생성해줘야 한다.


<%@ page language="java" contentType="text/html; charset=UTF-8"

    pageEncoding="UTF-8"%>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>Insert title here</title>

</head>

<body>


</body>

</html>


파일명: index.jsp


[첨부(Attachments)]

views-folder-index.zip




7. Servlet 만들기


패키지 경로: com.example.controller

서블릿 명: FrontController



8. 환경설정 - web.xml 수정


<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"

    version="4.0">


  <display-name>Archetype Created Web Application</display-name>

  <welcome-file-list>

   <welcome-file>index.jsp</welcome-file>

  </welcome-file-list>

  

  <servlet>

   <servlet-name>FrontController</servlet-name>

   <servlet-class>com.example.controller.FrontController</servlet-class>

   <init-param>

<param-name>charset</param-name>

<param-value>UTF-8</param-value>

</init-param>

  </servlet>

  <servlet-mapping>

   <servlet-name>FrontController</servlet-name>

   <url-pattern>*.do</url-pattern>

  </servlet-mapping>

</web-app>



파일명: web.xml


[첨부(Attachments)]

web.zip




9. Interface 만들기(Proxy 작업하려면 필수) - service/Calculator.java


인터페이스를 안 만들고, 클래스로 해버리면 오류 발생한다.


package com.example.service;


public interface Calculator {

public long sum();

}



파일명: Calculator.java


[첨부(Attachments)]

Calculator.zip




10. CalculatorImpl.java 만들기 (Calculator 인터페이스 적용함)


package com.example.service;


public class CalculatorImpl implements Calculator {


private long x;

private long y;

private long z;

public CalculatorImpl(long x, long y, long z) {

this.x = x;

this.y = y;

this.z = z;

}

public long sum() {

long result = x + y + z;

System.out.println("결과:" + result);

return result;

}

}



파일명: CalculatorImpl.java


[첨부(Attachments)]

CalculatorImpl.zip




11. Util - HttpUtil.java


package com.example.util;


import java.io.IOException;

import javax.servlet.RequestDispatcher;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class HttpUtil extends HttpServlet {


private static final long serialVersionUID = 1L;

public static void forward(HttpServletRequest req, HttpServletResponse res,

String path) throws ServletException, IOException {


try {

RequestDispatcher dispatcher = req.getRequestDispatcher(path);

dispatcher.forward(req, res);


}catch(Exception e) {

e.printStackTrace();

}

}

}


파일명: HttpUtil.java


[첨부(Attachments)]

HttpUtil.zip




12. Controller.java - Interface 만들기


package com.example.controller;


import java.io.IOException;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public interface Controller {


public void execute(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException;

}



파일명: Controller.java


[첨부(Attachments)]

Controller.zip



13. FrontController.java - Servlet 수정하기(MVC2 패턴 - FrontController, Command 패턴 적용됨)


지금 작업에서는 실질적으로 Proxy를 다루는 방법에 대해서 정의할 것이다.


package com.example.controller;


import java.io.IOException;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;


import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import com.example.service.Calculator;

import com.example.service.CalculatorImpl;

import com.example.util.HttpUtil;


/**

 * Servlet implementation class FrontController

 */

public class FrontController extends HttpServlet {

private static final long serialVersionUID = 1L;

private String charset = null;

    

protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

doAction(req, res, "GET");

}


protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

doAction(req, res, "POST");

}


protected void doAction(HttpServletRequest req, 

HttpServletResponse res,

String flag) 

throws ServletException, IOException {

ServletConfig sc = this.getServletConfig();

charset = sc.getInitParameter("charset");


req.setAttribute("charset", charset);

req.setCharacterEncoding(charset);

res.setContentType("text/html; charset=" + charset);


String uri = req.getRequestURI();

String conPath = req.getContextPath();

String command = uri.substring(conPath.length());


Controller subController = null;


// GET 전송방식만

if(command.equals("/board/write.do") &&

flag.contentEquals("GET")) {

System.out.println("write - page");

//HttpUtil.forward(req, res, "/WEB-INF/views/board/insert.jsp");

}

// POST 전송방식만

else if(command.equals("/board/write_result.do") && 

flag.contentEquals("POST")){

}

else if (command.equals("/index.do")){


System.out.println("index.do");

System.out.println("----------------");

Calculator cal = new CalculatorImpl(1, 2, 3);

Calculator proxy  = (Calculator) Proxy.newProxyInstance(Calculator.class.getClassLoader(),

new Class[] { Calculator.class }, 

new InvocationHandler() {


@Override

public Object invoke(Object proxy, Method method, Object[] args) 

throws Throwable {

long start = System.currentTimeMillis();

System.out.println("시작:" + start );

Object result = method.invoke(cal, args);

Thread.sleep(200);

long end = System.currentTimeMillis();

System.out.println("종료:" + end );

return result;

}

}

);

long result = proxy.sum();

System.out.printf("proxy:%d\n", result);

HttpUtil.forward(req, res, "/WEB-INF/views/index.jsp");

}

}


}



파일명: FrontController.java


[첨부(Attachments)]

FrontController.zip


파란색 코드가 조금 어려울 것으로 보이나 몇 개 만들면, 마우스 가져다되면, unImplemented.... 이런 메시지가 올라오면서

하나 만들거냐고 뜬다.

그걸 잘 활용해야 코드 작성이 조금 수월할 것이다.




* 맺음글(Conclusion)


매우 간단한 형태로 자바 프록시 사용방법에 대해서 소개하였다.

일반 Java 프로젝트에서도 동일하게 사용가능하다.


공용 패키지를 활용한 기능이기 때문이다.

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;


반응형

+ Recent posts