프로젝트를 접하다 보면 화면에 출력 하거나 긴 문자열을 간추려서 사용 할 때, 문자열에서 Html 태그를 제거 해야 하는 경우가 있는대 유용한 라이브러리가 있다.



사용하기 편리한 유틸, Spring Framework 사용 시 지원 해주는 유틸 등 제공 된다.


pom.xml 에 추가 하자.


<dependency>
  <groupId>com.lyncode</groupId>
  <artifactId>jtwig-functions</artifactId>
  <version>2.0.1</version>
</dependency>


사용 방법


import static com.lyncode.jtwig.functions.util.HtmlUtils.stripTags;

public class StripTagsExample {
    public static void main(String... args) {
        String result = stripTags("<!-- <a href='test'></a>--><a>Test</a>", "");
        // Produced result: Test
    }
}

참고 사이트


개발을 할 때 기존 기능을 보존 하면서, 확장 해야 하는 경우가 있다. 원본을 어쩔 수 없이 수정 해야 할 때는 피할 수 없지만, 확장만 한다면, Proxy 를 사용 하는 경우 쉽게 확장이 가능 하다. 사용 방법을 알아 보자.


Proxy 는 기존 에 만들었던 Class 를 새로운 Class로 포장 했다고 생각 하면 쉽다.


public class Packing {

    private Target target;

    public Packing(Target target) {
        this.target = target;
    }

    // doing...

}


위 소스를 주목 하자. 포장지 에 기존 Class 를 담았다. 하지만 먼가가 부족 하다. 함수를 연결해줄 고리가 필요 하다. 그것이 InvocationHandler 이다. java.lang.reflect 패키지에서 제공 하고 있다.


포장지에 연결 고리를 달아보자.


public class Packing implements InvocationHandler {

    // doing...

    @Override
    public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable {
        return paramMethod.invoke(paramObject, paramArrayOfObject);
    }

}


invoke 라는 함수를 볼 수 있다. 포장지와, 기존 Class 를 연결 시켜주는 역할을 한다.


paramObject: targetClass (기존 Class) 를 넣는다.
paramMethod: targetClass 에서 실행 하고 싶은 메소드를 넣는다.
paramArrayOfObject: targetClass 에서 실행하고 싶은 메소드의 파라미터 값을 넣는다.


위 3가지 설정을 끝내면 준비 작업은 끝났다. 3가지 정보에 따라 포장지에서 기존 Class 함수를 실행 하게 된다. 샘플을 만들어 보자.


Github: https://github.com/whitelife/whitelife-sample/tree/master/src/main/java/kr/co/whitelife/sample/proxy


  • Target
public class Target {

    public void doProcess() {
        System.out.println("doProcess.....");
    }

    public String doStringProcess(String string) {
        System.out.println("doStringProcess.....");
        return string;
    }

}

  • Packing
public class Packing implements InvocationHandler {

    private Target target;

    public Packing(Target target) {
        this.target = target;
    }

