[ Spring ] Spring Bean Configration File을 JAVA로 만들기

duck-ach·2022년 10월 29일
0

Spring

목록 보기
7/16

Spring Bean Configration File


원래 Spring Bean Configuration File을 만들 때 xml형식으로 Bean에 id를 주고, property에 속성(value)값을 주어서 데이터에 setter injection을 해주었다.

원래 Spring Bean Configuration File 형식

이것을 이제 xml문서가 아닌 Java의 코드로 구현하는 방법을 알아보겠다.


JAVA로 Spring Bean Configuration File하고 하는일이 같도록 하는 방법

Annotation

  • xml 설정이 너무 길어짐에 따라 그 대안으로 나타났다.
  • Class/Method/Field에 Annotation을 달아 그 자체로 설정이 가능하도록 한다.(단, xml이 우선순위가 더 높다)
  • @Annotation을 사용해서 데이터를 설정해준다.
  • 스프링(Spring)이 코드를 줄일 때 @애너테이션을 사용해서 줄인다. (일종의 약속)
  • Annotation은 사전적의미로 '주석'이라는 뜻이다.
  • Java에서 Annotation은 코드 사이에 주석처럼 쓰이며 특별한 의미, 기능을 수행하도록 하는 기술이다. (프로그램에게 추가적인 정보를 제공하는 메타데이터라고 볼 수 있다.)

Annotation 종류

@Configuration : 선언해주면 Spring Bean Configuration File 하고 하는일이 같아진다.
@Bean : Bean을 만드는 method (메소드 이름이 Bean의 이름(id), 반환타입이 Bean의 타입(class))

@Configuration
public class SpringBeanConfig {
	@Bean
	public Song song1() {
		Song song = new Song();
		song.setTitle("오르트구름");
		song.setGenre("인디");
		return song;
	}
}

Annotation 실행

  1. 어노테이션을 정의한다.
  2. 클래스에 어노테이션을 배치한다.
  3. 코드가 실행되는 중에 Reflection을 이용하여 추가 정보를 획득하여 기능을 실시한다.

Reflection이란?

프로그램이 실행 중에 자신의 구조와 동작을 검사하고, 조사하고, 수정하는 것이다.
Reflection을 이용하면 Annotation 지정만으로 원하는 클래스를 주입할 수 있다.


Java에 만든 SpringBeanConfigrationFile을 xml에서 불러오기

  • Annotation은 기본적으로 활성화되지 않기 때문에 xml에 명시적인 활성화 작업이 필요하다.
  • IDE에서 지원하며 체크하면 자동으로 추가가 된다.

Legacy Project를 생성하면 자동으로 생성되는 servlet-context.xml파일에서 진행해주면 된다.

아래 Namespaces를 눌러주면 여러가지 체크 칸이 나온다.
그 중 context - http:www.springframework.org/schema/context를 선택해주고, source로 돌아오면된다.

그 중 context 라고 적힌 태그들을 만들여 주면된다.


@Autowired Annotation과 @Qualifier Annotation

@Autowired

  • 해당 변수 및 메서드에 스프링이 관리하는 Bean을 자동으로 매핑해주는 개념이다.
  • 변수, Setter, Constructor, 일반 method에 적용이 가능하다.
  • <property>, <contructor-arg>태그와 동일한 역할을 한다.
  • @Autowired는 주로 타입(Type)을 이용해 주입하므로 동일한 Bean타입의 객체가 여러개 있을 경우 Bean을 찾기위해 @Qualifier 어노테이션을 함께 사용해야 한다.

