IT 강의 정리/윤재성의 스프링 입문

윤재성의 스프링 입문 강의 정리

Nellie Kim 2022. 10. 21. 21:25
728x90

04. 자바 프로젝트 만들기

원래 지금까지 하던대로 자바 프로젝트를 만들어 보자. 

① 클래스가지고 객체를 먼저 생성하고, ② 해당 객체의 주소값을 참조변수에 넣어주고 , ③그 참조변수를 통해서 뭔가 작업을 하는 식으로 보통 개발을 한다.

그런데, 만약에 내가 만든 HelloWorldEn클래스를 HelloWorldKo클래스로 변경을 하고 싶다면 어떻게 해야 할까? 

당연히, 객체를 만들었던 클래스명을 먼저 변경해주고,

객체 생성 코드 변경,  메서드에 매개변수로 넣어줄 데이터타입도 변경해야 한다.

자바프로젝트의 문제점. 밑줄 부분을 모두 수정 해야 한다.

지금까지는, 이렇게 무언가가 변경되었을때 수동으로 모두 수정을 해줘야 했다. 

다루기에 매우 번거롭다는 불편함을 가지고 있고, 유지보수에도 어려움이 있었다.

05. 다형성 사용하기

자바의 객체지향프로그래밍에서 제공하는 다형성 개념을 이용해서 위의 불편함을 해소해보자.

이와같은 인터페이스를 상속받은 구조로 만들어보자.

다형성을 사용하면 아래처럼 좌항은 인터페이스인 HelloWorld 로 두고, 우항만 HelloWorldEn → HelloWorldKo로 변경해주면 된다. 4강의 예제에 비하면 편해졌지만, 다형성 또한 우항을 변경해줘야 한다는 번거로움이 있다.

클래스로 객체를 만드는 방법은 어쩔 수가 없다. 클래스를 정확히 지칭해서 만들어 줘야 하기때문에, 정확한 클래스명을 써서 객체를 만들어야 한다. 수정해야할 객체 생성코드가 100줄이라면, 100줄의 클래스명을 모두 수정해야 한다.

다형성의 문제점. 우항을 수정해야 한다

이러한 불편함을 해소하기 위해 나온 것이 스프링 프레임워크이다.

06. 스프링 프레임워크 사용하기

스프링프레임워크를 적용하면 자바 코드작성부분을 줄일수 있다. 하지만 초기 프로젝트 세팅이 다소 복잡하다.

스프링프레임워크사용은 xml을 이용하는 방법 자바 어노테이션(java4.0~)을 이용하는 방법 2가지로 구분된다.

먼저 고전적인 방식인 xml을 이용하는 방법을 배워보자.

 

Maven 이란?

스프링프레임워크는 개발도구가 제공되지 않는다. 그냥 자바 어플리케이션을 개발할때 스프링에서 만든 자바 라이브러리를 붙여넣고,  그 라이브러리에서 제공되는 클래스를 이용해서 작업을 하는 방식이다. Maven이나 Gradle을 사용해서 라이브러리를 자동으로 다운받아서 세팅한다. 파이썬의 pip와 같은 것이다.

- 자바 프로젝트의 빌드를 자동으로 해주는 도구

- 개발자가 xml에 작성한 프로젝트 정보를 토대로 컴파일하고 라이브러리를 연결하는 등의 작업을 해주는 도구

- Maven서버를 통해 라이브러리를 다운받아 설정하는 작업도 수행

1) pom.xml파일 작성 - 스프링 버전 명시

이 파일에 Maven 프로젝트에 관련된 정보, 스프링프레임워크의 라이브러리 정보를 작성하면 이 라이브러리가 자동으로 다운로드 된다. 중복해서 계속 사용할 것이기 때문에 한번 만들어두고 복사해서 쓰자.

 

먼저, spring.io 사이트에서 LEARN에서 최신 버전(맨위 5.3.23)을 찾는다.

(전에는 스프링프레임워크 jar파일을 직접 다운로드 받을수 있었는데, 지금은 스프링프레임워크의 버전 정보만 제공한다.)

 

Maven Repository사이트(Maven에서 운영하는 라이브러리공식서버)에서 'spring context'검색후 해당 스프링버전을 찾아서 복사한후 pom.xml안의 <dependencies> </dependencies> 안에 붙여넣기.

저장하는 순간 내가 적은 스프링 라이브러리들을 다운로드가 되며, 좌측에 Maven Dependencies탭이 생성된다.

스프링에서 작업할때마다 해당 로그가 출력하게 하는 라이브러리도 추가 (slf4j)

slf4j가 사용할 수 있도록 하는 라이브러리도 추가(logback)

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>kr.co.softcampus</groupId>
	<artifactId>SpringBasic</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<!-- xml에서 사용할 속성들 -->
	<properties>
		<!-- 자바버전 -->
		<java-version>1.8</java-version>
		
		<!-- 스프링 버전 -->
		<org.springframework-version>5.3.23</org.springframework-version>
		<!-- <org.springframework-version>5.2.22.RELEASE</org.springframework-version>  -->
		
		<org.slf4j-version>2.0.3</org.slf4j-version>
		<ch.qos.logback-version>1.4.3</ch.qos.logback-version>
	</properties>


	<!-- 프로젝트에서 사용할 라이브러리 정보 -->
	<dependencies>
		<!-- spring context -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

		<!-- slf4j -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${org.slf4j-version}</version>
		</dependency>

		<!-- logback -->
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${ch.qos.logback-version}</version>
			<exclusions>
				<exclusion>
					<groupId>org.slf4j</groupId>
					<artifactId>slf4j-api</artifactId>
				</exclusion>
			</exclusions>
			<scope>runtime</scope>
		</dependency>
	</dependencies>
