자바에서 코드를 작성 시 모델을 만들다 보면 constructor와 기본 getter/setter.. 그리고 상황에 따라서 builder를 만들어 사용해야 합니다.

그런데 이런 일들을 모두 타이핑 하다 보니 보일플레이트 같은 코드들이 많이도 써야 합니다. 코드 제너레이터들이 있어서 편하게 할 수 있긴 하지만 더 편한 방법이 없을까요?

저같은 경우는 lombok을 사용해서 모델 객체들의 불필요한 보일플레이트 코드들을 줄이고 있습니다. annotation방법으로 사용하기 때문에 사용방법도 간단합니다.



1. @Getter / @Setter

기본적으로 멤버필드들에 대한 getter/setter메소드들을 만들어 줍니다.


2. @AllArgsConstructor / @NoArgsConstructor

멤버필드들이 모두 파라미터로 지정된 생성자와 빈 생성자를 만들어 줍니다.


3. @Builder

모델을 빌더 패턴으로 만들어 줍니다.


4. @ToString

toString의 override된 메소드를 만들어 줍니다.


그 외에도 여러 가지가 있지만 lombok features 에서 더 많은 것을 확인할 수 있습니다.



그럼 AndroidStudio에 적용하는 방법을 알아보도록 하죠.


1. 우선 lombok plugin을 설치해야 합니다.

Preference -> Plugins -> Browse Repositories 에서 lombok을 검색하면 Lombok Plugin이 나옵니다. 이걸 설치해 줍니다.


2. gradle에 lombok적용하기.

"""

provided "org.projectlombok:lombok:1.16.8"

"""


3. 트러블슈팅??

 - package javax.annotation does not exist

annotation does not exist...이걸 해결해 주기 위해서 gradle에 annotation을 provide해줍니다.

"""

provided 'org.glassfish:javax.annotation:10.0-b28'

"""


 - cannot find symbol class ConstructorProperties

lombok.config 설정파일을 추가해 줘야 합니다. 프로젝트와 같은 root에 lombok.config파일을 만들고

'''

lombok.anyConstructor.suppressConstructorProperties = true

'''

와 같은 내용을 추가해 줍니다.

자세한 내용은 Configuration system 에서 확인할 수 있습니다.




이와같은 과정 후에 결과를 보게 된다면

AndroidStudio의 structures view를 보게 되면

이와 같은 결과를 볼 수 있습니다.


이제 lombok을 이용해서 코드 다이어트를!!

Guava: Google Core Libraries for Java

  • Base

 - Objects.equal() : equal 비교시 null 체크를 하지 않아도 된다.

 - Objects.hashCode() : hash코드 생성을 보다 쉽게 만들 수 있다.

 - Objects.toStringHelper() : toString객체를 보다 쉽게 만들 수 잇다.

  -> 일반적인 코드 :

public class Book {
private String title;
private String writer;
private int price;

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getWriter() {
return writer;
}

public void setWriter(String writer) {
this.writer = writer;
}

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.price = price;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

Book book = (Book) o;

if (price != book.price) return false;
if (title != null ? !title.equals(book.title) : book.title != null) return false;
return !(writer != null ? !writer.equals(book.writer) : book.writer != null);

}

@Override
public int hashCode() {
int result = title != null ? title.hashCode() : 0;
result = 31 * result + (writer != null ? writer.hashCode() : 0);
result = 31 * result + price;
return result;
}

@Override
public String toString() {
return "Book{" +
"title='" + title + '\'' +
", writer='" + writer + '\'' +
", price=" + price +
'}';
}
}

  -> Guava를 이용한 코드

public class Book {
private String title;
private String writer;
private int price;

public Book(String title, String writer, int price) {
this.title = title;
this.writer = writer;
this.price = price;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
Preconditions.checkNotNull(title);
this.title = title;
}

public String getWriter() {
return writer;
}

public void setWriter(String writer) {
this.writer = writer;
}

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.price = price;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return Objects.equal(this.title, book.title) && Objects.equal(this.writer, book.writer)
&& this.price == book.price;

}

