'di' 태그의 글 목록 :: 잡다한 프로그래밍
반응형

1. Data Access Layer

DB에 접근하기 위해 다음 그림과 같이 DAO는 JDBC Template에 이는 JDBC Driver를 통해 Database에 접근하고 DataSource를 이용하여 DB에 실질적으로 커넥션 한다.


2. Data Source

데이터 베이스에 접근하기 위해 DataSource를 사용한다 DataSource의 종류는 다양하지만 DBCP라이브러리의 BasicDataSource를 통상 많이 사용하므로 실습에서도 이를 사용할 예정이다.


3. DAO란?

Data Access Object의 약자로서 서비스는 DB에 대해 아무것도 모르고, 실질적으로 DB에 접근하는 오브젝트를 의미한다. 쉽게 생각하여 DB의 CRUD를 실질적으로 수행하는 코드가 담긴 부분이다.


4. JDBC란?

JDBC란 DB와의 커넥션, SQL 사용, Close 커넥션 등 잡다한 일들을 해주는 프레임 워크이다. 기본 JAVA JDBC는 커넥션 등 try catch의 반복으로 반복적인 코드를 짜야하지만 Spring JDBC를 사용하면 SQL만 코드를 작성하고 나머지는 Spring 프레임워크에서 담당한다.


5. 실습하기

#1) 사전 준비

  • Mysql 5.7.28 설치

Mysql내부에는 csemall라는 DB, offers라는 테이블을 만들고 다음과 같은 구조로 이루어져 있다.

id(INT) name email text
1 Alice Alice@google.com alice
2 Bob Bob@google.com bob
3 Jun Jun@google.com jun

 

#2) Simple Spring Maven 프로젝트 실행

다음과 같은 helloDB프로젝트를 생성한다.

 

#3) pom.xml 수정

다음과 같은 dependency를 추가하고 저장을 누르면 maven에 라이브러리가 추가된 것을 확인할 수 있다.

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.38</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>4.1.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.4</version>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-dbcp2</artifactId>
			<version>2.1.1</version>
		</dependency>

 

#4) jdbc.properties 생성

props라는 패키지를 생성하고 그 밑에 jdbc.properties라는 파일을 생성한다. 파일 내부는 다음과 같다 이는 DB의 정보를 담고 있으며, 생성할 bean에 하드 코딩하는 것보다 좋은 방법이다.

jdbc.username = root
jdbc.password = 사용자 비밀번호를 입력
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/csemall

 

#5) beans.xml 생성

다음과 같이 beans.xml을 생성한다 context:annotation은 DAO클래스에서 사용할 @Autowired를 활성화한다는 의미이며 context:component는 @Component 에노 테이션이 있는 어떤 패키지를 scan 할 건지 명시하는 부분이다. bean으로 생성한 dataSource는 #4에서 생성한 jdbc.properties를 사용하여 실제 DB와 커넥션을 하는 부분이다 dbcp2의 BasicDataSource클래스를 사용하고 있고 이 클래스는 DataSource인터페이스를 따르는 클래스이다. 마지막으로 context:property-placeholder는 앞서 사용할 jdbc.properties가 어떤 위치에 있는지 명시하는 부분이다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
	
	<context:annotation-config></context:annotation-config>
	<context:component-scan base-package="csemall"></context:component-scan>

	<bean id="dataSource"
		class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
		<property name="driverClassName"
			value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>


	<context:property-placeholder
		location="kr/ac/hansung/spring/props/jdbc.properties" />

</beans>

 

#6) OfferDAO 클래스 생성

src/main/java아래의 csemall이라는 패키지를 만들고 다음과 같은 Class를 생성한다. @Component 에노테이션은 이 클래스는 id가 offerDAO라는 bean을 하나 생성하겠다는 의미이다. 또한 JdbcTemplate를 사용하여 bean에 생성한 dataSource를 주입(@Autowired)시켜 DB와 실질적인 커넥션을 해주는 부분이 담겨있다. getRowCount() 부분은 다음과 같은 sql을 query 하고 결과는 Integer로 받는다는 의미이다.