</project>

2) beans.xml파일 작성  - 생성할 bean객체 명시

객체를 만들 클래스를 bean에 등록하기. 이때 클래스가 여러개일수 있기 때문에

id속성을 사용하여 이름을 부여해주기. 클래스 명을 바꿀때는 여기 안의 클래스 명만 바꿔주면 된다.

이렇게 한줄만 바꿔주면 된다.  HelloWorldEn → HelloWorldKo  유지보수가 굉장히 간편해졌다!

beans.xml파일 역시 중복해서 계속 사용할 것이기 때문에 한번 만들어두고 복사해서 쓰자.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans.xsd">
						
	<!-- <bean id='hello' class='kr.co.softcampus.beans.HelloWorldEn'/> -->
	<bean id='hello' class='kr.co.softcampus.beans.HelloWorldKo'/>	<!--원하는 클래스 명만 써줄것-->				
</beans>

3) MainClass.java, HelloWorld.java인터페이스, HelloWorldEn.java, HelloWorldKo.java 파일 작성

package kr.co.softcampus.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import kr.co.softcampus.beans.HelloWorld;

public class MainClass {
	public static void main(String[] args) {
		// beans.xml파일을 로딩한다. 괄호안에 경로를 모두 작성해야 한다.
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("kr/co/softcampus/config/beans.xml");
	
		// xml에 정의한 bean 객체의 주소값을 가져온다.
		HelloWorld hello1 = (HelloWorld)ctx.getBean("hello");//형변환 해줘야 함
		callMethod(hello1);
		
		HelloWorld hello2 = ctx.getBean("hello", HelloWorld.class);//위와 동일한 코드. 취향에 맞게 사용하자!
		callMethod(hello2);
		
		ctx.close();
	}
	
	public static void callMethod(HelloWorld hello) {
		hello.sayHello();
	}
}

07. IoC 컨테이너

IoC(Inversion of Control, 제어 역전) 

일반적으로 프로그래밍을 작성할 때 프로그램이 흘러가는 흐름이나 생성되는 객체에 대한 제어권을 개발자가 가지는 것과 달리 프레임워크가 가지는 것을 의미한다.

개발자가 직접 객체 생성에 관련된 코드를 작성하는 것이 아닌, 프레임워크가 사용하는 파일에 작성하면 이를 토대로 프레임워크가 객체를 생성하여 반환하고 코드가 동작하는 순서를 결정하게 된다.

POJO Class(Plain Old Java Object) 

자바 모델이나, 기능, 프레임워크 등에 따르지 않고 홀로 독립적이며 단순한 기능만을 가진 객체들을 의미한다.

자바에서는 이러한 객체들을 Bean이라고 부른다.

POPO(PHP), POCO(닷넷 프레임워크), PODS(C++), POD(Perl) 등

IoC컨테이너의 흐름

- Metadata: xml이나 java코드를 가지고 설정한 정보 (이번 강의에서는 xml만 다룬다)

- java POJO classes: 미리 만들어 놓은 클래스

Spring 컨테이너(IoC컨테이너)가 내가 설정한 Metadata를 읽어서 그 정보를 토대로 만들어 놓은 클래스를 가져와서 객체를 만들어서 반환한다.

IoC 컨테이너의 종류?

1) BeanFactory (옛날버전. 더이상 사용x)

  -클래스를 통해 객체를 생성하고 이를 전달한다.

  -상속 등 객체 간의 관계를 형성하고 관리한다.

  -Bean에 관련된 설정을 위한 xml파일은 즉시 로딩하지만, 객체는 개발자가 요구할 때 생성한다.

  -XmlBeanFactory

2) AppicationContext

  -클래스를 통해 객체를 생성하고 이를 전달한다.

  -상속 등 객체 간의 관계를 형성하고 관리한다.

  -국제화 지원 등 문자열에 관련된 다양한 기능을 제공한다.

  -리스너로 등록되어 있는 Bean이 이벤트를 발생시킬 수 있다.

  -Bean에 관련된 설정을 위한 xml 파일은 즉시 로딩하면서 객체를 미리 생성해서 가지고 있다.

  -ClassPathXmlApplicationContext

  -FileSystemXmlApplicationContext

  -XmlWebApplicationContext

IoC 컨테이너 테스트

bean객체를 정의하는 코드를 가진 xml파일(beans.xml)이 패키지 내부에 있느냐, 외부에 있느냐에 따라서 테스트하는 코드가 달라진다. 비교하며 보자.

package kr.co.softcampus.main;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;

import kr.co.softcampus.beans.TestBean;

public class MainClass {