@Override
public int hashCode() {
return Objects.hashCode(this.title, this.writer, this.price);
}

@Override
public String toString() {
return Objects.toStringHelper(this)
.add("title", title)
.add("writer", writer)
.add("price", price)
.toString();
}
}

작성하다보니 Objects.toStringHelper 가 deprecated가 되었네요.. 이건 MoreObject.toStringHelper()를 이용하면 됩니다.

 - Preconditions : 객체 정보의 null과 같은 정보가 있는지를 미리 확인하여 exception을 낸다.

  -> 일반적인 코드

public void setTitle(String title) {

if(title == null) {
throw new NullPointerException("title must not be null!");
}

this.title = title;
}

  -> Preconditions를 이용

public void setTitle(String title) {
Preconditions.checkNotNull(title);
this.title = title;
}

 - Joiner : Collections객체들이나 array객체들의 정보들을 string으로 변경해준다.

List names = new ArrayList<>();
names.add("test1");
names.add("test2");
names.add("test3");

Joiner.on(", ").join(names); // test1, test2, test3

 - Splitter : Joiner와 반대로 split 캐릭터를 기준으로 Iterator객체로 변환하여 준다.

Iterator names = Splitter.on(",")
.trimResults()
.omitEmptyStrings()
.split("test1, test2, test3").iterator();

names.next(); //test1
names.next(); //test2
names.next(); //test3

  • Collections
 - Collections객체를 생각하면 기본적으로 List, Set, Map등을 생각합니다. 그리고 이런 Collection 객체들을 생성할 때 new 를 통해 객체 생성을 하죠.
기본적으로 List 를 본다면 new ArrayList<Strig>();와 같이 합니다. 하지만 Guava에서는 static factory 생성자를 이용합니다.

Lists.newArrayList();
Sets.newHashSet();
Maps.newHashMap();

 - Immutable Collections : 변경 불가능한 Collection.. 객체들에 대한 의도하지 않은 변경을 막고 Thread-Safe 한 객체를 만들 수 있다.

Set<String> nameSet = Collections.unmodifiableSet(new LinkedHashSet<String>(Arrays.asList("test1", "test2", "test3")));
Set<String> nameSet = ImmutableSet.of("test1", "test2", "test3");

이와 같이 변경불가능 객체를 만들 때 기존 코드보다 간결하게 만들 수 있다. Set 이와에 Map에 대해서도 알아보면.

Map<String, Integer> mapValue1 = new LinkedHashMap<>();
mapValue1.put("test1", 1);
mapValue1.put("test2", 2);
mapValue1.put("test3", 3);
Map<String, Integer> unModifiedMap = Collections.unmodifiableMap(mapValue1);

Map<String, Integer> mapValue2 = ImmutableMap.<String, Integer>builder()
.put("test1", 1).put("test2", 2).put("test3", 3)
.build();

Map<String, Integer> mapValue3 = ImmutableMap.of("test1", 1, "test2", 2, "test3", 3);

이와 Immutable Collection이 있는 것들은
  • ImmutableCollection
  • ImmutableList
  • ImmutableSet
  • ImmutableSortedSet
  • ImmutableMap
  • ImmutableSortedMap
  • ImmutableMultiset
  • ImmutableSortedMultiset
  • ImmutableMultimap
  • ImmutableListMultimap
  • ImmutableSetMultimap
  • ImmutableBiMap
  • ImmutableClassToInstanceMap
  • ImmutableTable
이렇게 존재한다. 기존 java 코드보다는 훨씬 간결한 코드를 이용할 수 있다.
 - BiMap : key에 대한 unique한 값을 보장하는게 아니라 value에 대한 unique 성을 보장한다.
 - MultiSet : element의 중복을 허용한다.
 - MultiMap : Map이 Key의 중복을 허용하지 않지만,  MultiMap은 Key의 중복을 허용한다. MultiMap은 Map<K, Collection<V>>의 특성을 가지고 있다.

  • Functional programming

public interface Function {
@Nullable T apply(@Nullable F input);
}

이와 같이 F를 입력 받아서 T의 결과를 리턴한다.
예를 든다면 마일을 입력받아서 킬로미터로 출력한다.