package csemall;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component("offerDAO")
public class OfferDAO {
	private JdbcTemplate jdbcTemplateObject; //얘는 new를 사용해됨 이후 DataSource는 DI를 통해 주입 시킬예정	

	@Autowired
	public void setDataSource(DataSource dataSource) { //DataSource는 인터페이스인데 우리가 앞서 bean에 선언한 datasource가 이 인터페이스를 따르고 DI한다
		this.jdbcTemplateObject = new JdbcTemplate(dataSource);
	}
	
	public int getRowCount() {
		String sqlStatement = "select count(*) from offers";
		return jdbcTemplateObject.queryForObject(sqlStatement, Integer.class);
	}
}

 

#6) Offer.java 클래스 생성

Lombok을 이용해 @Getter, @Setter를 생성하였고 DB에 저장된 id, name, email, text를 받는다. 이는 추후에 사용될 클래스이다.

package csemall;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Offer {
	private int id;
	private String name;
	private String email;
	private String text;
	
	public Offer() {
		
	}

	public Offer(String name, String email, String text) {
		this.name = name;
		this.email = email;
		this.text = text;
	}
	
	public Offer(int id, String name, String email, String text) {
		this.id = id;
		this.name = name;
		this.email = email;
		this.text = text;
	}
}

 

#7) MainApp.java 클래스 생성

다음과 같이 코드를 작성한다 ClassPathXmlApplicationContext를 이용하여 beans.xml을 읽어오고 읽어온 context의 offerDAO라는 빈을 이용하여 getRowCount() 함수를 출력한다

package csemall;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext context =
				new ClassPathXmlApplicationContext("kr/ac/hansung/spring/csemall/beans/beans.xml");
		
		OfferDAO offerDAO = (OfferDAO) context.getBean("offerDAO");
		System.out.println(offerDAO.getRowCount());
		
		context.close();
	}

}

 

#8) 실행화면

MainApp.java를 실행하면 다음과 같은 결과를 확인 할 수 있다.

반응형
반응형

1. Annotation이란?

앞 강의에서 DI란 무엇인지와 생성자를 이용하여 의존성 주입을 하는 방법을 실습해 보았다면 이번 강의에서는 Annotation을 이용하여 의존성 주입을 해볼 예정이다.

먼저 에노테이션이란 기존 XML을 이용하여 의존성 주입을 하는 것이 아닌 @Required 같은 태그를 이용하여 의존성 주입을 하는 것을 의미한다. 의존성 주입 에노테이션으로는 다음과 같은 3가지가 있다

 

#1 @Required

XML의 Bean의 property에 해당하는 부분 중 필수적인 부분을 명시할 때 다음과 같이 사용한다. 쉽게 말해 XML에 bean중 property가 꼭 있어야 하니 없으면 에러가 발생한다고 생각하면 좋다 즉 @required를 사용할때 xml에 property에 name에 해당하는 부분이 반드시 있어야 에러가 발생하지 않는다.

public class Boy{
private String name;

@required
public void setName(String name){
  this.name = name;
  }
  
}

 

#2 @Autowired

이전 강의에서 의존성 주입을 할 때 bean에 property를 사용하여 세터를 통한 주입을 했었다. 하지만 Class의 기존 세터 부분을 지우고 @Autowired 에노테이션을 추가하면 자동으로 의존성 주입이 이루어진다. 이는 다음 코드처럼 Boy클래스는 property를 사용하여 세터를 통해 bean을 생성했고 college라는 bean은 propery를 통해 boy라는 bean을 의존성 주입 했지만 College클래스에 @Autowired 에노테이션을 추가하고 세터부분을 지우고, 마찬가지로 bean의 property부분을 지우면 자동으로 의존성 주입이 이루어지는것을 확인할 수 있다.

public class Boy {
 private String name;
 private int age;
   
 //   getters and setters ...
}

public class College {
 
 @Autowired
 private Boy student; 
 
