프레임워크, Spring의 특징/기능, IoC컨테이너, DL/DI

brave_chicken·2024년 4월 25일

잇(IT)생 챌린지

목록 보기
36/90

chapter 2. 스프링의 특징과 의존성 주입

기타필기

  • pom.xml => 라이브러리 저장
    여기저장하면 메이븐 디벤던시스에 자동으로 들어감

  • 프로그램 생성시 에러 발생하는 경우 repository폴더 삭제 후 다시 라이브러리 다운로드 (교재 31-32p)
    C:\Users\사용자이름.m2\repository\org

  • Ajax의 x가 xml의미

1. 프레임워크

  • 완성된 소프트웨어가 아니라 어떤 문제를 해결하기 위해서 만들어진 미완의 모듈로 모든 자바개발자들이 공통으로 사용하는 기능을 미리 만들어서 제공하는 프로그램
    => 해결해야하는 문제 : 내가 개발해야하는 시스템
    ex. 교육시스템, 예약시스템, 인터넷뱅킹, 쇼핑몰, 게임사이트...

  • 재사용이 가능한 모듈, 일반적으로 프레임워크를 이용해서 개발하는 시스템들의 공통모듈은 프레임워크에서 제공하는 기능을 이용해서 개발
    => 공통모듈 : db연동, 트랜잭션처리, 뷰관리, 로그기록, 보안, 다국어처리, 메시징, ...

  • 검증받은 프로그램으로 신뢰성이 높은 시스템을 개발할 수 있다.

  • 프레임워크를 이용해서 개발하면 개발자들간의 의사소통이 가능

  • 대부분 개발자들이 직접 개발하고 처리하던 작업을 프레임워크가 제공해주기 때문에 유지보수나 시스템개발을 위해 필요한 시간을 절약할 수 있다.
    (프레임워크를 제대로 이해하고 있는 것이 전제로 들어가야함)

  • 경량의 모듈로 개발이 가능

  • 프레임워크에서 제공하는 모듈(XXX.jar - 라이브러리, 기능들)은 누가 언제 어떤 시스템을 개발하더라도 바로 사용할 수 있는 가볍고 안정적인 모듈

2. 스프링의 핵심기능

  • IoC 컨테이너
  • DI
  • AOP
  • 스프링 웹 MVC
  • 스프링 JDBC
  • 트랜잭션, 커넥션풀, 스케줄러...

3. 스프링의 특징

  • 자바 기반의 프레임워크(코틀린으로 확장..)
  • 스프링은 OOP특성을 잘 살려서 개발할 수 있도록 도와주는 프레임워크
  • 변경이 용이한 프로그램
    => 기존 시스템을 구성하는 구성요소가 변경된다고 하더라도 유연하게 시스템을 운영할 수 있도록 지원
    <자바의 특징(다형성)- 컴포넌트를 쉽고 유연하게 변경하면서 개발할 수 있는 언어>
    => OOP언어의 특징인 다형성을 극대화시켜서 클래스를 설계할 수 있도록 도와주는 프로그램
  • 스프링이 제공하는 IoC컨테이너(실습의 커넥션 팩토리같은)를 통해서 필요한 객체를 전달받아서 사용한다.
    => 프로그램 안에서 사용하는 모든 객체에 대한 lifecycle을 컨테이너가 알아서 관리한다.

4. IoC(Inversion Of Control)컨테이너의 사용

*컨테이너 : 객체의 lifecycle을 관리하는 컴포넌트

=> DI와 DL
=> 제어의 역전
=> 내가 관리하던 객체를 컨테이너에게 위임
즉, 내가 필요한 곳에서 직접 소스 안에서 객체생성을 하지 않고 컨테이너로부터 전달받아서 사용할 수 있도록 처리
=> IoC컨테이너에서 받은 객체(빈 Bean)는 싱글톤으로 관리된다.(기본설정)

  • Bean : 스프링컨테이너가 관리하는 객체

[스프링 IoC컨테이너의 종류]
BeanFactory

ApplicationContext

WebApplicationContext

BeanFactory

getBean이 호출되면 요청한 객체가 만들어진다.

