WEB개발

[Spring] 주석2 (Annotation)

wooyeon06 2023. 1. 19. 10:24

@Resource, @Autowired, @Inject, @Qulifier, @Configuration, @PropertySource


 

 

 

@Resource, @Autowired, @Inject, @Qulifier

 

  @Resource @Inject @Autowired
의존 Java Javax Spring
사용 가능 위치 - 필드
- 파라미터가 한 개인 setter
- 필드
- 생성자
- setter
- 필드
- 생성자
- setter
Bean 검색
우선순위
이름 -> 타입 타입 -> 이름 타입 -> 이름
Bean 강제 지정 @Resource(name="ID") @Inject
@Named("ID")
@Autowired
@Qualifier("ID")
Bean 없을 경우 예외 발생 예외 발생 @Autowired(required=false) 처리하면 예외 발생 방지

 

1.  @Autowironed

: 필요한 의존 객체의 “타입"에 해당하는 빈을 찾아 주입한다.

  • 생성자
  • setter
  • 필드

 

위의 3가지의 경우에 Autowired를 사용할 수 있다. 그리고 Autowired는 기본값이 true이기 때문에 의존성 주입을 할 대상을 찾지 못한다면 애플리케이션 구동에 실패한다.

	@Autowired
	@Qualifier("sqlSessionFactory")
	private SqlSessionFactory sqlFactory;

 

Constructor Dependency Injection

생성자 주입은 생성자에 의존성 주입을 받고자 하는 field를 나열하는 방법으로, 권고되는 방법의 하나 이다.

 

장점

    • 필수적으로 사용해야 하는 레퍼런스 없이는 인스턴스를 만들지 못하도록 강제함
    • Spring 4.3 이상부터는 생성자가 하나인 경우 @Autowired를 사용하지 않아도 됨
    • Circular Dependency / 순환 참조2 의존성을 알아 차릴 수 있음 
    • 생성자에 점차 많은 의존성이 추가 될 경우 리팩토링 시점을 감지 할 수 있음
    • 의존성 주입 대상 필드를 final로 불편 객체 선언할 수 있음
    • 테스트 코드 작성시 생성자를 통해 의존성 주입이 용이함

단점

  • 어쩔 수 없는 순환 참조는 생성자 주입으로 해결하기 어려움

 

Field Dependency Injection

member field에 @Autowired annotation을 선언하여 주입받는 방법이다.

  • 장점
    • 가장 간단한 선언 방식
  • 단점
    • 의존 관계가 눈에 잘 보이지 않아 추상적이고, 이로 인해 의존성 관계가 과도하게 복잡해질 수 있음
      • 반대로 Constructor injection과 Setter injection은 의존성을 명확하게 커뮤니케이션 함
    • 이는 SRP / 단일 책임 원칙에 반하는 안티패턴
    • DI Container와 강한 결합을 가져 외부 사용이 용이하지 않음
      • 단위 테스트시 의존성 주입이 용이하지 않음
    • 의존성 주입 대상 필드가 final 선언 불가

 

 

Setter Dependency Injection

setter 메소드에 @Autowired annotation을 선언하여 주입받는 방법이다.

(메소드 이름을 setter 대신에 다른 걸로 하여도 주입은 가능하지만 좋은 방법은 아니다) 

  • 장점
    • 의존성이 선택적으로 필요한 경우에 사용
    • 생성자에 모든 의존성을 기술하면 과도하게 복잡해질 수 있는 것을 선택적으로 나눠 주입 할 수 있게 부담을 덜어줌
    • 생성자 주입 방법과 Setter 주입 방법을 적절하게 상황에 맞게 분배하여 사용
  • 단점
    • 의존성 주입 대상 필드가 final 선언 불가

 

2. @Resource 

 

@Autowired와 같은 역할, @Resource 어노테이션은 빈의 이름을 이용해서 주입할 객체를 검색한다. @Resource 어노테이션을 사용하려면 앞서 @Autowired 어노테이션과 마찬가지로 다음의 두가지만 추가하면된다.

 

