본문 바로가기

spring framework

스프링 프레임워크 #1 - Chapter02 : 빈/의존관계/IoC /싱글톤

◎ 참고 서적  |  Mastering Spring 5

 

[차이]

Java : 클래스가 다른 클래스에 의존하는 형태, 클래스를 사용하기 위해 인스턴스를 생성할 때 클래스 간에 밀접한 결합이
           형성된다.

Spring : 인스턴스 생성 대신 Ioc 컨테이너라는 객체를 생성하고 의존 관계를 연결한다.

 

[장점]

Spring : Ioc 컨테이너로 연결하기 때문에 클래스 간 결합을 느슨하게 만들고 단위 테스트를 용이하게 만들어준다.

 

 

[ 최신 스프링 버전 5.1 에 대해 알아야 할 정보]

스프링 프레임워크 5.1 // JDK 12, JDK 11 // Java 8 이상

내 pc

 

[2장 학습에 앞서 필요한 소프트웨어]

  • 이클립스
  • 자바 8+
  • 메이븐 3.x
  • 인터넷연결

- BusinessService (인터페이스)

package com.mastering.spring.business;

import com.mastering.spring.beans.User;

public interface BusinessService {
  long calculateSum(User user);
}

- BusinessServiceImpl (클래스 implements 인터페이스 할)

package com.mastering.spring.business;

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

import com.mastering.spring.beans.Data;
import com.mastering.spring.beans.User;
import com.mastering.spring.data.DataService;

@Service
public class BusinessServiceImpl implements BusinessService {

  // @Autowired --> 해당 인터페이스 
  // DataService dataService = new DataServiceImpl()의 인스턴스 생성과 같지만 클래스 결합 정도가 다름
  @Autowired
  private DataService dataService;

  public long calculateSum(User user) {
    long sum = 0;
    for (Data data : dataService.retrieveData(user)) {
      sum += data.getValue();
    }
    return sum;
  }

  public void setDataService(DataService dataService) {
    this.dataService = dataService;
  }

}

- DataService (인터페이스)

package com.mastering.spring.data;

import java.util.List;

import com.mastering.spring.beans.Data;
import com.mastering.spring.beans.User;

public interface DataService {
  List<Data> retrieveData(User user);
}

- DataServiceImpl.java  (클래스 implements 인터페이스 할)

package com.mastering.spring.data;

import java.util.Arrays;
import java.util.List;

import org.springframework.stereotype.Repository;

import com.mastering.spring.beans.Data;
import com.mastering.spring.beans.User;


//@Repository ==> 데이터베이스에서 데이터를 가져올 때 사용하는 어노테이션
@Repository
public class DataServiceImpl implements DataService {
  public List<Data> retrieveData(User user) {
	// ArrayList 생성과 동시에 값 선언 하는 법 Data(n)는 setter를 이용한 값 지정
    return Arrays.asList(new Data(10), new Data(20));
  }
}

[어노테이션]

  1. @Component : 스프링 IoC 컨테이너에 어떤 빈을 생성해야 하는지 알려줘야 하는 어노테이션
    @Component 
    public Class DataServiceImpl implements DataService   ( 여기서 빈은 DataService )
  2. @Service : 비즈니스 서비스 구성요소에 사용됨 -> DAO 구성요소에서 사용된다.
    @Service
    public Class BusinessServiceImpl implements BusinessService
  3. @Repository : 데이터베이스에서 데이터를 가져 오는 것과 관련이 있음 --> DataServiceImpl에 사용
    @Repository
    public cCass DataServiceImpl implements DataService   

 

와이어링

@Service
public class BusinessServiceImpl{
	@Autowired
    private DataService dataService;

[스프링으로 IoC 컨테이너 생성하는 방법]

1. 빈 팩토리

2. 애플리케이션 컨텍스트

 

- pom.xml : 메이븐으로 프로젝틀를 빌드하는 과정 (JAR)

<dependency>

       <groupId>org.springframework</groupId>     ==>> jar 파일을 불러올 패키지

       <artifactId>spring-core</artifactId>                ==>> jar(라이브러리)명

</dependency>

<?xml version="1.0" encoding="US-ASCII"?>
<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 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.mastering.spring</groupId>
	<artifactId>Chapter02-Dependency-Injection</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>${project.artifactId}</name>

	<properties>
		<spring.version>5.1.3.RELEASE</spring.version>
	</properties>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework</groupId>
				<artifactId>spring-framework-bom</artifactId>
				<version>${spring.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
		</dependency>


		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.16</version>
		</dependency>

		<!-- Unit Test Dependencies -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.mockito</groupId>
			<artifactId>mockito-core</artifactId>
			<version>1.9.0-rc1</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<repositories>
		<repository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</repository>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>

	<pluginRepositories>
		<pluginRepository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</pluginRepository>
		<pluginRepository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</pluginRepository>
	</pluginRepositories>
</project>

 

※중요

@Autowired를 사용하여 결합도를 낮추려면

스프링 IoC 컨테이너는 빈의 위치를 알고있어야 한다 ==> 컴포넌트 스캔하여 찾을 수 있게 context를 만들어야 한다.

① SpringContext  :  컴포넌트 스캔 역할

@Configuration
class SpringContext {
}

② LanchJavaContext : 스캔 지시가 떨어지면 그 후 빈의 클래스명을 지목시킬 부분

public class LaunchJavaContext {

  private static final User DUMMY_USER = new User("dummy");

  public static Logger logger = Logger.getLogger(LaunchJavaContext.class);

  public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext(
        SpringContext.class);

    BusinessService service = context.getBean(BusinessService.class);
    logger.debug(service.calculateSum(DUMMY_USER));
  }
}
즉, ① + ②  = 컴포넌트 스캔한 패키지 지정해주면 + 해당 빈 클래스들 찾아와!! ▼
└─▶  LanchJavaContext 의 완전체
package com.mastering.spring.context;

import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import com.mastering.spring.beans.User;
import com.mastering.spring.business.BusinessService;

@Configuration
@ComponentScan(basePackages = { "com.mastering.spring" })
class SpringContext {
}

public class LaunchJavaContext {