@Qualifier

  • 유일한 빈 구별 : @autowired로 연결한 Bean목록에서 유일한 Bean을 구별한다. (@Qualifier(value="빈이름")
  • 타입 지정 : 연결할 빈의 값 타입을 지정한다. @Qualifier("serviceName")
    연결할 빈의 타입을 지정한 경우 springbeans.xml에 선언한 이름을 작성해 준다.
@Autowired
@Qualifier("hiService")

springbeans.xml

<bean id="hiService" class="spring.service.HiService"></bean>

context에 속해있는 component-scan, annotation-config, annotation-driven 차이점

context:component-scan

특정 패키지 안의 클래스들을 scan하고, Annotation을 확인한 후 bean인스턴스를 생성한다.

  • @Component @Controller @Service @Reposittory 등의 Annotation이 존재해야 bean을 생성할 수 있다.
  • 장점 : @Auttowired@Qualifier Annotation을 인식할 수 있다.
  • component-scan을 선언했다면 context:annotation-config를 선언할 필요가 없다.

context:annotation-config

ApplicationContext안에 이미 등록된 bean들의 Annotation을 활성화 하기 위해 사용된다.

  • bean들이 XML로 등록됐는지 혹은 패키지 스캐닝을 통해 등록됐는지는 중요하지 않다.
  • 이미 Spring Context에 의해 생성되어 저장된 bean들에 대해서 @Auttowired@Qualifier Annotation을 인식할 수 있다.
  • component-scan 또한 같은 일을 할 수 있는데, context:annotation-config는 bean을 등록하는 작업을 하지 않는다. (즉, bean을 등록하기 위해 패키지 안의 클래스를 스캔할 수 없다.)

mvc:annotation-driven

스프링 MVC Component들을 그것의 디폴트 설정을 가지고 활성화하기 위해 사용된다.

  • SpringMVC가 @Controller에 요청을 보내기 위해 필요한 HandlerMapping과 HandlerAdapter를 bean으로 등록한다. (등록된 bean에 의해 요청 url과 컨트롤러를 매칭할 수 있다.)
  • @Controller컨트롤러 에서는 @RequestMapping, @ExceptionHandler 등과 같은 주석을 통해 해당 기능을 사용할 수 있도록 한다. (근본적으로 @Controller없이 아무것도 못한다.)
  • bean을 생성하기 위해 xml 파일에 context:component-scan을 명시하면 이 태그를 포함하지 않아도 MVC 애플리케이션은 작동한다.
  • RequestMappingHandlerMapping
    • 요청 url과 매칭되는 컨트롤러(@Controller)를 검색하는 역할. 즉 요청 url을 보고 어떤 컨트롤러가 처리할지 결정한다.
  • RequestMappingHandlerAdapter
    • 컨트롤러의 실행 결과(요청을 처리한 결과)를 리턴하는 역할. Annotation 기반의 Controller 처리를 위해 반드시 필요하다.

servlet-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<context:component-scan base-package="com.gdu.app01" />
	
	
	
</beans:beans>

위의 beans:beans태그의 속성안에 xmlns:context="http://www.springframework.org/schema/context" 속성이 들어가있는 것을 확인할 수 있다.


예제) 애너테이션을 이용한 코드

우선 자바 객체를 만들어준다. Song객체와 Singer객체 두가지를 만들어보겠다.

Singer

package com.gdu.app1.java01;

public class Singer {

	// field
	private String name;
	private Song song; // 대표곡 1개
	
	// constructor
	public Singer() {
		
	}
	public Singer(String name, Song song) {
		super();
		this.name = name;
		this.song = song;
	}
	
	// getter/setter
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Song getSong() {
		return song;
	}
	public void setSong(Song song) {
		this.song = song;
	}
		
}

Song

public class Song {

	// field
	private String title;
	private String genre;
	
	// default constructor
	public Song() {
		
	}

	// constructor
	public Song(String title, String genre) {
		super();
		this.title = title;
		this.genre = genre;
	}

	// getter/setter
	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getGenre() {
		return genre;
	}

	public void setGenre(String genre) {
		this.genre = genre;
	}
	
}

Spring Bean Configration File.java

이제 SpringBeanConfig를 만들어줄건데, SpringBeanConfigFile.xml이 아닌 Java로 만들어 줄것이다.

@Configuration : 선언해주면 Spring Bean Configuration File 하고 하는일이 같아진다.
@Bean : Bean을 만드는 method (메소드 이름이 Bean의 이름(id), 반환타입이 Bean의 타입(class))

package com.gdu.app1.java01;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringBeanConfig {
	
    
   /*
	 [ 1. 메소드에 이름(id)를 주는 방법 ]
	 	<bean id="song1" class="Song">
	 		<property name="title" value="제목1"/>
	 		<property name="genre" value="장르1"/>
	 	</bean>
	*/
	@Bean
	public Song song1() {
		Song song = new Song();
		song.setTitle("오르트구름");
		song.setGenre("인디");
		return song;
	}
	
    
    
   /*
	 [ 2. bean에 직접 이름(name)을 주는 방법 ]
	 	<bean id="song2" class="Song">
	 		<property name="title" value="제목2"/>
	 		<property name="genre" value="장르2"/>
	 	</bean>
	*/
	@Bean(name="song2")		  // @Bean에 name값을 지정하면 id로 사용된다.
	public Song asdfghjkl() { // 이름(id)을 Bean에 줘버리면 메소드 이름은 아무거나 적어도된다. 
		Song song = new Song();
		song.setTitle("불꽃");
		song.setGenre("힙합");
		return song;
	}
    
    
    
   /*
	 [ 3. 생성자에 값을 주는 방법 ]
	 	<bean id="song3" class="Song">
	 		<constructor-arg value="제목3"/>
	 		<constructor-arg value="장르3"/>
	 	</bean>
	*/
	@Bean
	public Song song3() {
		return new Song("우리서로사랑하지는말자", "힙합");
	}
	
	
	// Question.
	// song1을 가지는 singer1을 만들어 보자.
	// setter injection
	@Bean
	public Singer singer1() {
		Singer singer = new Singer();
		singer.setName("윤하");
		singer.setSong(song1());
		return singer;
	}
	
	
	// song2를 가지는 singer2를 name값으로 만들어 보자.
	// setter injection
	@Bean(name="singer2")
	public Singer 한요한() {
		Singer singer = new Singer();
		singer.setName("한요한");
		singer.setSong(asdfghjkl());
		return singer;
	}
	
	// song3을 가지는 singer3을 만들어보자.
	// constructor injection
	@Bean
	public Singer singer3() {
		return new Singer("기리보이", song3());
	}
	
}