1. 자동 주입 대상에 @Resource 어노테이션 사용

2. XML설정에 <context:annotation-config />설정 추가

 

@Resource 어노테이션은 빈의 이름을 사용해서 주입할 빈 객체를 찾기 때문에, @Resource 어노테이션의 값으로 빈 객체의 이름을 지정한다.

 

@Resource 어노테이션은 생성자에 적용할 수 없고 필드나 메서드에만 적용할 수 있다.

 

@Autowired와 @Resource의 차이점.

적용 순서는 약간 다르지만, 둘다 타입, 이름 ,@Qualifire 애노테이션을 모두 사용한다.  차이점이라면 @Autowired애노테이션은 required 애노테이션을 사용해서 필수 여부를 지정할 수 있다는 점과 타입과 이름 중 무엇을 먼저 사용하느냐에 대한 것이다.

 

	@Resource
	private SqlSessionFactory sqlFactory;
	@Resource(name="sqlSessionFactory")
	private SqlSessionFactory sqlFactory;

 

3. @inject

 타입이 > 이름으로 찾는데, 그래도 없다면 예외가 발생합니다. 부모 클래스 타입에다가 여러 자식 클래스의 Bean 객체 중 하나를 오버라이딩 시키는 경우 발생할 수 있는 문제입니다. 따라서 @Named 어노테이션을 사용해 정확한 Bean ID를 지정해주는 것이 좋습니다.

 

	@Inject	
	@Named("sqlSessionFactory")
	private SqlSessionFactory sqlFactory;

 

 

만약 setter나 생성자의 파라미터에서 여러 개의 Bean 객체를 주입해준다면 아래와 같이 파라미터 앞에 위치시켜 지정해줄 수 있습니다.

 

 

4. @Qualifier

 클래스의 bean이 여러개일 경우 @Autowried DI가 애매하다. 이 문제는 @Qualifier Annotation에 id를 입력함으로써 주입 될 bean을 지정함으로써 해결할 수 있다. (XML은 위와 동일)

    @Autowired
    @Qualifier("exam")		//XML bean에서 id가 exam인 bean을 주입하라는 뜻
    @Override
    public void setExam(Exam exam) {
        // TODO Auto-generated method stub
        this.exam = exam;
    }

 

또, 위의 코드는 생성자가 생성되고 setExam을 호출하여 exam빈을 넣어주는 순서로 실행된다. 만약 생성자가 실행되는 과정에서 DI주입을 하고 싶으면 필드위에 @Autowired를 써주면 된다.

public class InlineExamConsole implements ExamConsole {
    @Autowired
    @Qualifier("exam")
    private Exam exam;		
	
    public InlineExamConsole() {
		
    }
    public InlineExamConsole(Exam exam) {		
        this.exam = exam;
    }

    @Override
    public void print() {
        System.out.printf("total is %d, avg is %f\n", exam.total(), exam.avg());
    }
	
    @Override
    public void setExam(Exam exam) {
        // TODO Auto-generated method stub
        this.exam = exam;
    }
}

 

 

 

 

 


 

@Configuration, @PropertySource

 

소스 코드를 작성하다 보면, 정적으로 하드코딩된 내용을 쓸때가 있다. 대표적으로 DB의 설정정보가 그렇다. 그래서 DB 설정 정보 클래스를 보면 다음과 같이 코딩되어있다.

@Configuration
@Import(value = {DBConfig.class})
public class AppConfig {
  ...
}

 

@Configuration
public class DBConfig {
  private String driver = "DB 접속 드라이버";
  private String url = "DB 접속 URL";
  private String username = "DB 접속 유저명";
  private String password = "DB 접속 패스워드";
  ...
  @Override
  public String toString() {
    return "DBConfig [driver=" + driver + ", url=" + url + ", username=" + username + ", password="
  	+ password + "]";
  }
  ...
}

 