 //이부분부터
 public void setStudent(Boy aboy){
  this.stuendt = aboy;
 }
 //이부분까지 삭제
 
}

<bean id=“boy” class=“Boy”>
 <property name=“name” value=“Rony”/>
 <property name=“age” value=“10”/>
</bean>

<bean id=“college” class=“College”>
  <property name=“student” ref=“boy”/> //이부분 삭제
</bean>

 

#3 @Qualifier

만약 Autowired를 할때 다음과 같이 Boy 클래스의 bean이 두 개 있으면 어떤 걸 자동으로 Autowired를 할지 몰라 오류가 생긴다 따라서 이럴 때 @Qualifier를 사용한다. 다음과 같이 bean에는 qualifer의 value를 지정하고 Class에는 @Qualifier의 에노테이션과 함께 value를 설정하면 어떤 bean을 주입할지 정하게 된다

<bean id=“boy1” class=“Boy”>
 <qualifier value=“rony”/>
 <property name=“name” value=“Rony”/>
 <property name=“age” value=“10”/>
</bean>

<bean id=“boy2” class=“Boy”>
 <qualifier value=“tony”/>
 <property name=“name” value=“Tony”/>
 <property name=“age” value=“8”/>
</bean>

<bean id=“college” class=“College”>
</bean>


public class College {
 
@Autowired
@Qualifier(value=“tony”)
 private Boy student; 
 
  //   getters ...
}

 

#4 @Resource

@Qualifer가 value를 지정해서 의존성 주입을 시켜줬다면 Resource는 bean의 id로 의존성 주입을 해준다. 다음과 같이 사용할 수 있다.

public class College {
  
  @Resource(name=“boy1”)
  private Boy student; 
 
  //   getters and setters ...
}

 

#5 @Component

 

@Component는 Bean.xml에 빈을 사용한다 명시 하지 않아도 자동으로 클래스를 Bean으로 만들어주는 에노테이션이다

@Component
public class College {
  
  private Boy student; 
 
  //   getters and setters ...
}

2. Annotation 실습

실습할 예제는 이전 DI강의에서 사용했던 프로젝트로 실습하겠습니다.

#1) animal.xml수정

먼저 animal.xml을 다음과 같이 수정합니다. 생성자로 주입했던 constructor-arg 부분을 지워 주고 애노테이션을 사용한다는 코드를 추가합니다

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
	<context:annotation-config></context:annotation-config>
	
	<bean id="dog" class="kr.ac.hansung.helloDI.Dog">
		<property name="myName" value="poodle"></property>
	</bean>

	<bean id="cat" class="kr.ac.hansung.helloDI.Cat">
		<property name="myName" value="bella"></property>
	</bean>

	<bean id="petOwner" class="kr.ac.hansung.helloDI.PetOwner">
		<constructor-arg ref="dog"></constructor-arg>
	</bean>
</beans>

 

#2)  @Autowired추가

PetOwner.java클래스의 생성자 부분을 지우고 다음과 같이 @Autowired에노테이션을 추가합니다

package kr.ac.hansung.helloDI;

import org.springframework.beans.factory.annotation.Autowired;

public class PetOwner {
	
		@Autowired
		private AnimalType animal;

		public void play() {
			animal.sound();
		}
}

다음과 같이 코드를 수정하고 Run 하게 되면 다음과 같은 에러가 발생하는 것을 확인할 수 있습니다. 앞서 공부한 것처럼 2개의 bean 중 어떤 bean을 주입할지 모르기 때문에 다음과 같은 error가 발생하는 것입니다. 따라서 2가지 방법을 통해 해결해보도록 하겠습니다.

error

#3) @Qualifier추가 및 animal.xml수정

다음과 같이 Qualifier에노테이션을 사용하여 다음과 같이 어떤 빈을 주입할지 선택합니다.

 

PetOwner.java

package kr.ac.hansung.helloDI;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class PetOwner {
	
		@Autowired
		@Qualifier(value="cat")
		private AnimalType animal;

		public void play() {
			
			animal.sound();
		
		}
}

 