실행을 위한 Main Class를 생성해보자.
Main Class

package com.gdu.app1.java01;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;

public class SpringMain {

	public static void main(String[] args) {
		// 스프링(Spring)이 코드를 줄일 때 @애너테이션을 사용해서 줄인다. (일종의 약속)
		AbstractApplicationContext ctx = new AnnotationConfigApplicationContext(SpringBeanConfig.class);
		Singer s1 = ctx.getBean("singer1", Singer.class);
		System.out.println(s1.getName());
		System.out.println(s1.getSong().getTitle());
		System.out.println(s1.getSong().getGenre());
		
		Singer s2 = ctx.getBean("singer2", Singer.class);
		System.out.println(s2.getName());
		System.out.println(s2.getSong().getTitle());
		System.out.println(s2.getSong().getGenre());
		
		Singer s3 = ctx.getBean("singer3", Singer.class);
		System.out.println(s3.getName());
		System.out.println(s3.getSong().getTitle());
		System.out.println(s3.getSong().getGenre());
	}

}

결과

예제) Collection Framework (List, Set, Map)을 Annotation Java 파일로 만들기

  • 0에서 100사이의 랜덤 정수 5개를 가지는 List를 생성하고,
  • 임의의 상장 3가지를 담은 Set을 생성하고,
  • 주소와 연락처를 담은 Map을 생성하자.

Student Class

package com.gdu.app1.java02;

import java.util.List;
import java.util.Map;
import java.util.Set;

public class Student {
	private List<Integer> scores; // 0 ~ 100 사이의 랜덤 점수 5개
	private Set<String> awards;   // 임의의 상장 3개
	private Map<String, String> contact; // 연락처(Address, Tel)

	public List<Integer> getScores() {
		return scores;
	}

	public void setScores(List<Integer> scores) {
		this.scores = scores;
	}
	
	public Set<String> getAwards() {
		return awards;
	}
	
	public void setAwards(Set<String> awards) {
		this.awards = awards;
	}
	
	public Map<String, String> getContact() {
		return contact;
	}
	
	public void setContact(Map<String, String> contact) {
		this.contact = contact;
	}


}

Spring Bean Configration File.java

package com.gdu.app1.java02;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringBeanConfig {
	@Bean
	public Student stud() { // <bean id="stud" class="Student">
		// List
		List<Integer> scores = new ArrayList<Integer>();
		for(int cnt = 0; cnt < 5; cnt++) {
			scores.add((int)(Math.random() * 101) + 1); // 101개의 난수가 발생 (0~100)
		}
		
		// Set
		Set<String> awards = new HashSet<String>();
		awards.add("개근상");
		awards.add("인기상");
		awards.add("우수상");
		
		// Map
		Map<String, String> contact = new HashMap<String, String>();
		contact.put("address", "서울");
		contact.put("tel", "02-123-2342");
	
		// 객체 데이터 생성
		Student student = new Student();
		student.setScores(scores);
		student.setAwards(awards);
		student.setContact(contact);
		
		return student;
	}
}

Main Class

package com.gdu.app1.java02;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;

public class SpringMain {

	public static void main(String[] args) {
		
		AbstractApplicationContext ctx = new AnnotationConfigApplicationContext(SpringBeanConfig.class);
		Student student = ctx.getBean("stud", Student.class);
		
		System.out.println(student.getScores());
		System.out.println(student.getAwards());
		System.out.println(student.getContact());

	}

}
profile
자몽 허니 블랙티와 아메리카노 사이 그 어딘가

0개의 댓글