Function<Integer, Double> calcMileToKillometer = new Function<Integer, Double>() {
@Override
public Double apply(@Nullable Integer input) {
return input * 1.60934;
}
};

double miles = calcMileToKillometer.apply(1);

- filter / transform
 객체에 있는 특정 값들이 속한 객체들만 골라낸 Collection을 만들거나, 특정 값들만 뽑아낸 새로운 Collection을 만들어 낼 수 있다.

List<Book> bookList = ImmutableList.of(new Book("Android", "test", 100), new Book("Java", "test2", 200), new Book("Object", "test3", 300));
List<Book> androidBookList = ImmutableList.copyOf(Collections2.filter(bookList, new Predicate<Book>() {
@Override
public boolean apply(@Nullable Book input) {
return "Android".equals(input.getTitle());
}
}));

List<String> titleList = ImmutableList.copyOf(Collections2.transform(bookList, new Function<Book, String>() {
@Override
public String apply(@Nullable Book input) {
return input.getTitle();
}
}));


우선 제가 자주 사용하는 것들로만 정리..
내부적으로 아직 @Beta annotation이 된 것들도 많아서.. 아직 그것들에 대해서는 적어두진 않았습니다. 


Android 개발할 때 eclipse를 통하여 개발을 많이 했는데, AndroidStudio를 통하여 개발을 해보게 되었습니다.

그중에 Build상태에서는 오류가 나오진 않았지만 Runtime시에 

Excution failed for task ':app:preDexDebug'

이와 같은 오류를 보게 되었습니다. Build는 되었는데 왜 Runtime시에 나타날까에 대한 것도 찾아 보았을 때 추가해준 Library중 하나가 AndroidLibrary가 아닌 Java LIbrary로 된 것이 있었습니다. 그리고 개발중에 사용된 java version이 

Java SE 8u25

버전을 사용했더니 문제가 나타났습니다. gradle빌드에 1.6이라는 옵션을 주었지만 해결되지 않았죠 ㅡㅡ.

혹시나 하는 마음에 

Java SE 7u71/72 

를 설치하고 JDK location을 변경해 주었더니 문제가 없어졌습니다.... 아무래도 java8에서 빌드된 java library가 dex로 변경을 할 수 없는 듯 해 보입니다. 이런식으로 해결은 했지만 그래도 좀 찜찜하네요 ㅡㅡ.



jar 파일을 디컴파일 하면 안에 있는 모든 내용의 소스를 풀어볼 수 있지요.
그래서 중요 코드는 난독화가 필요하고 
proguard를 통해서 코드 난독화를 진행할 수 있습니다.



기본적으로는 위 progard 홈페이지에 가서 필요 내용은 download 와 설명을 볼 수 있습니다.


proguard를 다운받고 나면 
 
bin 디렉토리안에 "proguardgui.bat" 파일이 있습니다.
이것을 통해서 gui에서 간단하게 proguard를 적용할 수 있습니다.

gui에서 직접 선택하여 진행할 수 잇고
적용할 설정 파일을 미리 작성하여 적용할 수도 있습니다.

아래 내용은 간단하게 적용한 설정 파일 내용들입니다.

-injars D:\input.jar
-outjars D:\output.jar

-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService

-keepclasseswithmembernames class * {
    native <methods>;
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}

 

 

http에 request를 보냈는데.. 서버에서 url주소를 redirect해서 보내주는 경우가 있지요. 그럼 개발자가 입력한 주소로는
요청이 안되는 경우가 생깁니다. 이걸 해결하기위해서는 javaj에서 redirect된 주소로 다시 요청을 해줘야 합니다.
방법은