	public static void main(String[] args) {
		// test1();
		//test2();
		//test3();
		test4();
	}

	//BeanFactory(잘 사용x) - 패키지 내부 
	public static void test1() {
		ClassPathResource res = new ClassPathResource("kr/co/softcampus/config/beans.xml");
		XmlBeanFactory factory = new XmlBeanFactory(res); //이때 객체 생성x
		
		TestBean t1 = factory.getBean("t1", TestBean.class); //이때 객체 생성o
		System.out.println(t1);
		
		TestBean t2 = factory.getBean("t1", TestBean.class); //같은 객체를 불러온다.
		System.out.println(t2);
	}
	
	//BeanFactory - 패키지 외부
	public static void test2() {
		FileSystemResource res = new FileSystemResource("beans.xml");
		XmlBeanFactory factory = new XmlBeanFactory(res); //이때 객체 생성x
		
		TestBean t1 = factory.getBean("t2", TestBean.class); //이때 객체 생성o
		System.out.println(t1);
		
		TestBean t2 = factory.getBean("t2", TestBean.class);  //같은 객체를 불러온다.
		System.out.println(t2);
		
	}
	
	// ApplicationContext - 패키지 내부
	public static void test3() {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("kr/co/softcampus/config/beans.xml");
			//이때 객체 생성. 디폴드가 beans.xml에 정의되어있는 객체들이 자동으로 생성. but 설정을 통해 getBean호출시 객체 생성하도록 할 수 있다.
		
		TestBean t1 = ctx.getBean("t1", TestBean.class);//이미 만들어진 객체의 주소값을 불러온다.
		System.out.println(t1);
		
		TestBean t2 = ctx.getBean("t1", TestBean.class);//같은 객체를 불러온다.
		System.out.println(t2);
		
		ctx.close();
	}
	
	// ApplicationContext - 패키지 외부
	public static void test4() {
		FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext("beans.xml");
		
		TestBean t1 = ctx.getBean("t2", TestBean.class);//이미 만들어진 객체의 주소값을 불러온다.
		System.out.println(t1);
		
		TestBean t2 = ctx.getBean("t2", TestBean.class);//같은 객체를 불러온다.
		System.out.println(t2);
		
		ctx.close();
		
	}	
}

이렇게 스프링 프레임워크는 IoC컨테이너를 이용해 Bean객체들을 관리한다.

08. bean 객체 생성하기

bean 태그의 기본 속성

- class : 객체를 생성하기 위해 사용할 클래스를 지정한다.

- id : Bean 객체를 가져오기 위해 사용하는 이름을 지정한다.

- lazy-init : 싱글톤인 경우 xml을 로딩할 때 객체 생성여부를 설정한다.

   true로 설정시 xml로딩시 객체를 생성하지 않고 객체를 가져올때 생성한다.

- scope : 객체의 범위를 설정한다.

   singleton은 객체를 하나만 생성해서 사용/ prototype은 객체를 가져올 때마다 객체를 생성한다.

beans.xml 파일 작성

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- xml을 로딩할 때 자동으로 객체가 생성된다. -->					
	<!-- 현재까지의 진도상 id속성이 없다면 객체의 주소값을 받아서 사용할 수 없다. 후에 자동주입 배울것. -->
	<bean class='kr.co.softcampus.beans.TestBean'/>
	
	<!-- xml을 로딩할 때 자동으로 객체가 생성된다. -->
	<!-- id 속성에 이름을 부여하면 getBean메서드를 통해 객체의 주소 값을 받아 사용할 수 있다.-->
	<!-- 생성된 객체는 더이상 생성되지 않는다. Singleton. 호출시 마다 계속 같은 객체 주소값을 반환한다는 뜻 -->
	<bean id='test1' class='kr.co.softcampus.beans.TestBean'/>
	
	<!-- lazy-init : true - xml을 로딩할 때 객체가 생성되지 않는다.(생략하면 false) -->
	<!-- getBean 메서드를 호출할 때 객체가 생성되며 singleton이기 때문에 객체는 하나만 생성된다. -->
	<bean id='test2' class='kr.co.softcampus.beans.TestBean' lazy-init="true"/>
	
	<!-- scope : prototype - xml을 로딩할 때 객체가 생성되지 않는다. -->
	<!-- getBean 메서드를 호출할 때마다 새로운 객체를 생성해서 반환한다. singleton아님-->
	<bean id='test3' class='kr.co.softcampus.beans.TestBean' scope ="prototype"/>
	
</beans>

MainClass작성

package kr.co.softcampus.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import kr.co.softcampus.beans.TestBean;

public class MainClass {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("kr/co/softcampus/config/beans.xml");
	
		//id 가 test1인 bean객체의 주소값을 받아온다.
		TestBean t1 = ctx.getBean("test1", TestBean.class);
		System.out.printf("t1 : %s\n", t1);
		
		TestBean t2 = ctx.getBean("test1", TestBean.class);
		System.out.printf("t2 : %s\n", t2);
		
		//id 가 test2인 bean객체의 주소값을 받아온다.
		TestBean t3 = ctx.getBean("test2", TestBean.class);
		System.out.printf("t3 : %s\n", t3);
		
