JAVA 리플렉션(Reflection) , 어노테이션(Annotation)

JAVA
홍윤's avatar
Aug 16, 2024
JAVA 리플렉션(Reflection) , 어노테이션(Annotation)
 
💡
리플렉션(Reflection)은 프로그램이 실행 중에 자신의 구조를 검사하거나 수정할 수 있게 해주는 프로그래밍 기법입니다. 주로 **자바(Java)**에서 많이 사용되며, 클래스, 메서드, 필드 등의 정보를 런타임에 동적으로 접근할 수 있는 기능을 제공합니다.

주요 기능

  1. 클래스 정보 조회:
      • 클래스의 이름, 패키지, 부모 클래스, 구현된 인터페이스 등을 런타임에 조회할 수 있습니다.
      • 예시: Class<?> clazz = Class.forName("com.example.MyClass");
  1. 메서드, 필드, 생성자 접근:
      • 클래스의 메서드, 필드, 생성자 등의 정보에 접근하고, 이를 호출하거나 값을 수정할 수 있습니다.
      • 예시: Method method = clazz.getMethod("myMethod");
  1. 객체 생성:
      • 리플렉션을 사용하여 클래스의 인스턴스를 동적으로 생성할 수 있습니다.
      • 예시: Object obj = clazz.newInstance();
  1. 접근 제어 무시:
      • private와 같은 접근 제어자를 무시하고, 비공개 필드나 메서드에도 접근할 수 있습니다.
      • 예시: field.setAccessible(true);

      장점

      • 유연성: 런타임에 객체의 메타데이터를 활용할 수 있어, 동적인 기능 구현이 가능합니다.
      • 프레임워크: Spring, Hibernate와 같은 많은 프레임워크가 리플렉션을 사용하여 애플리케이션의 다양한 객체를 관리합니다.

      단점

      • 성능: 리플렉션은 일반적인 메서드 호출보다 성능이 떨어질 수 있습니다.
      • 안전성: 컴파일 타임에 타입 체크가 불가능하므로, 런타임 에러가 발생할 가능성이 높아집니다.
      • 복잡성: 코드의 가독성이 떨어질 수 있으며, 유지보수가 어려워질 수 있습니다.
      리플렉션은 강력한 도구지만, 필요한 경우에만 신중하게 사용하는 것이 좋습니다. 잘못 사용하면 성능 저하나 유지보수 문제를 야기할 수 있기 때문입니다.

 
💡
어노테이션(Annotation)은 자바에서 코드에 메타데이터를 추가하는 방법입니다. 어노테이션은 클래스, 메서드, 필드, 파라미터, 변수 등 여러 위치에 붙일 수 있으며, 주석과 비슷하게 보이지만, 실행 시점에 영향을 미치거나 컴파일러에게 특정한 정보를 제공하는 역할을 합니다.

어노테이션의 주요 기능

  1. 메타데이터 제공:
      • 어노테이션은 코드에 추가적인 정보를 제공합니다. 예를 들어, 클래스가 특정 기능을 수행하도록 지시하거나, 컴파일러에게 경고를 무시하도록 지시할 수 있습니다.
  1. 컴파일러 힌트:
      • 컴파일러가 코드를 분석할 때 어노테이션을 보고 특정 작업을 수행하거나, 경고를 표시할 수 있습니다. 예를 들어, @Override는 컴파일러에게 해당 메서드가 부모 클래스의 메서드를 오버라이드하는지 확인하도록 합니다.
  1. 런타임 처리:
      • 일부 어노테이션은 런타임에 반영되어, 리플렉션(Reflection) 등을 통해 실행 중에 어노테이션 정보를 참조할 수 있습니다. 이 기능은 프레임워크나 라이브러리에서 많이 사용됩니다.
  1. 코드 문서화:
      • 어노테이션은 코드에 대한 문서 역할도 할 수 있습니다. 예를 들어, @Deprecated 어노테이션은 해당 메서드나 클래스가 더 이상 사용되지 않음을 나타내어, 다른 개발자에게 해당 코드를 사용하지 말라는 경고를 제공합니다.

      결론

      어노테이션은 자바에서 코드에 메타데이터를 추가하고, 컴파일러나 런타임에 영향을 미치도록 하는 중요한 도구입니다. 자바의 다양한 표준 어노테이션뿐만 아니라, 커스텀 어노테이션을 만들어 특정 기능을 구현할 수 있습니다. 이를 통해 코드의 가독성과 유지보수성을 높일 수 있으며, 특히 프레임워크 개발과 테스트 자동화에서 널리 사용됩니다.
 
notion image
 
RequestMapping
package ex02; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) // 실행시에 메소드 위에만 사용 할 수 있다. @Retention(RetentionPolicy.RUNTIME) // 해당 깃발은 실행 후에 확인 한다. //@Retention(RetentionPolicy.SOURCE) 컴파일이 실행 전에 확인 하고 한다. public @interface RequestMapping { String uri(); }
  • @Target(ElementType.METHOD)