  private static final User DUMMY_USER = new User("dummy");

// LaunchJavaContext 가 실행되면 로그로 기록 남겨라
  public static Logger logger = Logger.getLogger(LaunchJavaContext.class);

  public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext(
        SpringContext.class);

    BusinessService service = context.getBean(BusinessService.class);
    logger.debug(service.calculateSum(DUMMY_USER));
  }
}

실행 결과

● 컨테이너 관리 빈

의존 관계를 만드는 클래스 대신, 빈을 생성하고 관리하는 기능을 컨테이너에 위임하면 이로운 점

- 느슨하게 결합되 테스트할 수 있다. ==> 결함 감소

- 컨테이너가 빈을 관리하므로 로깅, 캐싱, 트랜잭션 관리 및 예외 처리와 같은 크로스 컷팅을 AOP를 사용해 빈 중심으로
  구성할 수 있다.   ==> 유지 보수가 용이

 

[어노테이션 (@Annotation) 사용 시 특징]

1, 빈을 더 짧고 간단하게 정의할 수 있음

2. 어노테이션을 사용하게 되면 더이상 POJO가 아님.

3. 어노테이션을 사용하면 오토와이어링 문제를 해결하기 어려울 수 있다.

 

애플리케이션 컨텍스트에 XML 구성 사용

- BusinessApplicationContext.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p"
	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            
	                       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd            
	                       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd            
	                   
	                       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

	<context:component-scan base-package="com.mastering.spring"/>

</beans>

xmlns:context-"경로" ==> 경로가 스캔할 패키지

이 xml을 사용함으로써

LaunchJavaContext.java 와 다르게 

“@Configuration
@ComponentScan(basePackages = { "com.mastering.spring" })
class SpringContext {
}”

파트가 LaunchXmlContext.java 에서는 사라짐

 

 

- LaunchXmlContext.java

package com.mastering.spring.context;

import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.mastering.spring.beans.User;
import com.mastering.spring.business.BusinessService;

public class LaunchXmlContext {

  private static final User DUMMY_USER = new User("dummy");

  public static Logger logger = Logger.getLogger(LaunchJavaContext.class);

  public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext(
        "BusinessApplicationContext.xml");

    BusinessService service = context.getBean(BusinessService.class);
    logger.debug(service.calculateSum(DUMMY_USER));
  }

}

 

 

 

[@Autowired 어노테이션 종류 from Chapter02]

package com.mastering.spring.context;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Configuration
@ComponentScan(basePackages = { "com.mastering.spring" })
class PrimaryAnnotationSpringContext {

}

interface SortingAlgorithm {

}

@Component
@Qualifier("mergesort")
class MergeSort implements SortingAlgorithm {
  // Class code here
}

@Component
@Primary
class QuickSort implements SortingAlgorithm {
  // Class code here
}

@Component
class SomeService {
  @Autowired
  @Qualifier("mergesort")
  // SortinAlgorithm은 두군데에서 implements 되지만 @Qualifier("meresort") 라고 지정된 곳의 
  // 의존관계로 규정된다.
  SortingAlgorithm algorithm;
}

public class PrimaryAnnotationJavaContext {

  public static Logger logger = Logger
      .getLogger(PrimaryAnnotationJavaContext.class);

  public static void main(String[] args) {
    ApplicationContext context = new AnnotationConfigApplicationContext(
        PrimaryAnnotationSpringContext.class);

    SortingAlgorithm algorithm = context.getBean(SortingAlgorithm.class);
    logger.debug(algorithm);

    SomeService service = context.getBean(SomeService.class);
    logger.debug(service.algorithm);

  }
}
  • @Autowired :
    의존 관계에 사용하면 애플리케이션 컨텍스트는 일치하는 의존관계를 검색
    오토와이어링된 모든 의존 관계 필요
    일치 1항목 존재 -> 와이어링 성공
    일치 2항목 존재 -> 와이어링 실패
    일치 0항목 존재 -> 와이어링 실패
    - 여러 항목 중 하나만 사용하려면 @Primary 어노테이션 사용
    - 오토와이어링 강화하려면 @Qualifier 사용

[의존관계 주입 옵션]

  • setter 주입
  • 생성자 주입

[스프링 빈 스코프 사용자 정의]

스프링 빈은 여러 스코프로 만들 수 있으며 기본 스코프는 '싱글톤'

 

:: 싱글톤 부터는 다음 시간에 이어서 진행::