		TestBean t4 = ctx.getBean("test2", TestBean.class);
		System.out.printf("t4 : %s\n", t4);
		
		//id 가 test3인 bean객체의 주소값을 받아온다.
		TestBean t5 = ctx.getBean("test3", TestBean.class);
		System.out.printf("t5 : %s\n", t5);
		
		TestBean t6 = ctx.getBean("test3", TestBean.class);//다른 주소값 생성
		System.out.printf("t6 : %s\n", t6);
		
		ctx.close();
	}
}

09. bean 객체의 생명 주기

- spring의 bean은 Singleton인 경우 xml파일을 로딩할 때 객체가 생성된다.

- Singleton이고 lazy-init 속성이 true일 경우 getBean메서드를 사용할 때 객체가 생성된다.(xml파일을 로딩할때x)

- prototype일 경우 getBean메서드를 사용할 때 매번 새로운 객체가 생성된다.

- IoC컨테이너가 종료 될 때 객체가 소멸 된다. (ctx.close();)

 

MainClass.java

package kr.co.softcampus.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import kr.co.softcampus.beans.TestBean1;
import kr.co.softcampus.beans.TestBean2;

public class MainClass {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("kr/co/softcampus/config/beans.xml");
		
		TestBean1 t1 = ctx.getBean("t1", TestBean1.class);
		System.out.printf("t1: %s\n", t1);
		
		System.out.println("------------------------------------------");
		
		TestBean2 t2 = ctx.getBean("t2", TestBean2.class);
		System.out.printf("t2: %s\n", t2);
		
		System.out.println("------------------------------------------");
		
		ctx.close();
	}
}

TestBean1.java

package kr.co.softcampus.beans;

public class TestBean1 {
	public TestBean1() {
		System.out.println("TestBean1의 생성자입니다");
	}
	
	public void bean1_init() {
		System.out.println("TestBean1의 init 메서드");
	}
	
	public void bean1_destroy() {
		System.out.println("TestBean1의 destroy 메서드");
	}
}

TestBean2.java

package kr.co.softcampus.beans;
public class TestBean2 {
	
	public TestBean2() {
		System.out.println("TestBean2의 생성자");
	}
	
	public void default_init() {
		System.out.println("TestBean2의 default_init");
	}
	
	public void default_destroy() {
		System.out.println("TestBean2의 default_destroy");
	}
}

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans.xsd"
	default-init-method='default_init' default-destroy-method='default_destroy'>
						
	<bean id='t1' class='kr.co.softcampus.beans.TestBean1' lazy-init='true' init-method='bean1_init' destroy-method='bean1_destroy' /> 		
	
	<bean id='t2' class='kr.co.softcampus.beans.TestBean2' lazy-init='true' />			
</beans>

default-init-method, default-destroy-method는 beans태그 안에 써준다.

init-method, destroy-method가 없으면 default-init-method, default-destroy-method를 호출한다.

둘다 있으면 당연히 init-method, destroy-method를 호출한다.

default-init-method, default-destroy-method 둘다 없으면 무시한다.

init-method, destroy-method 둘다 없으면 오류가 발생한다.

10. BeanPostProcessor

TestBeanPostProcessor.java

package kr.co.softcampus.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class TestBeanPostProcessor implements BeanPostProcessor{
	
	// init-method 호출 전
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("before");
		return bean; //bean객체 주소값 반환
	}

	// init-method 호출 전
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("after");
		return bean;
	}
}

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans.xsd">
						
	<bean id='t1' class='kr.co.softcampus.beans.TestBean1' lazy-init='true' init-method='bean1_init' /> 		
	
	<bean id='t2' class='kr.co.softcampus.beans.TestBean2' lazy-init='true' init-method='bean2_init'/>	
	
	<!-- 생성된 bean은 스프링프레임워크에서 직접생성을 한다. bean으로 정의해주기만 하면 된다. getBean으로 받아쓰는것도 아니어서 id도 필요없다. -->
	<bean class='kr.co.softcampus.processor.TestBeanPostProcessor'/>		
</beans>

postProcessBeforeInitialization → init-method → postProcessAfterInitialization 순으로 진행 된다.

 

init-method가 수행되기전의 init호출을 가로채서 다른 메서드를 호출한다. bean객체를 반환하여 작업 할 것을 하기도 한다.

11. 생성자를 통한 주입

의존성 주입(Dependency Injection)

- Bean객체를 생성할 때 Bean객체가 관리할 값이나 객체를 주입하는 것

- Bean객체 생성후 Bean객체가 가질 기본값을 자바코드로 설정하는 것이 아닌 Bean을 정의하는 xml코드에서 정의하는것

 

- value : 기본 자료형 값과 문자열 값을 설정한다.

- ref : 객체를 설정한다.

- type : 저장할 값의 타입을 설정한다.

- index : 저장된 값을 주입할 생성자의 매개변수 인덱스 번호

 

먼저 매개변수가 1개인 자바코드와 생성자주입을 비교해보자.

일반 자바코드에서는 TestBean.java에 int data1를 매개변수로 갖는 생성자를 1개 생성한 후, MainClass.java에서 TestBean t2 = new TestBean(100); 으로 생성자를 호출했다.

 