private InputStream openConnectionCheckRedirects(URLConnection c) throws IOException 
{
   boolean redir;
   int redirects = 0;
   InputStream in = null;
   do 
   {
      if (c instanceof HttpURLConnection) 
      {
         ((HttpURLConnection) c).setInstanceFollowRedirects(false);
      }
      in = c.getInputStream(); 
      redir = false; 
      if (c instanceof HttpURLConnection) 
      {
         HttpURLConnection http = (HttpURLConnection) c;
         int stat = http.getResponseCode();
         if (stat >= 300 && stat <= 307 && stat != 306 &&
            stat != HttpURLConnection.HTTP_NOT_MODIFIED) 
         {
            URL base = http.getURL();
            String loc = http.getHeaderField("Location");
            URL target = null;
            if (loc != null) 
            {
               target = new URL(base, loc);
            }
            http.disconnect();
            if (target == null || !(target.getProtocol().equals("http")
               || target.getProtocol().equals("https"))
               || redirects >= 5)
            {
               throw new SecurityException("illegal URL redirect");
            }
            redir = true;
            c = target.openConnection();
            redirects++;
         }
      }
   } 
   while (redir);
   return in;
}

public void makeConnection(URL url){
   URLConnection conn = url.openConnection();
   InputStream is = openConnectionCheckRedirects(conn);

   /* request에 대한 결과 처리 부분*/

   is.close();
}

이런식으로 하면 되겠습니다. 서버에서 제공하는 api가 redirect된 주소로 해서 보내주는 경우가 많기에... 이 방법을 써서 해결하게 되었습니다.




자바로 만들어본 간단한 포트 스캐너...
그냥 요즘 그동안 공부한거 복습하고
포트폴리오 만들고 하다가
자바를 또 건드려보는... 그러다가 심심해서 만들어 본 포트 스캐너...
아이피 입력해서 그 아이피에 사용하는 포트와 사용하지 않는 포트를 출력...


import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
import javax.swing.*;
public class PortScanner implements Runnable, ActionListener{
 //기본
 private JFrame fr = new JFrame("Port Scanner");
    private JButton scanBN = new JButton("스캔");
    private JTextField inputIP = new JTextField("", 18);
    private JLabel ipLB = new JLabel("IP Address : ");
    private DefaultListModel listModel = new DefaultListModel();
    private JList listResult = new JList(listModel);
    private JScrollPane js = new JScrollPane(listResult);
   
    //패널
    private JPanel inputPanel = new JPanel();
   
    //레이아웃
    BorderLayout bLayout = new BorderLayout();
    private int index;
    private String ip;
    public PortScanner(){         
     listResult.setVisibleRowCount(10);
     
     inputPanel.add(ipLB);
     inputPanel.add(inputIP);
     inputPanel.add(scanBN);
     scanBN.addActionListener(this);
     
     fr.getContentPane().add(bLayout.CENTER, js);
     fr.getContentPane().add(bLayout.SOUTH, inputPanel);
     
     fr.setBounds(100, 100, 400, 300);
     fr.setVisible(true);
       
    }
    public void actionPerformed(ActionEvent ae){
        Object obj = ae.getSource();
        if(obj == scanBN){
            ip = inputIP.getText();
            if(ip == null || ip.trim().length()==0){
             inputIP.requestFocusInWindow();
             inputIP.setText("");               
                return;
            }else{
                listModel.insertElementAt(ip,index);
                listResult.setSelectedIndex(index);             
                listResult.ensureIndexIsVisible(index);
                index++;
                Thread t = new Thread(this);
                t.start();               
            }
        }
    }
    public void run(){
        Socket s = null;
        for(int i=1;i<65536;i++){
            try{
                s = new Socket(ip,i);
                listModel.insertElementAt(i+
                        "번 포트가 사용중입니다",index);
                listResult.setSelectedIndex(index);
                listResult.ensureIndexIsVisible(index);
            }catch(IOException e){
             listModel.insertElementAt(i+
                        "번 포트를 사용하지 않습니다.",index);
                listResult.setSelectedIndex(index);
                listResult.ensureIndexIsVisible(index);
            }
        }
    }
    public static void main(String[] args){
        new PortScanner();
    }
}

'tip' 카테고리의 다른 글

linux locale 설정 변경  (0) 2009.01.07
linux man page...  (0) 2009.01.04
Mysql 에러별 대처 방법  (0) 2008.12.29
부팅시 USB키보드가 안먹힐때  (0) 2007.07.27
모니터 사이즈 비교  (0) 2007.07.18

+ Recent posts