animal.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"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
	<context:annotation-config></context:annotation-config>
	
	<bean id="dog" class="kr.ac.hansung.helloDI.Dog">
		<property name="myName" value="poodle"></property>
		<qualifier value="dog"></qualifier>
	</bean>

	<bean id="cat" class="kr.ac.hansung.helloDI.Cat">
		<property name="myName" value="bella"></property>
		<qualifier value="cat"></qualifier>
	</bean>

	<bean id="petOwner" class="kr.ac.hansung.helloDI.PetOwner">
	</bean>
</beans>

 

#4) 실행

수정을 완료하였다면 다음과 같은 실행 결과를 확인할 수 있다.

qualierfier 실행 성공

 

#5) @Resource 추가 및 animal.xml수정

@Qualifier대신 Resource에노테이션을 사용해보자 다음과 같이 animal.xml을 수정한다

 

PetOwner.java

package kr.ac.hansung.helloDI;
import javax.annotation.Resource;

public class PetOwner {
		@Resource(name="dog")
		private AnimalType animal;

		public void play() {
			
			animal.sound();
		
		}
}

 

animal.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"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
	<context:annotation-config></context:annotation-config>
	
	<bean id="dog" class="kr.ac.hansung.helloDI.Dog">
		<property name="myName" value="poodle"></property>
	</bean>

	<bean id="cat" class="kr.ac.hansung.helloDI.Cat">
		<property name="myName" value="bella"></property>
	</bean>

	<bean id="petOwner" class="kr.ac.hansung.helloDI.PetOwner">
	</bean>
</beans>

 

#6) 실행

수정을 완료하고 실행하면 다음과 같은 결과를 확인할 수 있다.

Resource 성공


3. 정리

@Autowired를 사용하여 bean의 의존성 주입을 하며, 여러 빈이 있을 경우 Qualifier에노테이션을 사용하여 빈을 선택하거나, bean의 id를 통해 의존성 주입을 하는 @Resource 에노테이션을 사용할 수 있다.

반응형
반응형

1. DI(Dependency Injection)란?

DI는 말 그대로 의존성을 주입한다 라는 뜻이다. 쉽게 설명하면 JAVA에서 객체를 new를 이용하여 생성하는데 이렇게 사용하지 않고 외부에서 생성한 객체를 세터 또는 생성자를 통해 사용하겠다는 의미이다. 이를 의존성 주입이라 한다. 의존성 주입 방법에는 세터를 이용하거나 생성자를 이용한 방법 두가지가 있다. 예제로는 생성자를 이용한 방법으로 예제를 공부하겠다

DI 사용하지 않은 예시

그림처럼 DI를 이용하지 않고 new를 사용하여 객체를 생성하게 되면 PetOwner와 Dog간 강한 결합이 생기게 되어 도그를 수정하면 PetOwner가 수정될 수 있다.

 

DI

반면 다음과 같이 Bean Container를 이용하여 객체를 생성하고 의존성 주입을 시켜주면 결합이 강하지 않다는 장점이 생긴다. 이때 Bean Container는 XML로 이루어져 있다.

DI 설명

다음 그림처럼 메인함수에서는 ApplicationContext라는 BeanContainer를 생성하고 미리 작성된 xml에 따라 ApplicationContext는 Dog, Cat, PetOwner라는 객체를 생성하고 Dog, Cat을 PetOwner에 의존성 주입한다. 스프링은 new방식 대신 DI방식을 권장한다.


2. 의존성 주입 실습

#1 프로젝트 생성

File > New > Spring Legacy Project를 클릭하고 다음과 같은 Simple Spring Maven 프로젝트를 생성한다. 프로젝트명은 원하는 프로젝트명을 사용해도 무방하다. 이 실습에서는 helloDI라는 프로젝트명을 사용하겠다.

프로젝트 생성

#2 AnimalType, Cat, Dog 생성하기

src/main/java에 클래스와 인터페이스를 담을 패키지를 생성한다 패키지명은 자유로워도 좋다. 패키지를 생성하였다면 패키지 아래에 AnimalType.java라는 인터페이스를 생성한다