생성자를 통한 주입은 TestBean.java에 int data1를 매개변수로 갖는 생성자를 1개 생성한 후(동일),

beans.xml에 bean정의한 후 constructor-arg 에 value값을 100으로 지정(생성자주입)해서 

MainClass.java에서 TestBean obj2 = ctx.getBean("obj2", TestBean.class)으로 생성자를 호출했다.

 

이때 주의할 점은 beans.xml에서 value값을 지정할 때 type값을 지정해줘야 한다.

TestBean.java에서 생성자가 int와 double과 String을 매개변수로 갖는 생성자가 각각 있을 경우, beans.xml에서 int보다 double을, double보다 String을 우선으로 인식하기 때문에 int로 넣으려고 했던 값이 double이나 String으로 들어가는 불상사가 생길 수 있기 때문이다!

스프링에서는 String > double > int 순으로 인식한다!!!

TestBean.java
beans.xml
MainClass.java

이번에는 매개변수가 여러개 있는 생성자를 만들어서 자바코드와 생성자주입을 비교해보자.

자바코드에서는 TestBean.java에서 매개변수3개인 생성자를 만들고 MainClass.java에서 new로 매개변수3개 넣어주고 출력한다.

생성자주입에서는 beans.xml에 constructor-arg value= 를 3줄 써준다(생성자주입).  타입도 맞게 써준다.

MainClass.java에서 getBean으로 불러온다.

 

MainClass에서 불러올때 자바코드에서는 매개변수를 순서에 따라 맞게 써줘야 한다. 순서가 다르면 컴파일에러가 난다.  그런데 생성자 주입시에는 순서가 달라도 에러가 나지않고 정상적으로 주입이 된다. 그래도 나중에 헷갈릴 수 있으니 웬만하면 순서에 맞춰서 xml에 적어주자.

매개변수에 동일한 타입을 여러개 가지고있는 생성자가 있을경우 , index='0' 이런식으로 인덱스를 지정해서 순서를 정해줘도 된다.

TestBean.java
beans.xml
MainClass.java

지금까지 정수,실수,문자열을 주입을 해봤다. 이번에는 객체를 주입해보자.

자바코드는 아래와 같이 만드는데

객체생성자주입은 Testbean2 bean안에 DataBean의 bean을 넣어준다. DataBean객체를 보고 Testbean2에 주입해준다. 

DataBean.java
TestBean2.java
beans.xml
MainClass.java

이번에는 이미 만들어진 bean 객체를 주입해보자.

DataBean의 bean을 prototype으로 정의먼저 해주고, 

그 아래에 TestBean2아래에 id가 data_bean인 DataBean인 객체를 2개 매개변수로 넣어준다.

이미 만들어진 객체를 주입할땐 id 대신에 ref라는 값으로 정의해준다.

정리) 생성자주입이란, bean객체가 생성이 될 때 각각의 변수에 어떠한 값을 집어 넣겠다고 bean.xml에 지정해주는 것을 생성자 주입이라고 한다!!!

사용법은 <constructor-arg> 뒤에 

정수,실수,문자열주입 : value / 객체주입 : class / 만들어놓은객체주입 : ref 사용!!!

12. Setter 메서드를 통한 주입

자바코드와 DI비교해보자.

자바코드에서는 TestBean클래스에서 data1을 필드로 주고, 게터세터메서드 생성한후 main클래스에서 obj1.setData1(100);로 세팅한다.

DI에서는 beans.xml에서 <property>에 name값을 주면 set + name값 (세터메소드)를 찾아서 value값을 세팅한다. 타입이 여러개일때는 타입도 지정해주기.

TestBean.java
beans.xml
MainClass.java

여러개의 필드를 주입해보자.

DataBean.java
TestBean.java

data5는 객체를 생성해서 주입, data6은 만들어놓은 DataBean객체를 주입!!

beans.xml
MainClass.java

정리)  Bean객체를 생성할때 객체의 기본값을 Setter메서드를 통해 주입할 수 있다. 

<property name = ' ' > 뒤에 

정수,실수,문자열주입 : value / 객체주입 : class / 만들어놓은객체주입 : ref 사용!!!

13. 컬렉션 주입

1) List 주입

String, Integer로 이루어진 List컬렉션을 주입해보자.

아래처럼 <property> <list> <value> 순으로 값 주기

TestBean.java

 

beans.xml
MainClass.java

이번에는 객체로 이루어진 List를 주입해보자. 객체 만들어서 주입 3줄 / 미리 만들어진 빈객체 주입 3줄

property - list - bean class  /  property - list - ref bean  순으로 작성!

2) Set 주입

set는 중복데이터를 저장하지 않는다.

beans.xml에 같은 String, Integer데이터를 넣어도 중복값은 출력되지 않는다.

scope를 프로토타입으로 설정해도, 주소가 다른 객체가 생성이 되는데 스프링은 객체의 주소가 달라도 id값이 같으면 중복으로 인지하여 중복값이 출력 되지 않도록 한다. 객체를 계속 생성하고 싶으면 62-63번줄처럼 싱글톤으로 계속 생성해줘야 한다.

beans.xml
MainClass.java
출력결과 - 중복값은 모두 제거되고 출력된다.