ApplicationContext

  • IoC컨테이너 안에서 대표가 되는 컨테이너인 ApplicationContext는 컨테이너객체가 생성될때 설정파일에 등록된 모든 객체를 만들고 객체와 객체간의 관계까지 모두 설정하면서 IoC컨테이너가 초기화된다.
    (xml, 빈, 어노테이션으로 설정된 모든 객체를 만들기 때문에 사용하지 않는 객체라고 하더라도 문제가 있으면 빈을 만들다가 오류가 발생한다.)
  • getBean을 호출하면 IoC컨테이너가 가지고 있는 객체를 찾아서 리턴

WebApplicationContext

웹환경에서 사용
=> 대표 컨테이너이므로 인터페이스
=> 이 인터페이스의 하위를 이용해서 작업

스프링 컨테이너 - ApplicationContext, IoC, DI

1) DL(Dependency Lookup)

설정파일에 등록한 객체를 컨테이너가 제공하는 메소드(getBean)를 이용해서 빈을 찾아오는 작업

2) DI(Dependency Injection)

  • 개발자가 스프링컨테이너로부터 직접 빈을 찾아와서 사용하는 DL과 다르게 Spring IoC 컨테이너 내부에서 설정파일에 등록된 빈을 직접 생성하고 내부에서 의존하고 있는 빈도 생성해서 연결시켜주는 작업
  • 스프링컨테이너에 의해서 자동으로 객체의 관계가 설정되도록 하려면 빈이 사용하는 의존모듈으로 어떤 방법으로 주입받을 것인지 미리 설정파일에 등록해야한다. 또한 주입받을 방법에 맞는 메소드를 미리 준비해놓아야 한다.

[Constructor Injection]

  • 생성자를 통해서 주입받기(주로 사용하는 방법)
    (1) 주입받을 클래스에서 객체를 주입받을 수 있도록 생성자를 미리 정의해놓아야한다.
    (2) 설정파일에도 매개변수가 있는 생성자를 호출할 수 있도록 즉, 객체를 주입받을수있도록 엘리먼트(태그)를 정의해야한다.

[Setter Injection(Property Injection)]

  • Setter 메소드를 통해서 주입받기

DL포함 이전이론 실습

factorypattern

Connection 상위클래스

public abstract class Connection {
	 String url;
	 public Connection(String url){
		 this.url=url;
	 }
	 public abstract  void connect();
}

MongoDBConnection extends Connection

public class MongoDBConnection extends Connection {
	public MongoDBConnection(String url) {
		super(url);
	}

	public void connect() {
		System.out.println(url + "위치의 MongoDB 서버로 연결됐습니다.");
	}
}

MySQLConnection extends Connection

public class MySQLConnection extends Connection {
	public MySQLConnection(String url) {
		super(url);
	}

	public void connect() {
		System.out.println(url + "위치의 MySQL 서버로 연결됐습니다.");
	}
}

OracleConnection extends Connection

public class OracleConnection extends Connection {
	public OracleConnection(String url) {
		super(url);
	}

	public void connect() {
		System.out.println(url + "위치의 오라클 서버로 연결됐습니다.");
	}
}

ConnectionFactory ☆

객체생성만 담당하는 클래스(외부조립기, 컨테이너(객체라이프사이클담당))

public class ConnectionFactory {
	public Connection getConnection(String str) {
		String url = str.toLowerCase();//넘어오는거 소문자로 변경
		//지금은 if문을 이용해서 객체를 가져오지만 자동으로 객체가 만들어질 수 있도록
		//properties파일, xml에 객체를 등록하면 
		//시작될때 파일에 등록된 객체를 자동으로 만들고 시작하도록 구현
		if (url.indexOf("oracle") >= 0) {
			return new OracleConnection(url);//객체생성?
		} else if (url.indexOf("mysql") >= 0) {
			return new MySQLConnection(url);
		} else if (url.indexOf("mongo") >= 0) {
			return new MongoDBConnection(url);
		} else {
			return null;
		}
        if~else문으로 구현한건 할수없이..
	} 	
}

app3

MyBeanStyle 상위클래스

public interface MyBeanStyle {
	void testHello(String name);
}

MyBeanStyleA implements MyBeanStyle

public class MyBeanStyleA implements MyBeanStyle{
	public MyBeanStyleA() {
		System.out.println("기본생성자 - MyBeanStyleA");
	}
	public void testHello(String name) {
		System.out.println("hello.."+name+"!");
	}
}

