새로운 블로그로 이전하였습니다!

로직을 구현하던 중 특정 클래스 파일 목록을 뿌려줘야 하는 기능이 필요하여

기존엔 특정 Package 안에 있는 클래스 파일을 가져오는 방식으로 구현하였었는데

모든 Class를 가져오기 때문에 보안에 취약할 수도 있고, 프로젝트 패키지의 구조가 바뀌거나, 원치 않는 클래스 파일이 지정해줬던 Package 안에 생성될 경우 Exception 이 날 수 있어서 다른 방법을 고안하였습니다.

interface 를 하나 만들어서 해당 interface를 상속받은 클래스를 가져오는 방식으로 구현하여서 위의 문제점을 해결하였는데 꽤나 유용한 것 같아서 두가지 방법을 작성해보고자 합니다.

우선 아래와 같은 interface와 구현체가 존재한다고 가정합니다.

 

interface

print 인터페이스를 구현한 클래스를 가져올 예정입니다.

public interface Print {
	String printString();
}

VO

print 인터페이스를 구현한 class 입니다.

public class Hello implements Print {
	@Override
    public void printString() {
    	System.out.println("Hello");
    }
}
public class World implements Print {
	@Override
    public void printString() {
    	System.out.println("World");
    }
}

 

ServiceLoader

정의

Java SE 6 부터 등장한 클래스로 특정 인터페이스를 구현한 클래스를 검색하고 로드하는 기능을 제공합니다.

Context classpath 를 사용하여 구현체를 찾아서 캐시에 보관하고 이를 사용할 수 있습니다.

사용방법

classpath 지정

java/resoruce/META-INF/services 폴더에 interface의 classpath 로 만든 파일을 만들어줍니다.

파일 안에 구현체의 classpath를 작성해줍니다.

com.kdh.learnspi.vo.Hello
com.kdh.learnspi.vo.World

 

클래스 로드

import java.util.*;

ServiceLoader<Print> loader = ServiceLoader.load(Print.class);
for( Print print : loader ) {
	print.printString();
}

결과

Hello
World

 

Reflections 라이브러리

정의

자바에 기본 내장되어있는 Reflection API 보다 더 편리하고 다양한 검색을 제공하는 외부 라이브러리 입니다.

사용방법

라이브러리 추가

https://mvnrepository.com/artifact/org.reflections/reflections/0.10.2

해당 사이트에서 프로젝트의 환경에 맞게 의존성 추가 혹은 jar 파일을 받아줍니다.

클래스 리플렉션

Reflections의 함수 중 getSubTypesOf 메서드를 이용하여 해당 클래스를 상속받은 클래스를 검색합니다.

Set<Class<? extends Print>> list = new Reflections().getSubTypesOf(Print.class);

for(Class<? extends Print> print : list) {
	print.printString();
}

결과

Hello
World

 

비교

ServiceLoader는 java 에 내장되어 있는 클래스라서 외부 라이브러리에 의존할 필요가 없지만 classpath를 따로 지정해주어야 한다는 단점이 있습니다.

Reflections 는 선언과 동시에 바로 사용이 가능하여 편리했지만 외부 라이브러리에 의존한다는 점과 속도가 ServiceLoader에 비해 매우 느린 것을 볼 수 있었습니다.

  • ServiceLoader : 11ms
  • Reflections : 553ms

처음 애플리케이션이 시작될 때 한번 로딩되고 끝날 경우는 Reflections를 사용해도 무방할 것 같지만

해당 로직을 계속 불러와야할 경우 Reflections의 사용은 다소 어려울 것 같아 보입니다.

'Back-End > Java & Spring' 카테고리의 다른 글

[JAVA-Spring] Lombok 동작 원리  (0) 2022.08.17
[JAVA] 제네릭 표현  (0) 2022.08.17
[JAVA-Spring] 가변적인 Parameter 처리  (0) 2022.08.02
[JAVA] Log4j  (0) 2022.08.02
[JAVA] JNDI( Java Naming and Directory Interface )  (0) 2022.08.02
복사했습니다!