3) Map 주입

key는 String, value는 모든 값을 받기위해 Object로 지정해보자.

beans.xml
MainClass.java
출력결과

4) Property 주입

문자값만 저장해서 사용할 수 있다.

TestBean.java
beans.xml
MainClass.java
출력결과

14. 자동 주입

지금까지 Bean을 정의할 때 주입할 객체는 생성자를 통한 주입이나 setter를 통한 주입을 사용했다.

스프링에서는 객체를 주입할 때 자동으로 주입될 수 있도록 설정할 수 있는데,

이름, 타입, 생성자를 통해 가능하며 이것을 auto wire라고 한다.

기본자료형(정수, 실수, 문자열)은 자동주입이 불가능하며 기존의 방식으로 주입해야 한다. 자동주입은 객체만 가능하다.

1) 이름을 통한 주입

xml파일에 id값을 지정해주면 , 변수에 바로 넣는게 아니고  setter메소드를 호출하여 주입해준다.

일반주입(setter메소드를 통한 주입)과 이름을 통한 자동주입을 비교해보자.

beans.xml
MainClass.java

2) 타입을 통한 주입

타입을 통한 자동주입은 , 타입만 같으면 무조건 주입하기 때문에 id값은 생략 가능하다.

beans.xml
MainClass.java

3) 생성자를 통한 주입

이렇게 xml파일에 생성자의 매개변수 타입과 정의된 빈의 타입이 일치할 경우 , autowire='constructor' 를 사용하여 생성자자동주입을 할 수 있다 (?)

이때, 객체가 아닌 정수값이나 문자열은 자동 주입이 안되기 때문에 아래처럼 직접 정의 해 줘야 한다.

이때 디폴트 값으로 byName을 지정하고 싶으면 default-autowire="byName" 을 지정해준다.

디폴트 값을 무시하고 싶다면, autowire='no' 를 적어주자.

 

15. 빈 등록

지금까지는 xml파일에 빈을 등록했었다. 이제부터는 자바파일에 빈 등록을 배워보자.

위에는 xml을 사용하는 방식, 아래는 java파일을 사용하는 방식 비교
자바파일로 빈을 등록하는 방법

xml으로 빈을 정의하는것과 자바파일로 빈을 정의하는 것을 비교해보자.

beans.xml

자바 파일은  @Configuration 을 붙여주며 시작한다. 자바파일이 xml파일 역할을 하는것이라고 명시해 주는 것이다.

자바파일로 빈을 정의할때는 , 메소드 형태로 빈을 만들어주는데, 이때 메소드 이름이 빈의 id값이 된다. 여기서는 java1이 id값이 된다. main클래스에서 불러올때 java1을 호출해 주는 것이다.

BeanConfigClass.java
MainClass.java

메인클래스 자바파일 호출하는 괄호안에 BeanConfigClass.class를 적으면 BeanConfigClass.java 파일에 세팅해놓은 @Bean 을 찾아서 , 아 여기에서 빈을 정의했구나 ! 판단하고 이 메서드를 자동호출하고 , 반환하는 값을 스프링프레임워크에서 갖고 있게 된다. xml이든 자바파일이든 둘다 싱글톤이다.

 

그 외에, lazy-init, Scope, Primary 속성들을 비교해보자.

16. init, destroy 메서드

xml파일로 init, destroy메서드등록과 자바파일로 등록하는법 비교해보면 아래와 같다.

17. 주입

xml파일로 자동주입등록과 자바파일로 등록하는법 비교해보면 아래와 같다.

beans.xml
BeanConfigClass.java

18. 어노테이션을 이용한 빈 설정

어차피 만들 빈클래스를 xml or 자바파일에 설정하지말고, 해당 파일(TestBean)에서 어노테이션으로 간편하게 설정하자.

 

우선 아래와 같이 xml파일에 context 네임스페이스를 추가 해줘야 한다. 

1) @Required

반드시 주입해야 할 프로퍼티가 있다면 @Required 를 붙여준다. 그러나 스프링 5.1 부터는 @Required를 붙여도 아무런 일이 일어나지 않는다. 반드시 주입해야 할 프로퍼티는 생성자를 통해 주입받는 것을 사용해야 한다.

TestBean1.java

2) @Autowired

객체만 오토와이어가 가능하다. TestBean1.java파일에 @Autowired를 붙여서 직접 주입 해보자! 이때 같은 '타입'에 주입을 하는 것이기 때문에 그 타입이 정해지지 않았다면 오류가 발생한다.

@Autowired 넣을때는 반드시! 빈을 정의(beans.xml, BeanConfigClass.java 에 작성)해 줘야한다. 그래야 그 작성된 빈을 가져와서 주입할 수 있으니까.

@Autowired는 세터메소드에 붙여도 되고 , 변수에 붙여도 되고, 생성자에 주입 해도 된다.

TestBean1.java
TestBean.java 자동주입을 세터메소드에 설정
TestBean.java 자동주입을 변수에 직접 설정

3) @Qualifier

