01. 리플젝션과 어노테이션 정의
리플젝션(Reflection)프로그램이 실행되는 동안 자기 자신을 검사하고 수정할 수 있는 기능. 즉, 컴파일 타임이 아니라 런타임에 클래스와 메서드, 변수 등의 정보를 확인하고 조작할 수 있는 기법이라는것.
어노테이션(Annotation)코드에 추가하는 메타데이터. 주석처럼 보일 수 있지만 프로그램 실행에 영향을 주는 추가정보이다. JVM에게 제공되는 설명서 역할을 하며 이를 확인하여 특정 동작을 수행하게 한다. 스프링 등과 같은 프레임워크에서 코드를 자동으로 실행할 때 사용된다. 설정을 단순화하여 코드 가독성 또한 높일 수 있다.
리플렉션이 필요한 이유
- 런타임에 클래스 정보 확인 가능
- 객체 없이 메서드 실행 가능
- 어노테이션 기반 프로그래밍
⇒ 계속해서 유지보수가 필요하다면 이를 일일히 하지 않고 편리하게 처리할 수 있음
✔계속계속 유지보수가 필요한 경우

package ex01;
import java.util.Scanner;
interface Controller {
void login();
void join();
void logout();
}
// 1. A회사
class Dispatcher {
Controller controller;
public Dispatcher(Controller controller) {
this.controller = controller;
}
public void routing(String path) {
// 패스가 들어올때마다 함수 호출
if (path.equals("/login")) {
controller.login();
} else if (path.equals("/join")) {
controller.join();
} else if (path.equals("/logout")) {
controller.logout();
}
}
}
// 2. B회사 (구매할게 - Controller 구현해서 만들어)
class UserController implements Controller {
@Override
public void login() {
System.out.println("Login call");
}
@Override
public void join() {
System.out.println("Jogin call");
}
@Override
public void logout() {
System.out.println("Logout call");
}
}
public class App {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String path = scanner.nextLine();
UserController uc = new UserController();
Dispatcher ds = new Dispatcher(uc);
ds.routing(path);
}
}B회사에서 기능을 추가할 때마다 A회사에 수정을 요청해야함.
A회사는 B회사를 포함해 Dispatcher를 판매한 모든 회사에서 기능 추가 시 수정해줘야함
⇒ 아주 비효율적/비생산적임
⇒ 리플렉션이 필요함
리플젝션 적용