MyBeanStyleB implements MyBeanStyle

public class MyBeanStyleB implements MyBeanStyle{
	public void testHello(String name) {
		System.out.println("안녕하세요"+name+"님!");
	}
}

MyBeanTest_Spring_BeanFactory

스프링 IoC컨테이너를 이용해서 작업

  • 스프링은 빈(스프링컨테이너가 관리하는 객체)에 대한 설정정보를 xml, 어노테이션, 클래스에 정의
  • BeanFactory컨테이너의 하위클래스를 이용해서 작업
public class MyBeanTest_Spring_BeanFactory {
	public static void main(String[] args) {
		//MyBeanStyleA obj = new MyBeanStyleA();//빈이 아님 그냥 자바객체
		//1. xml을 파싱(xml로 만들어진 설정파일을 분석해서 <bean>으로 등록된 엘리먼트의 설정을 보고 객체를 생성
		//	=> xml을 파싱해주는 기능은 스프링 내부에서 제공되는 객체를 이용
		Resource res = new ClassPathResource("/config/bean.xml");//src부터 찾는다.
		//ClassPathResource는 bean.xml 파일 분석
		//bean.xml에서 A,B바꾸면 코드한줄안바꿔도 교체됨
		System.out.println("+++++++++++++++Resource객체생성후++++++++++++++++++");
		
		//2. 스프링이 제공하는 IoC컨테이너(객체를 생성하고 관리하는 기능을 갖고 있는 클래스 - ConnectionFactory같은 역할을 담당하는 클래스)
		// 클래스를 생성
		// => 객체를 요청하면 Resource객체 내부에서 파싱된 정보를 이용해서 객체를 생성해서 필요한 곳으로 넘겨준다.
		BeanFactory factory = new XmlBeanFactory(res);
		System.out.println("+++++++++++++++IoC 컨테이너 객체생성후++++++++++++++++++");
		
		//3. 원하는 객체를 찾아오기(lookup)
		MyBeanStyle obj = (MyBeanStyle)factory.getBean("mybean");
		MyBeanStyle obj2 = (MyBeanStyle)factory.getBean("mybean");
		if(obj==obj2) {
			System.out.println("같은객체");
		}else {
			System.out.println("다른객체");
		}
		System.out.println("+++++++++++++++getBean호출될때++++++++++++++++++");
		run(obj);
		show(obj);
		change(obj);
		
		
	}
	public static void run(MyBeanStyle obj) {
		System.out.println("====================");
		obj.testHello("BTS");
		System.out.println("====================");
	}
	public static void show(MyBeanStyle obj) {
		obj.testHello("BTS");
		obj.testHello("BTS");
		obj.testHello("BTS");
		obj.testHello("BTS");
		obj.testHello("BTS");
	}
	public static void change(MyBeanStyle obj) {
		System.out.println("********************");
		obj.testHello("BTS");
		obj.testHello("BTS");
		System.out.println("********************");
	}
}

MyBeanTest_Spring_ApplicationContext

스프링 IoC컨테이너를 이용해서 작업

  • 스프링은 빈(스프링컨테이너가 관리하는 객체)에 대한 설정정보를 xml, 어노테이션, 클래스에 정의
  • ApplicationContext를 이용해서 객체를 관리
  • getBean을 이용해서 lookup
public class MyBeanTest_Spring_ApplicationContext {
	public static void main(String[] args) {
		//MyBeanStyleA obj = new MyBeanStyleA();//빈이 아님 그냥 자바객체
		//1. IoC컨테이너를 생성- ApplicationContext
		ApplicationContext factory = new ClassPathXmlApplicationContext("/config/bean.xml");
		System.out.println("+++++++++++++++ApplicationContext 객체생성후++++++++++++++++++");
		//MyBeanStyle타입의 obj가 스프링컨테이너가 관리하는 빈
		MyBeanStyle obj = (MyBeanStyle)factory.getBean("mybean");
		System.out.println("+++++++++++++++ getBean후++++++++++++++++++");
		run(obj);
		show(obj);
		change(obj);
		
	}
	public static void run(MyBeanStyle obj) {
		System.out.println("====================");
		obj.testHello("BTS");
		System.out.println("====================");
	}
	public static void show(MyBeanStyle obj) {
		obj.testHello("BTS");
		obj.testHello("BTS");
		obj.testHello("BTS");
		obj.testHello("BTS");
		obj.testHello("BTS");
	}
	public static void change(MyBeanStyle obj) {
		System.out.println("********************");
		obj.testHello("BTS");
		obj.testHello("BTS");
		System.out.println("********************");
	}
}