같은 타입의 빈이 여러개 있을 경우에, 타입을 가지고 주입하면 오류가 발생했었다.  그럴때는 이름을 통해(byName) 주입을 했었는데, @Autowired로 주입할 때도 같은 타입의 빈이 여러개 있으면 이름을 지정해서 주입을 하는 방법을 사용한다.

@Qualifier("빈이름") 으로 해당 빈을 찾아서 주입한다.

TestBean1.java

4) 생성자 주입

19 . JSR-250 어노테이션

먼저, Maven Repository에서 Javax Annotation API검색해서 아래와 같은 라이브러리를  pom.xml에 추가해줘야 한다.

JSR-250어노테이션은 이름을 통한 주입을 좀 더 쉽게 하기위해 사용하는 것이다. 많은 어노테이션이 있지만, 스프링에서는 @postConstructor, @preDestroy, @Resource 정도 사용을 한다.

1) @postConstructor, @preDestroy

init-method 와 destroy-method 역할을 하는 것이 @postConstruct 와 @preDestroy이다.

BeanConfigClass.java 파일에 아래와 같이 설정했던 것을, 

 

TestBean1.java파일에 직접 설정해 주도록 하자.

TestBean1.java

2) @Resource

@Autowired 와 @Qualifier("빈이름") 을 설정해주면 빈의 이름을 통해서 주입을 할 수가 있었다. 그 두개를 합쳐놓은게 @Resource이다. 이번엔 빈이름이 아닌 , 변수의 이름과 동일한 이름을 가져와 주입이 된다.

20. Component

빈클래스 자체에 등록하는것.

그동안은 xml 파일이나 자바파일에 빈을 등록한 후 사용을 했다.

그런데 @Component를 사용하면, xml파일이나 빈 컴피그파일(자바파일)에 빈을 등록하지 않아도 자동으로 등록이 된다.

bean.xml에서 한줄한줄 빈등록을 했던 것을 TestBean.java파일에서 @Component만 붙여줌으로서 빈에 등록한다. TestBean1,2는 beans.xml에서 등록, TestBean3은 TestBean3.java파일에서 @Component 붙여서 등록

빈 등록이라든지 빈에 관련된 여러가지 내용들을 xml이나 자바파일에 등록을 하게 되는데,

xml을 이용하는 방식을 사용할때 Comoponent를 쓰게되면 아래와 같이 작성을 해야한다. 

"이 패키지들의 component를 다 읽어서 빈으로 등록해줘!"

Bean Configuration 자바파일을 이용할때는 아래와 같이 작성해야 한다.

컴포넌트 스캔은 하나의 이름만 지정 가능하기 때문에, 여러이름을 지정하고 싶으면 xml파일이나 자바파일에 수동으로 등록해줘야 한다.

21. Component Bean 기본설정

22. Component 자동 주입

23. AOP

특정메소드를 호출할때, 그 메소드를 가로채가서 다른 메소드를 먼저 호출하거나 그 이후에 호출하게 만드는 것.

예를 들어,  특정 웹페이지 요청시 로그인 여부를 검사하게 하는것  등으로 활용한다.

method1()메소드가 호출될때, AdvisorClass가 가지고 있는 beforeMethod를 호출해라.(method가 호출되기전에) 라는 뜻으로 아래와 같이 beans.xml에 작성해줄 수 있다.

method1()메소드가 호출될때, AdvisorClass가 가지고 있는 beforeMethod를 호출해라.(method가 호출되기전에)

24. execution 명시자

AOP적용시 다양한 상황에 대응할 수 있도록 명시자를 제공한다. beans.xml

25. @AspectJ 어노테이션 사용하기

지금까지는 어드바이저 역할을 할 클래스를 만들었었다. xml에 설정해놓은 메소드를 찾아서 자동으로 호출을 했었다.

그런데 @AspectJ 를 활용하면 xml파일이 아닌 어드바이저역할을 할 클래스에 직접 세팅할 수 있다.

먼저 , xml파일에는 <aop:aspectj-autoproxy/> , 자바파일에는 @EnableAspectAutoProxy를 작성해 줘야 한다.어차피 어드바이저클래스를 만들바엔 그 클래스에 직접 세팅하자는 의미이다.

AdvisorClass.java

26. 오라클 데이터베이스 설치

오라클사이트에서 11g버전의 database 다운로드후 setup.exe를 눌러 설치를 진행한다. 

이 창이 뜨면 우측 하단 비밀번호 관리 클릭 - SYS/ SYSTEM/ SCOTT 에 비밀번호 1234로 설정

cmd창에서 sqlplus명령어로 설치된 것 확인

27. SQL Devleoper설치

오라클 데이터베이스를 GUI환경에서 관리할 수 있도록 제공되는 도구이다. 깔끔한 환경으로 편리하게 사용가능하다.

오라클에서 다운로드 - sqldeveloper.exe 더블클릭

시트 새로 만들고 select * from emp; 를 실행시켜 아래에 잘 출력 되는지 확인.

28. Spring JDBC

스프링 프레임워크는 JDBC프로그래밍을 위해 JdbcTemplate클래스를 제공하고 있다.

JdbcTemplate클래스는 SQL쿼리문을 손쉽게 구현할 수 있도록 설정되어 있다.

사용법