인터페이스 코드는 다음과 같다 인터페이스를 implements할 cat, dog클래스들이 사용할 sound라는 메소드를 정의하고 있다.

 

AnimalType.java

package kr.ac.hansung.helloDI;

public interface AnimalType {

	public void sound();

}

 

다음으로 Cat.java라는 클래스를 생성한다. 게터와 세터가 만들어져 있으며 AnimalType의 sound메소드를 사용하고 있다.

 

Cat.java

package kr.ac.hansung.helloDI;

public class Cat implements AnimalType {

	public String getMyName() {
		return myName;
	}

	public void setMyName(String myName) {
		this.myName = myName;
	}

	private String myName;

	@Override
	public void sound() {
		
		System.out.println("Cat name = " + myName + ":" + "Meow");
	}

}

 

마지막으로 Dog.java라는 클래스를 생성한다. 게터와 세터가 만들어져 있으며 AnimalType의 sound메소드를 사용하고 있다.

 

Dog.java

package kr.ac.hansung.helloDI;

public class Dog implements AnimalType {

	public String getMyName() {
		return myName;
	}

	public void setMyName(String myName) {
		this.myName = myName;
	}

	private String myName;

	@Override
	public void sound() {
		
		System.out.println("Dog name = " + myName + ":" + "Bow Wow");
	}

}

 

#3 PetOwner클래스 생성하기

petOwner클래스는 AnimalType의 animal을 받는다 이는 cat이나 dog를 받아서 사용하겠다는 의미이고 play라는 메소드와 생성자를 정의했다.

 

PetOWner.java

package kr.ac.hansung.helloDI;

public class PetOwner {

		private AnimalType animal;

		public PetOwner(AnimalType animal) {
			this.animal = animal;
		}
		
		public void play() {
			
			animal.sound();
		
		}
}

 

#4 animal.xml 생성하기

Bean Container가 생성하기 위해 참고할 xml파일을 생성한다 코드는 다음가 같다.

<bean id = "dog"> 부분이 dog라는 클래스의 객체를, <bean id = "cat"> 부분이 cat이라는 클래스의 객체를 <bean id = petOwner"> 부분이 petOwner의 객체를 생성하고 <constructor-arg ref = "dog">는 dog객체를 의존성 주입하겠다 라는 의미이며, <property name="myName" value="poddle">는 도그의 myName의 값을 poodle로 주겠다는 의미이다.

 

animal.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="dog" class="kr.ac.hansung.helloDI.Dog">
		<property name="myName" value="poodle"></property>
	</bean>

	<bean id="cat" class="kr.ac.hansung.helloDI.Cat">
		<property name="myName" value="bella"></property>
	</bean>

	<bean id="petOwner" class="kr.ac.hansung.helloDI.PetOwner">
		<constructor-arg ref="dog"></constructor-arg>
	</bean>
</beans>

 

#5 MainApp.java작성

마지막으로 메인 함수인 MainApp.java를 다음과 같이 작성한다. ApplicationContext가 animal.xml을 바라보게 하고

petOwner의 person이 context의 getBean함수를 통해 객체를 가져와 사용하는 모습이다.

package kr.ac.hansung.helloDI;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {

	public static void main(String[] args) {

		ClassPathXmlApplicationContext context = 
				new ClassPathXmlApplicationContext("/kr/ac/hansung/helloDI/conf/animal.xml");
		
		PetOwner person = (PetOwner) context.getBean("petOwner");
		person.play();
		
		context.close();
	}

}

 

#5 프로젝트 Run

프로젝트를 Run 하게 되면 다음과 같은 결과를 확인할 수 있다.

결과화면

 

사용자는 animal.xml에서 정의한 대로 dog, cat, petOwner의 객체를 먼저 생성하고, 이후 dog를 petOwner에 주입하였으니 결과 화면에는 dog가 보이게 된다 만약 cat을 주입하고 싶다면 constructor-arg의 ref = cat으로 수정해주면 된다

반응형

+ Recent posts