bean.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="mybean" class="app3.MyBeanStyleA" scope="prototype"/>  -->
	<!-- scope를 prototype으로 정의하면 요청할때마다 객체를 생성해서 리턴 -->
	<bean id="mybean" class="app3.MyBeanStyleA"/>
	<bean id="account" class="exam01.Account"/>
	<bean id="tv" class="exam02.SamsungTV"/>
</beans>

exam01

첨부된 파일을 Spring 컨테이너로부터 객체를 넘겨 받아 작업할 수 있도록 수정하세요
exam.xml을 생성해서 정의합니다.

Account

public class Account {
	private String accId;
	private long balance=1000000;
	public void setAccId(String accId){
		this.accId = accId;
	}
	public Account() {
		System.out.println("기본생성자 - Account");
	}
	public Account(String accId, long balance) {
		super();
		this.accId = accId;
		this.balance = balance;
		System.out.println("매개변수 2개 생성자 - Account");
	}
	public String getAccId(){
		return this.accId;
	}
	//입금하기
	public void input(long money){
		balance = balance+money;
	}
	//출금하기
	public void output(long money){
		balance = balance-money;
	}
	//잔액조회하기
	public long getBalance(){
		return balance;
	}
}

AccountDemo

public class AccountDemo {
	public static void main(String[] args) {
		Scanner key = new Scanner(System.in);
		ApplicationContext factory = new ClassPathXmlApplicationContext("/config/bean.xml");
		Account acc = (Account)factory.getBean("account");
		acc.setAccId("111-222-3333");
		System.out.println("************은행업무 프로그램**************");
		System.out.println("현재 잔액은:"+acc.getBalance()+"입니다.");
		System.out.println("어떤 작업을 하시겠습니까?");
		System.out.print("1.입금");
		System.out.print("2.출금");
		System.out.println("3.조회");
		System.out.println("원하는 작업을 선택하세요.");
		int num = key.nextInt();
		switch(num){
			case 1:
				System.out.println("입금금액을 입력하세요");
				acc.input(key.nextLong());
				System.out.println(acc.getAccId()
						+"계좌의 현재잔액은 "+acc.getBalance());
				break;
			case 2:
				System.out.println("출금금액을 입력하세요");
				acc.output(key.nextLong());
				System.out.println(acc.getAccId()
						+"계좌의 현재잔액은 "+acc.getBalance());
				break;
			case 3:
				System.out.println(acc.getAccId()
						+"계좌의 현재잔액은 "+acc.getBalance());
				break;
			default:
				System.out.println("잘못선택하셨습니다.");
				System.exit(0);
		}
	}

}

bean.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="mybean" class="app3.MyBeanStyleA" scope="prototype"/>  -->
	<!-- scope를 prototype으로 정의하면 요청할때마다 객체를 생성해서 리턴 -->
	<bean id="mybean" class="app3.MyBeanStyleA"/>
	<bean id="account" class="exam01.Account"/>
	<bean id="tv" class="exam02.SamsungTV"/>
</beans>

exam02

ApplicationContext를 이용해서 작업할 수 있도록 변경하세요
LgTV와 SamsungTV를 바꾸시며 테스트해보세요
exam.xml에 추가합니다.
bean.xml에서 exam01을 처리하신 분들은 xml을 생성해보세요

TV 상위클래스

public interface TV {
	void powerOn();
	void powerOff();
	void volumeUp();
	void volumeDown();
}

SamsungTV implements TV

public class SamsungTV implements TV{
	public SamsungTV() {
		System.out.println("기본생성자 - SamsungTV");
	}
	
	public void powerOn() {
		System.out.println("SamsungTV---전원 켠다.");
	}

	public void powerOff() {
		System.out.println("SamsungTV---전원 끈다.");
	}