1) 데이터베이스.txt 만들고, 간단한 쿼리문 작성(int, string1개씩)한후, SQL Developer에 붙여넣어 table을 생성하기.

2) jdbcBean.java파일을 만들고, 1)에서 만들어 놓은 컬럼명과 동일하게 필드명 작성후, 게터세터메소드 생성하기.

자바에서 JDBC프로그램을 시행하기위해서는 데이터베이스와 연동하기 위한 JDBC 드라이버가 필요하다.

우리는 오라클 JDBC를 추가해줘야 한다. (오라클 JDBC, MySQL JDBC 등등 각각의 JDBC드라이버가 필요하다)

보통은 MVN Repository 사이트에서 검색해서 가져오면 되는데, 오라클 JDBC는 MVN Repository 사이트에서 다운로드 받을 수가 없다. 따로 입력해줘야 한다.

 

3 -1) 먼저,  pom.xml에 아래와 같이 자르파일을 다운받을 수 있는 새로운 리포지토리주소를 등록해주자.

3 -2) oracle jdbc라이브러리도 추가해주기.

리포지토리주소 등록 , oracle jdbc라이브러리 등록

 

데이터베이스 접속은, 자바에서 기본적으로 제공하고 있는 JDBC를 이용해도 되지만, 쿼리문 전송할 때마다 매번 close를 해줘야 하기 때문에, 실제로 현업에서는 DBCP를 많이 사용한다.

3 -3) 데이터베이스를 이용하기 위해 DBCP라이브러리를 추가하기.

DBCP라이브러리등록

 

3 -4) JdbcTemplate 라이브러리 추가

4) BeanConfigClass.java에 접속정보를 관리하는 DataSource관련 빈 등록(집주소,우편번호등), 쿼리를 전달하는 JdbcTemplate빈 등록(우체부 채용)

5) 데이터베이스처리에 관련된 jbdcDAO클래스(우체부 데려오고, 우체부가 데이터 가공하는 방법 매뉴얼 ex.삽입)를 만들고 코드 작성하기

6) MainClass에서 dao를 가져와서 데이터 저장/수정/삭제 처리 코드 작성하기 (우체부에게 매뉴얼에 적힌대로 일시키기)

7) 오라클화면에서 select * from jdbc_table; 쿼리를 보내면 아래와 같이 잘 저장된 것을 확인할 수 있다.

Mapper클래스

이제 데이터를 가져오는 작업을 해보자. 

select 에서 row가 하나만 나온다면, 빈객체 하나를 만들어서 직접 컬럼에서 값을 가져와서 빈에 세팅을 한다.

만약 row가 매우 많다면, row하나당 빈객체 하나를 만들어서 row각각의 정보를 담은 빈객체를 row갯수만큼 다 만든후, 이것을 리스트에 담아서 반환해야 하는데, 이 과정을 Mapper클래스가 모두 알아서 해준다!

1) MapperClass를 만들고, RowMapper 인터페이스를 구현한다.

mapRow라는 메소드를 오버라이딩한다. ResultSet이란 객체를 가지고 접근을 한다. 이 객체가 매개변수로 들어오게 된다. 이 객체는 select문을 수행해서 가져온 결과가 담겨있다. 이 ResultSet으로부터 컬럼의 데이터를 가져와서 빈에 주입해주고 그 빈을 반환해준다.

2) jdbcDAO클래스에서 db(우체부)데려오고, Mapper(줄세우기도우미)를 주입받고, 가져온다.

     → MyBatis에서는 jdbcDAO클래스가 따로없고 모두 BeanConfigClass 안에서 주입받는다 !!!

3) 메인클래스에서 List로 가져온다.

29. MyBatis

Mybatis를 사용하는 방식으로는 크게 두가지로 나뉜다.

1. SqlSessionTemplate (MyBatis3.0 이전) : DAO클래스에 직접 SqlSession객체를 선언하고 @Autowired로 의존주입하여 쿼리문을 수행하는 방식

2. MapperInterface (MyBatis3.0 버전) : 기존의 Repository계층인 DAO로직을 MyBatis로 대신한다. 인터페이스만 설계하고 DAO클래스는 따로 만들지 않는다. 주입도 자동으로 처리된다.

 

자바버전은 반드시 1.8(8버전)이상으로 맞추어 줘야한다. 그 이하일 경우 컴파일 에러가 난다.

1) BeanConfigClass에 DataSource(주소), SqlSessionFactory(우체부 버전2), Mapper(줄세우기도우미) 부분 작성.

Mapper는 무수히 많이 만들어도 된다.

SqlSessionFactory는 데이터베이스와의 연결과 SQL의 실행에 대한 모든 것을 가진 가장 중요한 객체다.(일단 JdbcTemplate대신 쓰는거라고 생각하자..!!)
이 객체가 DataSource를 참조하여 MyBatis와 Mysql 서버를 연동시켜준다.

2) 메인클래스에서 Mapper이름으로 가져온다. 실행시 오류가 안난다면 정상작동

3) MapperInterface인터페이스에서 일대일대응형식으로 Results부분 코드작성

4) 메인클래스에서 select문 작성

 

 

출처 :  [인프런]윤재성의 스프링 프레임워크 개발자를 위한 실습을 통한 입문 과정