-
Notifications
You must be signed in to change notification settings - Fork 0
김현우, 이경민 리플랙션
ReflectionTest의 showClass() 메소드를 구현해 Question 클래스의 모든 필드, 생성자, 메소드에 대한 정보를 출력한다.
-
⚙️ 실행 코드
@Test @DisplayName("테스트1: 리플렉션을 이용해서 클래스와 메소드의 정보를 정확하게 출력해야 한다.") public void showClass() { Class<Question> clazz = Question.class; logger.debug("Classs Name {}", clazz.getName()); for (Field field : clazz.getDeclaredFields()) { logger.debug("Field : {}", field); } for (var constructor : clazz.getDeclaredConstructors()) { logger.debug("Constructor : {}", constructor); } for (Method method : clazz.getDeclaredMethods()) { logger.debug("Method : {}", method); } }
-
🖥️ 결과
14:45:18.742 [main] DEBUG next.reflection.ReflectionTest - Classs Name next.reflection.Question 14:45:18.745 [main] DEBUG next.reflection.ReflectionTest - Field : private long next.reflection.Question.questionId 14:45:18.745 [main] DEBUG next.reflection.ReflectionTest - Field : private java.lang.String next.reflection.Question.writer 14:45:18.745 [main] DEBUG next.reflection.ReflectionTest - Field : private java.lang.String next.reflection.Question.title 14:45:18.745 [main] DEBUG next.reflection.ReflectionTest - Field : private java.lang.String next.reflection.Question.contents 14:45:18.746 [main] DEBUG next.reflection.ReflectionTest - Field : private java.util.Date next.reflection.Question.createdDate 14:45:18.746 [main] DEBUG next.reflection.ReflectionTest - Field : private int next.reflection.Question.countOfComment 14:45:18.746 [main] DEBUG next.reflection.ReflectionTest - Constructor : public next.reflection.Question(java.lang.String,java.lang.String,java.lang.String) 14:45:18.746 [main] DEBUG next.reflection.ReflectionTest - Constructor : public next.reflection.Question(long,java.lang.String,java.lang.String,java.lang.String,java.util.Date,int) 14:45:18.746 [main] DEBUG next.reflection.ReflectionTest - Method : public boolean next.reflection.Question.equals(java.lang.Object) 14:45:18.747 [main] DEBUG next.reflection.ReflectionTest - Method : public java.lang.String next.reflection.Question.toString() 14:45:18.747 [main] DEBUG next.reflection.ReflectionTest - Method : public int next.reflection.Question.hashCode() 14:45:18.747 [main] DEBUG next.reflection.ReflectionTest - Method : public void next.reflection.Question.update(next.reflection.Question) 14:45:18.747 [main] DEBUG next.reflection.ReflectionTest - Method : public java.lang.String next.reflection.Question.getContents() 14:45:18.747 [main] DEBUG next.reflection.ReflectionTest - Method : public java.lang.String next.reflection.Question.getWriter() 14:45:18.747 [main] DEBUG next.reflection.ReflectionTest - Method : public java.lang.String next.reflection.Question.getTitle() 14:45:18.747 [main] DEBUG next.reflection.ReflectionTest - Method : public long next.reflection.Question.getQuestionId() 14:45:18.747 [main] DEBUG next.reflection.ReflectionTest - Method : public java.util.Date next.reflection.Question.getCreatedDate() 14:45:18.747 [main] DEBUG next.reflection.ReflectionTest - Method : public long next.reflection.Question.getTimeFromCreateDate() 14:45:18.747 [main] DEBUG next.reflection.ReflectionTest - Method : public int next.reflection.Question.getCountOfComment()
Junit3에서는 test로 시작하는 메소드를 자동으로 실행한다. 이와 같이 Junit3Test 클래스에서 test로 시작하는 메소드만 Java Reflection을 활용해 실행하도록 구현한다.
-
⚙️ 실행 코드
@Test public void runner() throws Exception { Class<Junit3Test> clazz = Junit3Test.class; for (Method method : clazz.getDeclaredMethods()) { if (method.getName().startsWith("test")) { method.invoke(clazz.getDeclaredConstructor().newInstance()); } } }
-
🖥️ 결과
Running Test1 Running Test2
Junit4에서는 @Test 애노테이션일 설정되어 있는 메소드를 자동으로 실행한다. 이와 같이 Junit4Test 클래스에서 @MyTest 애노테이션으로 설정되어 있는 메소드만 Java Reflection을 활용해 실행하도록 구현한다.
-
⚙️ 실행 코드
@Test public void run() throws Exception { Class clazz = Junit4Test.class; for (Method method : clazz.getDeclaredMethods()) { if (method.isAnnotationPresent(MyTest.class)) { Constructor<Junit4Test> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); method.invoke(clazz.getDeclaredConstructor().newInstance()); } } }
-
🖥️ 결과
Running Test1 Running Test2
-
📚 공부하기
- ❓
.isAnnotationPresent(Class<? extends Annotation>)
가 NPE를 발생하는 경우는 어떤 경우일까? - 매개변수로 들어가는 값이
null
인 경우 NPE가 발생합니다!
- ❓
자바 Reflection API를 활용해 다음 Student 클래스의 name과 age 필드에 값을 할당한 후 getter 메소드를 통해 값을 확인한다.
-
⚙️ 실행 코드
@Test public void privateFieldAccess() throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class<Student> clazz = Student.class; logger.debug(clazz.getName()); Field nameField = clazz.getDeclaredField("name"); Field ageField = clazz.getDeclaredField("age"); nameField.setAccessible(true); ageField.setAccessible(true); Student student = clazz.getConstructor().newInstance(); nameField.set(student, "홍길동"); ageField.set(student, 20); assertThat(student.getName()).isEqualTo("홍길동"); assertThat(student.getAge()).isEqualTo(20); }
-
🖥️ 결과
15:05:29.750 [main] DEBUG next.reflection.ReflectionTest - next.reflection.Student
-
📚 공부하기
-
.set(Object obj, Object value)
으로 값 지정하기- 값을 지정할 객체
obj
가 null인 경우에는 NPE가 발생! - 지정하고자 하는 필드에 대해 접근할 수 없는 경우(ex. private) IllegalAccessException이 발생!
- 필드에 적합한 형식의 인자가 아닌 경우 IllegalArgumentException 발생!
- 값을 지정할 객체
-
-
⚙️ 실행 코드
@Test void constructorAccess() throws Exception { String userName = "홍길동"; int userAge = 30; Class<User> clazz = User.class; Constructor[] constructors = clazz.getDeclaredConstructors(); for (Constructor<User> constructor : constructors) { User user = constructor.newInstance(userName, userAge); assertAll( () -> assertThat(user.getName()).isEqualTo(userName), () -> assertThat(user.getAge()).isEqualTo(userAge) ); } }
-
🖥️ 결과
15:11:56.625 [main] DEBUG next.reflection.ReflectionTest - User : User{name='홍길동', age=30}
-
⚙️ 실행 코드
package next; import java.io.File; import java.io.IOException; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import org.junit.jupiter.api.Test; public class ElapsedTimeTest { @Test public void 지난시간() throws IOException, ClassNotFoundException { for(Class<?> clazz : getClassesForPackage("next")) { for(Method method : clazz.getDeclaredMethods()) { if(method.isAnnotationPresent(ElapsedTime.class)) { elapsedTime(method, clazz); } } } } public void elapsedTime(Method method, Class<?> clazz) { long start = System.currentTimeMillis(); try { method.invoke(clazz.getDeclaredConstructor().newInstance()); } catch (Exception e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("Elapsed Time: " + (end - start)); } public static List<Class<?>> getClassesForPackage(String packageName) throws IOException, ClassNotFoundException { List<Class<?>> classes = new ArrayList<>(); String path = packageName.replace('.', '/'); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Enumeration<URL> resources = classLoader.getResources(path); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); File directory = new File(resource.getFile()); if (directory.exists()) { findClasses(directory, packageName, classes); } } return classes; } private static void findClasses(File directory, String packageName, List<Class<?>> classes) throws ClassNotFoundException { if (!directory.exists()) { return; } File[] files = directory.listFiles(); if (files == null) { return; } for (File file : files) { if (file.isDirectory()) { findClasses(file, packageName + "." + file.getName(), classes); } else if (file.getName().endsWith(".class")) { String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6); classes.add(Class.forName(className)); } } } }
-
🖥️ 설명 및 결과
파일 중에서
@ElaspedTime
어노테이션이 적용된 메서드를 모두 조회하고 조회된 메서드들에 대해서 실행 시간을 측정합니다.
-
⚙️ 실행 코드
package org.example public class Main{ public static void Main(String[] args) { System.out.println("Hello world!"); } }
-
🖥️ 결과
Compiled from "Main.java" public class org.example.Main { public org.example.Main(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #13 // String Hello world! 5: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return }
-
0: aload_0
: 자기 자신 객채를 로드합니다! -
1: invokespecial #1
:Object
클래스의 생성자를 호출하는데 이 때 #1이Method java/lang/Object."<init>":()V
를 가리킵니다. -
4: return
: 메서드를 종료합니다. -
0: getstatic #7
: 상수 풀에서 #7을 스택에 로드합니다. #7은Field java/lang/System.out:Ljava/io/PrintStream;
를 가리킵니다. -
3: ldc #13
: #13 문자열을 스택에 로드합니다. #13은String Hello world!
를 나타냅니다. -
5: invokevirtual #15
: 상수 풀에서 #15 메서드를 호출 및 스택의 문자열을 출력합니다. #15는Method java/io/PrintStream.println:(Ljava/lang/String;)V
를 나타냅니다. -
8: return
: 메서드를 종료합니다.
-