    public void doProcess() {
        try {
            System.out.println("Packing doProcess start.....");

            this.invoke(this.target, this.target.getClass().getDeclaredMethod("doProcess", new Class[]{}), new Object[]{});

            System.out.println("Packing doProcess end.....");
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    public void doStringProcess(String string) {
        try {
            System.out.println("Packing doStringProcess start.....");

            String result = (String) this.invoke(this.target, this.target.getClass().getDeclaredMethod("doStringProcess", new Class[]{String.class}), new Object[]{string});
            System.out.println(result);

            System.out.println("Packing doStringProcess end.....");
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable {
        return paramMethod.invoke(paramObject, paramArrayOfObject);
    }
}

  • ProxyTest
public class ProxyTest {

    public static void main(String[] args) {
        Packing packing = new Packing(new Target());

        packing.doProcess();
        System.out.println();

        packing.doStringProcess("doString");
    }

}


샘플 작성이 끝났다면 실행 해 보자. 아래와 같은 출력을 볼 수 있다.


Packing doProcess start.....
doProcess.....
Packing doProcess end.....

Packing doStringProcess start.....
doStringProcess.....
doString
Packing doStringProcess end.....


여기 까지 왔다면, 성공 한 것이다. 기존에 각자 사용하던 Class를 Target 으로 생각하고, 포장지를 만들어서 사용하면 된다.


만약 Spring Framework 를 사용하는 경우, 직접 구현할 필요 없이 Spring AOP Around 기능을 사용하면 쉽게 처리가 가능 할 것이다. 위 샘플 코드와 비슷 하기 때문이다.


대표 적인 로그 라이브러리라 볼 수 있다. 로그를 출력 하는 Appenders 설정을 찾을 수 없기 때문에 발생 한다.


보통의 경우 classpath, Web application 의 경우 /WEB-INF/ 하위 경로 를 사용하기도 한다.


xml, properties 두 가지 스타일로 설정을 하면 된다. xml 로 설정을 해보자.
해당 파일을 추가 하자.


  • log4j.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

    <!-- Appenders -->
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %5p %c{1} - %m%n" />
        </layout>
    </appender>

    <!-- Application Loggers -->
    <logger name="kr.co.whitelife.spring">
        <level value="debug" />
    </logger>

    <!-- Root Logger -->
    <root>
        <priority value="debug" />
        <appender-ref ref="console" />
    </root>

</log4j:configuration>


Global 로그 설정 부분은 출력 할 appender 설정, 로그 레벨 설정 부분이다. Root Logger 부분을 참고 하고, 특정 package log 만 설정을 원하는 경우, Application Loggers 부분을 참고 하자.


Logger logger = LoggerFactory.getLogger(Sample.class);
logger.debug("I am {}", "boy");


테스트 코드를 실행 해보자. log 를 확인 할 수 있다.


'Java' 카테고리의 다른 글

Java Html 태그 제거 하기  (0) 2014.10.29
Java Proxy 사용하기  (0) 2014.10.13
Java Clone 사용 하기  (0) 2014.10.10
Java String.split(String regex) 사용 시 '|' 파싱 이 안되는 경우  (1) 2014.10.08
Java HashMap Key 정렬 하기  (0) 2014.09.24

원본 데이터의 보존이 중요 시 되는 경우, 사용하게 된다. 복제 기능에 대해서는 Cloneable 라는 Interface 를 구현 해야 한다.


public abstract interface Cloneable {
}


Object 클래스에 정의 되어 있다. 용도에 따라 얼마든지 Override 해서 사용 하면 된다.


protected native Object clone() throws CloneNotSupportedException;


접근 제한자가 protected 로 되어 있기 때문에, 외부에서는 접근이 불가능 하므로 아래와 같이 선언 해야 외부에서 사용이 가능 하다. Cloneable Interface 를 implements 하지 않았을 경우, java.lang.CloneNotSupportedException 이 발생 하니 주의 하자.


public class Model implements Cloneable {

    // Doing...

    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}


메소드 정의가 끝났다면, clone() 을 호출 하여 사용 하도록 하자.


문자열 구분자 | 사용 하는 경우 파싱이 안될 때가 있다.


System.out.println(Arrays.toString("test|test|test|".split("|")));


각 각 하나씩 파싱이 되어 원하는 결과를 얻을 수 없다.


[, t, e, s, t, |, t, e, s, t, |, t, e, s, t]


수정 후 실행 해 보자.


System.out.println(Arrays.toString("test|test|test|".split("\\|")));


원하는 결과를 얻을 수 있다.


[test, test, test]


설명


HashMap의 경우에는 무작위로 Key, Value 가 들어간다. 활용 용도에 따라서 정렬이 필요 할수 있는대 TreeMap 에서 제공 하고 있다. 기본적인 Map에 정렬 기능이 추가 되있다고 생각하면 되겠다.


Sample


private Map<String, String> testMaps() {
    Map<String, String> testMaps = new HashMap<String, String>();

    // Doing (PUT)

    testMaps = new TreeMap<String, String>(actionCodes);

    logger.debug(testMaps.toString()); // ASC
    logger.debug(testMaps.descendingMap().toString()); // DESC

    return testMaps;
}


TreeMap은 객체 생성 시 Map을 생성자 인자로 받게 되어 있다. 오름차순 정렬이 기본이다. 내림차순을 이용하려면 descendingMap() 메소드를 사용하자.


참고사이트


자바 프로젝트를 하다보면 공통 라이브러리 작성을 하여 개발 시간을 단축 시키고, 생산성을 높이고는 한다. 

하지만 때로는 그 코드가 발목을 잡을 때도 있다. 


  • 동일한 기능을 가진 함수가 중복 사용 시
  • 동일한 기능을 다른 개발자가 개발 하는 경우

위 경우만 성립 된다 하더라도, 요구사항 변경 시 넓은 부분을 각각 수정해 줘야 한다. 시간도 많이 들어가고, 불편 하지 않을까...

공통으로 많이 생각하는 부분은, 이미 누군가가 라이브러리로 만들어 놓지 않았을까 하는 생각이 든다.
예전에는 만들려고만 했지만, 요즘은 Search 를 먼저 하고, 없는 경우에 만들려고 한다. 분명 잘하는 개발자가 만들어서 배포가 되어있을 것이다. 

Web Application 개발 시에도, Spring Framework(http://spring.io/) 을 너무 당연하게 사용하고 있다. 

개발 시 자주 대면하는 유효성 체크, Collection, 날짜, 문자 등등 있다. 이런 걸 좀더 편하게 사용할 수 있게 Google 에서 배포되는 라이브러리가 있다.

Google Guava 라는 녀석 이다.


Maven Dependency
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>14.0.1</version>
</dependency>

위 링크를 참조하거나, Maven 을 사용한다면 pom.xml 에 추가 하자.


사용해 봤던 기능에 대한 예시이다.


유효성 체크에 대한 Sample

int value = 20;
        
Preconditions.checkArgument(value > 25, "error value: %d", value);


Multimap 대한 Sample

하나의 Key로 다수의 Value를 가질 수 있다.

ListMultimap<String, String> multimap = ArrayListMultimap.create();

multimap.put("baby", "a");
multimap.put("baby", "b");
multimap.put("baby", "c");
multimap.put("baby", "d");

System.out.println(multimap.toString());


위 예시와 다른 기능은 API 를 참고 하자.


참고 사이트: http://blog.outsider.ne.kr/710




Reflection 이란 단어는 반사, 방향, 거울 등에 비친 상 이라는 의미를 담고 있습니다.


개발 당시 많이 사용 하는 new 연산자는 Class의 규칙에 의해서 객체화 된 Instance를 생성 합니다.  


Test test = new Test();


Instance를 거울에 비추어 보면 반대로 Class의 정보를 얻을 수 있습니다. 

거울의 역할을 하는 메소드는 Object Class의 getClass 라고 볼 수 있습니다.


아래는 실제 사용 예 입니다.


public final native Class<?> getClass();


이제 반대로 확인이 가능합니다. Class의 구성 요소인 생성자, 메소드, 전역 변수 등 정보가 필요 할 떄 사용되는 패키지가 java.lang.reflect 입니다. 


패키지를 이용하면 변수의 값을 바꿀 수도 있고, 메소드를 실행도 할 수 있습니다. 막강한 기능인 패키지를 기반으로한 Instance 생성도 가능 합니다. 


Java Framework 중 하나인 Spring의 중요한 기능인 DI(Dependency Injection) 가 동작이 가능한 것도 java.lang.Class<T>, java.lang.reflect 가 존재하기 때문 입니다.


DI 추가 설명 - http://blog.whitelife.co.kr/13


Spring ApplicationContent.xml 중 일부

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
    http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.1.xsd">

 <bean class="kr.whitelife.controller.Wcontroller">
  <constructor-arg ref="service"/>
 </bean>
 
 <bean id="service" class="kr.whitelife.service.Wservice">
  <property name="dao" ref="dao"></property>
 </bean>
 
 <bean id="dao" class="kr.whitelife.dao.Wdao"/>
 
</beans>


<bean> 태그의 class 속성에 들어가 있는 정보를 가지고 package 기반의 Instance 생성을 할 수 있습니다.


간단한 예제 입니다.

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

public class Reflective {

  public void message(String choice) throws NoSuchMethodException, NullPointerException, SecurityException, IllegalAccessException, InvocationTargetException {
    this.getClass().getDeclaredMethod(choice, (Class<?>[]) null).invoke(this);
  }

  public void hello() {
    System.out.println("hello");
  }

  public static void main(String[] args) {
    try {
      Reflective r = (Reflective) Class.forName("Reflective").newInstance();
      r.message("hello");
    } catch (NoSuchMethodException e) {
      System.out.println(e);
    } catch (NullPointerException e) {
      System.out.println(e);
    } catch (ClassNotFoundException e) {
      System.out.println(e);
    } catch (SecurityException e) {
      System.out.println(e);
    } catch (InstantiationException e) {
      System.out.println(e);
    } catch (IllegalAccessException e) {
      System.out.println(e);
    } catch (InvocationTargetException e) {
      System.out.println(e);
    }
  }
}

위 기능은 Package를 이용한 객체 생성, method 정보를 찾아서 실행 까지 합니다.  기존에 new 연산자를 이용해서 Instance 생성을 했다면 reflect 패키지 활용 시 문자열을 활용하여 동적으로 Instance 제어가 된다.


Java의 상속, 인터페이스와 더불어 reflection과 결합하게 된다면 훨씬 개발이 요구사항에 따른 대응이 수월해지고, Class 단위의 분기를 했다면, Method 단위의 분기도 수월하게 처리가 가능 합니다.


※ 참고 사이트

  Java API - http://docs.oracle.com/javase/6/docs/api/

  이미지 URL - http://www.clker.com/cliparts/e/K/g/G/i/j/blue-stick-man-reflect-hi.png


'Java' 카테고리의 다른 글

Java HashMap Key 정렬 하기  (0) 2014.09.24
Google Guava 라이브러리 사용하기  (0) 2014.07.24
Java Console 게시판 만들기 - 4  (4) 2013.11.13
Java Console 게시판 만들기 - 3  (0) 2013.11.12
Java Console 게시판 만들기 - 2  (0) 2013.11.11

+ Recent posts