ABOUT ME

Today
Yesterday
Total
  • Spring Annotation 의 내부구조 동작원리
    스터디 2024. 6. 9. 03:44

    Spring Annotation 이란 ?

    Annotation은 Java 5부터 추가된 문법 요소로, 코드 사이에 주석처럼 쓰이며 특별한 의미, 기능을 수행하도록 하는 기술

    Annotation 의 용도

    1. 컴파일러에게 코드 작성 문법 에러를 체크하도록 정보를 제공
    2. 소프트웨어 개발툴이 빌드나 배치시 코드를 자동으로 생성할 수 있도록 정보 제공
    3. 실행시(런타임시)특정 기능을 실행하도록 정보를 제공 → 이걸 젤 많이 사용함

    Myannotation interface

    package myannotation;
    
    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)
    public @interface MyAnnotation {
        String value() default "-";
        int number() default 15;
    
    }
    
    

    Myservice.class

    package myannotation;
    
    public class MyService {
        @MyAnnotation
        public void method1() {
            System.out.println("실행내용1");
        }
    
        @MyAnnotation("*")
        public void method2() {
            System.out.println("실행내용2");
        }
    
        @MyAnnotation(value = "*", number = 20)
        public void method3() {
            System.out.println("실행내용3");
        }
    }
    
    

    Main

    package org.example;
    
    import myannotation.MyAnnotation;
    import myannotation.MyService;
    
    import java.lang.reflect.Method;
    
    public class Main {
        public static void main(String[] args) {
    
            Method[] methodList= MyService.class.getMethods();
    
            for(Method m : methodList) {
                if(m.isAnnotationPresent(MyAnnotation.class)) {
                    System.out.println(m.getName());
                    MyAnnotation annotation=m.getDeclaredAnnotation(MyAnnotation.class);
    
                    String value=annotation.value();
                    int number=annotation.number();
                    for(int i=0;i<number;i++) {
                        System.out.print(value);
                    }
                    System.out.println();
                }
            }
        }
    }
    
    method1
    ---------------
    method2
    ***************
    method3
    ********************
    
    Process finished with exit code 0
    

    그렇다면 Bean이 뭐냐?

    스프링은 스프링컨테이너를 통해 객체를 관리하는데, 스프링 컨테이너에 관리되는 객체를 Bean 이라고 한다.

    Bean 등록방법

    1. Component Scanning
    2. 일일히 자바 설정파일에 등록

    Bean vs Component

    Bean과 Component 어노테이션의 첫 번째 차이는 선언 위치다. **Component**는 class level에 선언되고 **Bean**은 메소드 레벨에 선언된다.

    Component

    @Component
    public class PasswordEncoder {
        public String encode(String seed) {
            return new BCryptPasswordEncoder().encode(seed);
        }
    
        public boolean matches(String seed, String password) {
            return new BCryptPasswordEncoder().matches(seed, password);
        }
    }
    

    해당 클래스는 개발자가 직접 만든 클래스임 → 직접 컨트롤 가능한 클래스는 component로 선언

     

    Bean

    @Configuration
    public class MyBean {
        @Bean
        public PasswordEncoder getPasswordEncoder() {
            return new BCryptPasswordEncoder();
        }
    }
    

    Bean 선언시에 Configuration Annotation이 정의되어 있어야 한다. 그래야 외부 클래스에서 사용이 가능하다

    Bean 내부

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by FernFlower decompiler)
    //
    
    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import org.springframework.core.annotation.AliasFor;
    
    @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Bean {
        @AliasFor("name")
        String[] value() default {};
    
        @AliasFor("value")
        String[] name() default {};
    
        boolean autowireCandidate() default true;
    
        String initMethod() default "";
    
        String destroyMethod() default "(inferred)";
    }
    
    

    Component 쓰는 이유

    • 일일이 Bean 으로 등록하는게 귀찮아서

    의존성 주입 과정

     

    1. Component Scan
    2. Bean 생성 및 의존성 주입

    생성자 주입 : 객체의 생성과 의존관계 주입이 동시에 일어남

    setter, Field 주입 : 객체의 생성 → 의존관계 주입으로 라이프 사이클이 나누어짐

    https://dev-coco.tistory.com/170

    Spring LifeCycle

    1. Spring Container 생성
    2. Spring Bean 생성
    3. 의존관계 주입
    4. 초기화 콜백
    5. 사용
    6. 소멸 전 콜백
    7. 스프링 종료

    Bean이 결국 의존성 주입을 위해 사용되는 거라면, 어떤 방식으로 만드는게 좋을까 ?

    1. 생성자 주입
    @Controller
    public class CocoController {
        private final CocoService cocoService;
    
        public CocoController(CocoService cocoService) {
            this.cocoService = cocoService;
        }
    }
    
    
    1. 필드주입
    @Controller
    public class CocoController {
        @Autowired
        private CocoService cocoService;
    }
    
    

    단점

    • 프레임워크에 의존적이고 객체지향적으로 좋지 않다.
    • 코드가 간결하지만, 외부에서 변경하기 힘들다.
    1. 수정자 주입
    @Controller
    public class CocoController {
        private CocoService cocoService;
    
        @Autowired
        public void setCocoService(CocoService cocoService) {
            this.cocoService = cocoService;
        }
    }
    
    

    단점

    • setMethod가 public으로 정의되어 있어서 수정이 public으로 가능하다

    '스터디' 카테고리의 다른 글

    Try with Resources / Try Finally  (0) 2024.06.10
    JAVA 병렬 프로그래밍  (0) 2024.06.09
    Spring Transactional 뜯어보기  (0) 2024.05.15
    Spring Tomcat 분석  (0) 2024.05.01
    SQL Query 최적화 (Spring JPA, Go gorm)  (1) 2024.03.15
Designed by Tistory.