-어노테이션이 메소드에만 적용될 수 있음을 나타낸다. 즉 ‘@RequestMapping’ 어노테이션은 클래스, 필드, 변수 등이 아닌 메소드 위에만 사용 할 수 있다.
 
  • @Retention(RetentionPolicy.RUNTIME)
-어노테이션이 런타임(RUNTIME) 동안 유지됨을 나타낸다. 즉 어노테이션 프로그램이 실행 되는 동안 리플렉션(Reflection)을 통해 접근을 할 수 있다.
 
  • @Retention(RetentionPolicy.SOURCE)
-위에 있는 런타임과 다르게 소스 코드에서 만 유지되고, 컴파일 후에는 사라진다는 의미이다.
 
  • @interface
-어노테이션을 정의하는 자바의 키워드다. 이 키워드를 사용하여 ‘RequestMapping’이라는 새로운 어노테이션을 정의하고 있다.
 
  • String uri(): 어노테이션에 ‘uri’라는 속성을 정의한다. 속성은 문자열 값을 가지고 ‘@RequestMapping’ 어노테이션을 사용 할때 URI 정보를 제공하는 데 사용한다.
 
UserController
package ex02; public class UserController { // /board/save-form @RequestMapping(uri = "/board/save-form") public void saveForm() { System.out.println("save-for 호출됨"); } @RequestMapping(uri = "/userinfo") public void userinfo() { System.out.println("userinfo 호출됨"); } @RequestMapping(uri = "/login") public void login() { System.out.println("login 호출됨"); } @RequestMapping(uri = "/join") public void join() { System.out.println("join 호출됨"); } }
  • 클래스 선언
-’UserController’라는 클래스다 클래스는 웹 애플리케이션의 컨트롤러 역활을 한다.
 
  • 메소드 및 ‘@RequestMapping’ 어노테이션
-여러 메소드가 정의되어 있으면, 각 메서드는 ‘@RequestMapping’ 어노테이션을 통해 특정 URI와 매핑되어 있습니다.
 
  • ‘userinfo’ 메소드
-’/userinfo’ uri로 요청이 들어오면 실행된다. 이 메소드는 콘솔에 "userinfo 호출됨"이라는 메시지를 출력합니다.
 
실제 웹 애플리케이션 환경에서는 프레임워크(예: Spring MVC)가 이러한 URI와 메서드 간의 매핑을 처리하고, 사용자가 특정 경로로 접근할 때 해당 메서드를 실행하도록 합니다. 이 코드는 그런 매핑 방식을 시뮬레이션하는 예시입니다.
 
app
package ex02; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class app { public static void main(String[] args) throws InvocationTargetException, IllegalAccessException { String path = "/board/save-form"; UserController uc = new UserController(); System.out.println(uc.getClass()); System.out.println("======================================"); Method[] methods = uc.getClass().getDeclaredMethods(); // 리턴을 해준다. for (Method mt : methods) { RequestMapping anno = mt.getAnnotation(RequestMapping.class); RequestMapping rm = (RequestMapping) anno; if(rm == null) break; if (rm.uri().equals(path)) { mt.invoke(uc); } } } }
  • ‘path’ 변수와 ‘UserController ‘ 객체 생성
-’path’변수: 특정 URI를 지정한다. URI와 매핑된 메서드를 동적으로 호출하기 위해 사용된다.
-’UserController’ 객체: 리플렉션을 통해 메소드를 호출할 대상 객체를 생성한다.
 
  • 클래스와 메소드 정보 출력
-클래스 정보 출력: ‘uc.getClass()’를 사용해 ‘UserController’ 클래스의 정보를 출력한다.
 
  • 클래스의 메소드들 가져오기
-’getDeclaredMethods()’: ‘UserController’ 클래스에 선언된 모든 메소드를 ‘Method’ 배열로 반환한다. 이 배열을 통해 각 메소드에 접근 할 수 있다.
 
  • 메소드 순회 및 어노테이션 확인
-for-each 루프: ‘methods’ 배열을 순회하면서 각 메소드에 접근한다.
-어노테이션 가져오기: ‘mt.getAnnotation(RequsetMapping.class)’를 사용해 해당 메소드에 ‘@ReqeustMapping’ 어노테이션이 붙어 있는지 확인한다. 어노테이션이 없으면 ‘rm’은 ‘null’이 되므로 루프를 종료한다.
 
  • URI 비교: 어노테이션의 ‘uri()’ 메소드를 호출해 ‘path’ 변수와 비교한다. ‘uri()’ 값이 ‘path’와 일치하는지 확인한다.
 
  • 메소드 실행: ‘mt.invoke(uc)’를 사용해 일치하는 메소드를 ‘UserController’ 객체에서 호출한다. 이때 ‘uc’ 객체를 메소드의 호출 대상으로 사용합니다.
Share article

Uni