하지만 이와 같은 코딩은 몇가지 단점이 있다.

1) 먼저, 소스코드에 설정 정보가 있기 때문에, 변경사항이 있으면 빌드부터 다시 해야 한다는 점이다.

2) 또, 소스코드를 공유해야하는 상황일 때, 민감한 설정 정보까지 포함되어 있어서 공유에 어려움이 따른다.

 

 -  @PropertySource

 

스프링 프로퍼티를 사용하는 법은 매우 간단하다. resources 폴더에 dbProperty.properties 파일을 만들고, 스프링 설정 클래스(@Configuration이 적용된 클래스)에 @PropertySource를 적용해주면 된다.

 

[dbProperty.properties 파일]
dbconfig.driver=myDriver
dbconfig.url=myUrl
dbconfig.username=myUsername
dbconfig.password=myPassword
@Configuration
@PropertySource(value = {"dbProperty.properties"})
public class DBConfig {
  ...
}

 

이렇게 하면 스프링이 시작될 때, dbProperty.properties를 불러와서, 스프링 프로퍼티에 해당 데이터를 저장한다.

 

 

 -  @Value

 

이제 DBConfig 클래스의 속성을 dbProperty.properties에 있는 데이터를 사용해서 채워보겠다. 각 속성의 선언문 앞에 @Value("${프로퍼티명}")을 적어주면 된다.

@Configuration
@PropertySource(value = {"dbProperty.properties"})
public class DBConfig {
  @Value("${dbconfig.driver}")
  private String driver;
  @Value("${dbconfig.url}")
  private String url;
  @Value("${dbconfig.username}")
  private String username;
  @Value("${dbconfig.password}")
  private String password;
  ...
}
public static void main(String[] args) {
  AnnotationConfigApplicationContext appContext = new AnnotationConfigApplicationContext(AppConfig.class);
  DBConfig dbConfig = appContext.getBean(DBConfig.class);
  System.out.println(dbConfig);
}

 

스프링부트에서는 @PropertySource없이 defulat가 application.properties이기 때문에
바로 @Value어노테이션으로 값을 가져올 수 있다.

 

 

Spring Framework에서 ${value:} 구문은 Spring Expression Language (SpEL)를 사용하여 프로퍼티 값을 참조할 때 기본값을 설정하는 구문입니다.

 

기본 구문 설명

  • ${propertyName:defaultValue}: propertyName이라는 이름의 프로퍼티가 정의되어 있으면 그 값을 사용하고, 정의되어 있지 않거나 값이 비어 있으면 defaultValue를 사용합니다.
 
@Value("${my.property:default_value}") private String myProperty;
 

${value:}에서 :의 의미는 value가 존재하지 않거나 빈 값일 경우 사용할 기본값을 지정하는 것입니다. 그러나 : 뒤에 아무 것도 없기 때문에 기본값이 빈 문자열로 설정됩니다.

@Value("${my.property:}") private String myProperty;
  • 여기서 my.property가 정의되어 있지 않으면 myProperty 변수는 빈 문자열로 설정됩니다.

 

 

 -  Environment

저장된 스프링 프로퍼티는 다음과 같이 Environment 객체를 통하여 접근할 수도 있다.

 

@Configuration
@PropertySource(value = {"myProperty.properties"})
public class DBConfig {
  @Autowired
  private Environment env;
  
  private String driver = env.getProperty("dbconfig.driver");
  private String url = env.getProperty("dbconfig.url");
  private String username = env.getProperty("dbconfig.username");
  private String password = env.getProperty("dbconfig.password");
  ...
}

 

 

 

 

 

 

 

 

 

 

 


 

@resource - https://articles09.tistory.com/29

@autowired- https://devlog-wjdrbs96.tistory.com/166

https://codevang.tistory.com/256

@Qualifier - https://cjw-awdsd.tistory.com/12