	public void volumeUp() {
		System.out.println("SamsungTV---소리 올린다.");
	}

	public void volumeDown() {
		System.out.println("SamsungTV---소리 내린다.");
	}
}

LgTV implements TV

public class LgTV implements TV{
	public void powerOn() {
		System.out.println("LgTV---전원 켠다.");
	}

	public void powerOff() {
		System.out.println("LgTV---전원 끈다.");
	}

	public void volumeUp() {
		System.out.println("LgTV---소리 올린다.");
	}

	public void volumeDown() {
		System.out.println("LgTV---소리 내린다.");
	}
}

TVUser

public class TVUser {
	public static void main(String[] args) {
		ApplicationContext factory 
			= new ClassPathXmlApplicationContext("/config/bean.xml");
		
		TV tv = (TV)factory.getBean("tv");
		tv.powerOn();
		tv.volumeUp();
		tv.volumeDown();
		tv.powerOff();
	}
}

bean.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="mybean" class="app3.MyBeanStyleA" scope="prototype"/>  -->
	<!-- scope를 prototype으로 정의하면 요청할때마다 객체를 생성해서 리턴 -->
	<bean id="mybean" class="app3.MyBeanStyleA"/>
	<bean id="account" class="exam01.Account"/>
	<bean id="tv" class="exam02.SamsungTV"/>
</beans>

DI실습

di.constructor.xml

DI연습하기
생성자방식으로 작업하세요
OOP특성이 적용되도록 수정하세요

AbstractDice 상위클래스

public interface AbstractDice {
	int getDiceValue();
}

AbstractPlayer 상위클래스

public interface AbstractPlayer {
	void play();
	int getTotalValue();
}

Player implements AbstractPlayer

public class Player implements AbstractPlayer{
	AbstractDice d;//상위버전으로 만들어야 유연하게 사용할수있음=유지보수적게해도됨
	int totalValue=0;//totalValue변수는 주사위를 굴리고 얻은 결과가 저장되므로 컨테이너가 생성해주지 않아도 된다.
	public Player() {
		
	}
	public Player(AbstractDice d) {
		super();
		this.d = d;
		System.out.println("매개변수 1개 생성자 - Player");
	}
	public void play(){
		totalValue=0;
		for (int i = 0; i < 3; i++) {
			totalValue+=d.getDiceValue();
		}
	}
	public int getTotalValue(){
		return totalValue;
	}
}

Dice extends Random implements AbstractDice

public class Dice extends Random implements AbstractDice{
	public Dice() {
		System.out.println("기본 생성자 - Dice");
	}
	public int getDiceValue(){
		return nextInt(6)+1;
	}
}

Test01

public class Test01 {
	public static void main(String[] args) {
//		Player p = new Player();
//		p.play();
		
		ApplicationContext factory
			= new ClassPathXmlApplicationContext("/config/constructor.xml");
		AbstractPlayer player = (AbstractPlayer)factory.getBean("player");
//		AbstractPlayer player = factory.getBean("player",AbstractPlayer.class);
//		이렇게도 표현할 수 있다 보여준거. 이러면 캐스팅 안해도됨
		player.play();
		System.out.println("세번 굴린 주사위의 합:"+player.getTotalValue());
	}

}

constructor.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="dao" class="basic.MemberDAO"/>
	<bean id="insa" class="basic.InsaImpl">
		<!-- constructor-arg가 정의된 갯수를 보면서 매개변수가 몇 개인지 파악 
			constructor-arg가 하나면 매개변수 한 개인 생성자를 호출하고
			ref bean="dao"에 의해서 매개변수의 타입이 dao로 등록된 빈의 타입이라는 것을 판단한다.
		-->
		<constructor-arg><!-- 매개변수1개 -->
			<!-- bean의 id가 dao인 빈을 연결 -->
			<ref bean="dao"/>
		</constructor-arg>
	</bean>
	<bean id="dice" class="di.constructor.exam01.Dice"/>
	<bean id="player" class="di.constructor.exam01.Player">
		<constructor-arg ref="dice" /><!-- 위의꺼 약식으로 표현한것 -->
	</bean>
</beans>

본 포스팅은 멀티캠퍼스의 멀티잇 백엔드 개발(Java)의 교육을 수강하고 작성되었습니다.

0개의 댓글