Getter, Setter와 리플렉션

2024. 2. 2. 20:46프로그래밍

728x90

 

 

Best way of invoking getter by reflection

I need to get the value of a field with a specific annotation, So with reflection I am able to get this Field Object. The problem is that this field will be always private though I know in advance it

stackoverflow.com

자바로 클래스를 작성하다보면, 가장 많이 하는 것 중에 하나가 데이터베이스를 접근 할 때 만드는 객체 클래스가 아닌가 합니다. 이럴때는 보통 get,set 메서드를 사용하고 클래스내의 멤버들 즉, 데이터는 은폐하게 만드는 것이 보통 일반적인 경우 입니다.

 

이런 오브젝트들은 대부분 테이블에서 꺼내오기 때문에 연속성 유지의 측면에서 똑같은 이름을 사용하는 클래스 멤버를 만들고 해당 멤버를 외부에서 꺼내 쓰거나 저장하도록 getter, setter 를 만들어 둡니다. 

이런 클래스를 DTO, Data Transfer Object 라고 한답니다.

 

일반적인 코드는 다음과 같이 될 것입니다.

여기까지만 해도 그럴듯해 보입니다. 그리고 이런 기능을 하기 위해서 우리는 someclass 가 필요 한 상황입니다.

하지만 이 데이터를 엑셀로 만들기 위한 경우를 생각 해보기로 합시다.

 

엑셀을 만들기 위한 멋진(?) 라이브러리도 준비 되어 있다고 하고요

 

 

Java code example to export data from database to Excel file

 

www.codejava.net

 

위에서 소개한 클래스는 테이블 명과 연결 만 있으면, 엑셀을 만들 수 있도록 준비한 클래스 입니다.

자, 그럼 당신의 고객에게 다음과 같은 요청 사항이 들어왔다고 생각 해 봅시다.

 

"테이블에서 00 기간 내에 있는 특정 컬럼 값을 엑셀로 출력해 주세요" 라고 말입니다.

 

그렇다면, 이런 생각이 들 수도 있겠죠

이미 내가 만들어 놓은 클래스와 위의 클래스와 연동만 하면 되지 않을까?

나에게는 이미 someobjList 가 존재하기 때문에 다음과 같이 몇개의 메서드를 바꿀 수 있을 것입니다.

아래는 그래서 엑셀 헤더를 만드는 메서드를 다음과 같이 변형 해서 사용 할 수 있을 것입니다.

private void writeHeaderLine(Map<String, String> columnMap, XSSFSheet sheet) throws SQLException {
	int numberOfColumns = columnMap.size();

	Row headerRow = sheet.createRow(0);

	for(Iterator keyIter = columnMap.keySet().iterator(); keyIter.hasNext();)
	{
		String columnName = keyIter.next();
		Cell headerCell = headerRow.createCell(i - 2);
		headerCell.setCellValue(columnName);
	}
}

 

무슨 의미냐고 물으 실 수 있을 것입니다. 왜냐하면 위 메서드의 정확한 의도를 지금부터 설명 드려야 하니까요...

 

우선 columnMap 안에서는 내가 엑셀 헤더를 만들어야 할 헤더 이름과 설명이 들어 있습니다.

columnMap.put("ColumnA", "사과컬럼");
columnMap.put("ColumnB", "복숭아컬럼");

 

그러고 나면, 사과컬럼과 복숭아컬럼이라는 이름의 헤더으로 하는 엑셀 시트가 하나 만들어 질 것입니다.

그런 다음 값을 다음과 같이 집어 넣을 수 있을 것입니다.

private void writeDataLines(Map<String, String> columnMap, List result, XSSFWorkbook workbook, XSSFSheet sheet)
		throws SQLException {
	int rowCount = 1;

	while (result.next()) {
		Row row = sheet.createRow(rowCount++);
		int colCnt = 0;
		for(Iterator keyIter = columnMap.keySet().iterator(); keyIter.hasNext();)
			Object valueObject = result.get(colCnt++);

			Cell cell = row.createCell(colCnt);
			????

		}

	}
}

 

자, 그럼 이 result  리스트에 담긴 SomeClass 의 객체를 뽑았습니다. 이 클래스의 멤버 변수를 뽑아야 될 것입니다.

다음과 같은 코드가 일반적으로 만들어 질 수 있을 것입니다.

Object valueObject = result.get(colCnt++);
SomeClass sobj = (SomeClass)valueObject;

Object obj = sobj.getColumnA();

Cell cell = row.createCell(colCnt);


if (obj instanceof Boolean)
    cell.setCellValue((Boolean) obj);
else if (obj instanceof Double)
    cell.setCellValue((double) obj);
else if (obj instanceof Float)
    cell.setCellValue((float) obj);
else if (obj instanceof Date) {
    cell.setCellValue((Date) obj);
    formatDateCell(workbook, cell);
} else cell.setCellValue((String) obj);

obj = sobj.getColumnB();
...

 

위의 코드는 우리가 일반화 해서 재 사용하기에는 어려운 코드가 되었습니다.

물론 일반화해서 재 사용하는 코드를 만드는 것이 목적이더라도, 재사용 코드를 만드는 것이 또 다른 문제이기는 하지만요..

여기서는 최초에 설명 했던 이 DTO 에서 엑셀을 뽑아 내는 경우를 좀 일반화 할 수 없을까에 대한 문제에서 고민을 해봤단 정도로 이해 하면 될 것 같습니다.

 

다음과 같은 코드를 최초 설명한 스택오버플로우의 로직으로 다음과 같이 만들어 본다고 합시다.

private Object getGetterValue(Class cls, Object clsObj, String methdName) throws Exception
{
	for (PropertyDescriptor pd : Introspector.getBeanInfo(cls).getPropertyDescriptors()) {
	  if (pd.getReadMethod() != null && !"class".equals(pd.getName()) && pd.getReadMethod().getName().equals(methdName))
		return (pd.getReadMethod().invoke(clsObj));
	}
	
	return null;
}

 

그럼 위의 코드를 다음과 같이 적용해 볼 수도 있을 것입니다.

Object valueObject = result.get(colCnt++);
SomeClass sobj = (SomeClass)valueObject;

Object obj = getGetterValue(SomeClass.class, sobj, "getColumnA" ); //sobj.getColumnA();

Cell cell = row.createCell(colCnt);


if (obj instanceof Boolean)
    cell.setCellValue((Boolean) obj);
else if (obj instanceof Double)
    cell.setCellValue((double) obj);
else if (obj instanceof Float)
    cell.setCellValue((float) obj);
else if (obj instanceof Date) {
    cell.setCellValue((Date) obj);
    formatDateCell(workbook, cell);
} else cell.setCellValue((String) obj);

obj = sobj.getColumnB();
...
728x90