BackEnd/Spring & Springboot Study

자바의 내부 클래스는 스프링 빈이 될 수 있을까?

유튜브를 보다가 토비님이 제목과 같은 영상을 올리셔서 정리하고자 한다.

 

영상을 만드신 이유는 인프런 질문에 @Configuration과 proxyBeanMethods 강의를 실습하던 도중

Bean1, Bean2, Common, MyConfig 클래스들을 Static 클래스로 만들어야 정상적인 테스트가 가능

해당 클래스들을 정적 클래스가 아닌 내부 클래스로 작성하니 적절한 빈 타입을 찾을 수 없다는 에러에서 시작됐다.


개요

@SpringBootApplication
public class SpringbootAcApplication {

    @Component
    static class StaticInnerClass {
        public StaticInnerClass() {
            System.out.println("StaticInnerClass");
        }
    }
    
    public static void main(String[] args) {
        ConfigurableApplicationContext ac = SpringApplication.run(SpringbootAcApplication.class, args);
        System.out.println(ac.getBean(StaticInnerClass.class));
    }
}
com.example.springbootac.SpringbootAcApplication$StaticInnerClass@3bb8aabc
다음과 같이 hashCode가 나온다.
@SpringBootApplication
public class SpringbootAcApplication {

    // Static Nested Class
    @Component
    static class StaticInnerClass {
        
        public StaticInnerClass() {
            // Local Class (Nested Class)
            class LocalClass {
                public LocalClass() {
                    System.out.println("LocalClass");
                }
            }

            // Anonymous Class
            var runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println("Runnable");
                }
            };

            System.out.println("StaticInnerClass");
        }
    }
    
    // (Non-Static Nested) Inner Class
    @Component
    class InnerClass {
        public InnerClass() {
            System.out.println("InnerClass");
        }
    }

    public static void main(String[] args) {
        ConfigurableApplicationContext ac = SpringApplication.run(SpringbootAcApplication.class, args);
        System.out.println(ac.getBean(StaticInnerClass.class));
        System.out.println(ac.getBean(InnerClass.class));
    }

}

두 클래스 모두 Bean으로 등록된 것으로 보인다.

com.example.springbootac.SpringbootAcApplication$StaticInnerClass@29a4f594 com.example.springbootac.SpringbootAcApplication$InnerClass@57b75756

 

Static Nested Class는 Top Level Class로 다른 파일로 클래스를 정의한 것과 거의 99% 같다.

(위치만 다른 클래스 안쪽 - Outer Class와 깊이 관여하고(Grouping)

내부에서만 사용할 것 같을때 사용하고 가독성이 좋아짐)

 

Inner Class

외부 클래스의 오브젝트가 먼저 존재해야 하며, 외부 클래스와 직접적인 연관관계를 맺은 경우 InnerClass의 Object가 만들어짐 (외부 인스턴스(특정 Object)와 연결시켜서만 만들 수 있음)

 

@SpringBootApplication
public class SpringbootAcApplication {
    private String name;

    // Static Nested Class
    @Component
    static class StaticInnerClass {
        void print() {
            System.out.println(name);	// 외부 클래스와 무관계 (멤버 변수 사용 불가)
        }
    }

    // (Non-Static Nested) Inner Class
    @Component
    class InnerClass {
        void print() {
            System.out.println(name); //외부 클래스와 직접적인 관계 (멤버 변수 사용 가능)
        }

    }

    public static void main(String[] args) {
        ConfigurableApplicationContext ac = SpringApplication.run(SpringbootAcApplication.class, args);
        System.out.println(ac.getBean(StaticInnerClass.class));
        System.out.println(ac.getBean(InnerClass.class));
        new StaticInnerClass();
        SpringbootAcApplication outerClass= new SpringbootAcApplication();
        InnerClass innerClass = outerClass.new InnerClass();
    }
}

 

Spring 관점에서

@Component -> 클래스 정보를 이용해서 Bean을 만들어라는 선언

Reflection을 이용해서 클래스 정보를 가져가 클래스로 새로운 인스턴스를 만들어 Bean Object로 써라

InnerClass는 독자적으로 쓸 수 없음 (Bean이 될 수 없음)

 

But @SpringBootApplication은 @ComponentScan 뿐 만 아니라 @Component어노테이션도 있다.

 

1. OuterClass가 Bean으로 등록 되었는지 확인

2. 만약 OuterClass가 Bean으로 등록 되었다면 등록된 OuterClass를 이용해서 outerClass.new InnerClass();

3. InnerClass를 Bean으로 등록

 


정리 

 

 

중첩 클래스 - 클래스 안에 클래스가 정의 된 것

 

중첩 클래스는 두 가지로 구분이 된다.

 

1. static nested class (정적 중첩 클래스)

2. Inner class (내부 클래스)(non-static-nested)

 

InnerClass는 OuterClass의 Object(Instance)가 존재해야 연관관계를 맺고 자신은 Object로 만들 수 있음

 

OuterClass를 Bean으로 등록하고 InnerClass를 Bean으로 등록시키는 것이 실제로 쓸모가 있는가?

 

static nested class의 경우 DI를 통해 OuterClass 정보를 주입하는데 InnerClass는 그 과정이 필요 없음

OuterClass의 내부 정보를 활용하는 InnerClass로 Bean을 분리하여 동작할 필요가 있는 경우 활용할 수 있다. ex) 모니터링

 

BeanFactoryMethod를 가지고 일반 @Component붙은 클래스 안에 정의하는 경우 Bean으로 노출이 되면 OuterClass쪽의 정보를 수집할 수 있음 - 다른 Bean에 모니터링 정보를 제공하는 용도로 Reference Guide에 나와있다고 한다. (Light Mode)


참조

 

https://www.youtube.com/watch?v=2G41JMLh05U 

https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

 

Nested Classes (The Java™ Tutorials > Learning the Java Language > Classes and Objects)

The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See Java Language Changes for a summary of updated

docs.oracle.com