App
package ex02;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class App {
public static void main(String[] args) {
UserController con = new UserController();
Method[] methods = con.getClass().getMethods();
for (Method method : methods) {
try {
if (method.getName().equals("join")) {
method.invoke(con);
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
// invoke() 찾아내서 호출함!
}
}Dispatcher
package ex02;
import java.lang.reflect.Method;
public class Dispatcher {
UserController con;
public Dispatcher(UserController con) {
this.con = con;
}
public void routing(String path) { // /login
Method[] methods = con.getClass().getMethods();
for (Method method : methods) {
RequestMapping rm = method.getAnnotation(RequestMapping.class);
if (rm == null) continue; // 다음 for문으로 바로 넘어감
if (rm.value().equals(path)) {
try {
method.invoke(con);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}UserController
package ex02;
public class UserController {
@RequestMapping("/login")
public void login() {
System.out.println("login call");
}
@RequestMapping("/join")
public void join() {
System.out.println("join call");
}
@RequestMapping("/logout")
public void logout() {
System.out.println("logout call");
}
@RequestMapping("/userinfo")
public void userinfo() {
System.out.println("userinfo call");
}
}RequestMapping
package ex02;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // 이 이노테이션이 언제 실행되는지 설정할 수 있음
@Target(ElementType.METHOD) //메서드 위에만 붙일 수 있음
public @interface RequestMapping {
String value();
}컴퍼넌트 스캔
Dispatcher
package ex04;
import java.lang.reflect.Method;
public class DispatcherServlet {
UserController con;
public DispatcherServlet(UserController con) {
this.con = con;
}
public void componentScan() {
}
public void routing(String path) { // /login
Method[] methods = con.getClass().getMethods();
for (Method method : methods) {
RequestMapping rm = method.getAnnotation(RequestMapping.class);
if (rm == null) continue; // 다음 for문으로 바로 넘어감
if (rm.value().equals(path)) {
try {
method.invoke(con);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}UserController
package ex04;
@Component
public class UserController {
@RequestMapping("/login")
public void login() {
System.out.println("login call");
}
@RequestMapping("/join")
public void join() {
System.out.println("join call");
}
@RequestMapping("/logout")
public void logout() {
System.out.println("logout call");
}
@RequestMapping("/userinfo")
public void userinfo() {
System.out.println("userinfo call");
}
}Component
package ex04;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) // 클래스 위! Component가 붙은 클래스만
public @interface Component {
}BoardController
package ex04;
@Component
public class BoardController {
@RequestMapping("/write")
public void write() {
System.out.println("write call");
}
@RequestMapping("/delete")
public void delete() {
System.out.println("delete call");
}
}App(ex04 패키지 안에 있는, ex04클래스 안에 있는 모든 파일 출력)
package ex04;
import java.io.File;
import java.net.URL;
public class App {
public static void main(String[] args) {
// 1. @Component가 붙으면 new해서 컬렉션에 담기
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
URL packageUrl = classLoader.getResource("ex04");
File packageDir = new File(packageUrl.getFile());
for (File file : packageDir.listFiles()) {
if (file.getName().endsWith(".class")) {
String className = "ex04." + file.getName().replace(".class", "");
System.out.println(className);
}
}
}
}실행결과

App(Component라는 어노테이션이 붙은 class파일 출력해줘)
package ex04;
import java.io.File;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
public class App {
public static void main(String[] args) {
// 1. @Component가 붙으면 new해서 컬렉션에 담기
Set<Object> instances = new HashSet();
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
URL packageUrl = classLoader.getResource("ex04");
File packageDir = new File(packageUrl.getFile());
for (File file : packageDir.listFiles()) {
if (file.getName().endsWith(".class")) {
String className = "ex04." + file.getName().replace(".class", "");
//System.out.println(className);
try {
Class cls = Class.forName(className);
if (cls.isAnnotationPresent(Component.class)) {
Object instance = cls.getDeclaredConstructor().newInstance();
instances.add(instance);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
} // for 종료
for (Object instance : instances) {
System.out.println(instance.getClass().getName());
}
}
}실행결과

2. 리플젝션과 어노테이션을 활용해 write 메서드 동적호출
DispatcherServlet
package ex04;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
public class DispatcherServlet {
public Set<Object> componentScan(String packageName) {
// 1. @Component가 붙으면 new해서 컬렉션에 담기
Set<Object> instances = new HashSet();
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
URL packageUrl = classLoader.getResource(packageName);
File packageDir = new File(packageUrl.getFile());
for (File file : packageDir.listFiles()) {
if (file.getName().endsWith(".class")) {
String className = packageName + "." + file.getName().replace(".class", "");
//System.out.println(className);
try {
Class cls = Class.forName(className);
if (cls.isAnnotationPresent(Component.class)) {
Object instance = cls.getDeclaredConstructor().newInstance();
instances.add(instance);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
} // for 종료
return instances;
}
public void routing(Set<Object> instances, String path) {
for (Object instance : instances) {
Method[] methods = instance.getClass().getMethods();
for (Method method : methods) {
RequestMapping rm = method.getAnnotation(RequestMapping.class);
if (rm == null) continue; // 다음 for문으로 바로 넘어감
if (rm.value().equals(path)) {
try {
method.invoke(instance);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}
}- 특정 패키지 내의 클래스를 검색
@Component어노테이션이 붙어 있는 클래스의 객체를 생성.
@RequestMapping어노테이션이 있는 메서드를 찾아 실행함.
routing()→ 요청된 경로(path)와 일치하는 메서드를 찾아 실행.
componentScan()→ 패키지에서 특정 어노테이션이 붙은 객체를 자동으로 생성.
App
package ex04;
import java.util.Scanner;
import java.util.Set;
public class App {
public static void main(String[] args) {
// RequestMapping, Component, DispatcherServlet (돈주고 삼 = SpringWeb)
Scanner sc = new Scanner(System.in);
String path = sc.nextLine(); // /write
DispatcherServlet dispatcherServlet = new DispatcherServlet();
Set<Object> instances = dispatcherServlet.componentScan("ex04");
dispatcherServlet.routing(instances, path);
}
}서블릿 역할을 하는 객체를 생성.
ex04 내에 Component가 붙은 클래스를 찾아 객체 생성.
path값 (입력값) 과 일치하는 @RequestMapping(”입력값”)이 붙은 메서드를 찾아 실행.
Share article