1. 특정 패키지에 있는 클래스 가져오기
이전에 학습했던 System Class Loader를 이용하면 특정 패키지에 있는 모든 클래스를 가지고 올 수 있다.
private Set<Class<?>> findAllClassesUsingClassLoader(String packageName) {
final InputStream stream = ClassLoader.getSystemClassLoader()
.getResourceAsStream(packageName.replaceAll("[.]", "/"));
final BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(stream)));
return reader.lines()
.filter(line -> line.endsWith(".class"))
.map(line -> getClass(line, packageName))
.collect(Collectors.toSet());
}
private Class<?> getClass(String className, String packageName) {
final String classPath = packageName + "."
+ className.substring(0, className.lastIndexOf('.'));
try {
return Class.forName(classPath);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(String.format("Cannot find class. (%s)", classPath));
}
}
2. 가져온 클래스들의 빈 생성자 찾기
private Set<Controller> findAllControllers(final String controllerPackage) {
final Set<Class<?>> classes = findAllClassesUsingClassLoader(controllerPackage);
return classes.stream()
.filter(clazz -> clazz.getDeclaredConstructors().length != 0)
.filter(this::hasNoArgumentConstructor)
.map(this::findNoArgumentConstructor)
.map(constructor -> {
try {
return (Controller) constructor.newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new IllegalArgumentException(String.format("Cannot invoke constructor. (%s)", constructor.getName()));
}
})
.collect(Collectors.toSet());
}
private boolean hasNoArgumentConstructor(final Class<?> clazz) {
return Arrays.stream(clazz.getDeclaredConstructors())
.anyMatch(constructor -> constructor.getParameterTypes().length == 0);
}
private Constructor<?> findNoArgumentConstructor(final Class<?> clazz) {
return Arrays.stream(clazz.getDeclaredConstructors())
.filter(constructor -> constructor.getParameterTypes().length == 0)
.findAny()
.orElseThrow(() -> new IllegalArgumentException("Cannot find no-argument constructor."));
}
getDeclaredConstructors()
는 해당 클래스가 가지고 있는 모든 생성자를 반환한다. newInstance()
메소드를 통해서 생성자를 통해 인스턴스화 할 수 있다.
참고 자료
https://www.baeldung.com/java-find-all-classes-in-package
https://stackoverflow.com/questions/25054460/java-execute-a-class-method-with-a-specify-annotation
https://stackoverflow.com/questions/27810634/how-can-i-check-a-class-has-no-arguments-constructor
https://www.tutorialspoint.com/java/lang/class_getdeclaredconstructors.htm
https://www.tutorialspoint.com/javareflect/javareflect_constructor_newinstance.htm