본문 바로가기
Spring/자바 객체 지향의 원리와 이해

스프링 삼각형과 설정 정보

by k-mozzi 2023. 9. 11.
반응형
Preface

 

이번 장에선 스프링의 3대 프로그래밍 모델에 대해 공부했다.

 

내용 자체가 복잡하고 어려운 편은 아니라 조금만 생각하면 쉽게 이해하고 넘어갈 수 있었다.

 

SOLID와 디자인패턴 부분을 공부할 땐 의존성이 도대체 무엇을 의미하는 것인지, 의존성을 주입한다는 것이 무슨 뜻인지 이해할 수 없었지만,

 

이번 장을 공부하며 의존성이란 단어에 대해 조금은 익숙해진 것 같다.

 

 

또, 지금껏 자바를 공부하며 기본서에 있는 코드들을 자주 접하고 직접 쳐보기도 했었는데,

 

지금 보니 나는 이미 의존성을 주입하는 다양한 방식들을 모두 경험해봤었다.

 

아는 만큼 보인다는 말이 괜히 있는게 아닌가보다.

 

 

이번 장과 더불어 SOLID, 디자인패턴 장은 두고두고 자주 읽어볼 생각이다.


 

1. IoC/DI

 

 

- IoC(Inversion of Control): 제어의 역전

 

 

- DI(Dipendency Injection): 의존성 주입

 

 

- 의존성이란 전체가 부분에 의존하는 것이라고 표현할 수 있다.

1) 한 요소가 다른 요소를 필요로하는 관계이다.

2) 프로그래밍에서 new로 표현된다.

→ 조금 더 자세히 말하자면, 대입 연산자에 의해 변수에 값이 할당되는 순간 의존이 생긴다.

 

 

- 결국 의존성 주입이란 소프트웨어를 모듈화시키는 것이라고 할 수 있다.

→ 외부에 있는 의존 대상을 주입하는 것이다.

 

 

- 클래스 사이에 인터페이스를 두는 방식은 두 객체 사이의 직접 의존성을 해결하는 방식이다.

 

 

- 스프링 없이 의존성을 주입하는 방법

1) 생성자를 통한 의존성 주입: 인스턴스 생성자의 인자로 객체 참조 변수를 대입

2) 속성을 통한 의존성 주입: 속성 접근자 메서드(Getter/Setter) 사용

 

 

 

- 스프링을 통한 의존성 주입

 

1. XML 파일 사용

1) 특정 값을 입력할 땐 bean 태그를 이용해 등록한다.

→ id 속성과 class 속성을 함께 지정하면 된다.

2) 스프링을 도입할 시 XML 파일만 수정하면 프로그램의 실행 결과를 바꿀 수 있다.

package expert002;

public interface Tire {
	String getBrand();

}

 

package expert002;

public class KoreaTire implements Tire {
	public String getBrand() {
		return "코리아 타이어";
	}
}

 

package expert002;

public class AmericaTire implements Tire {
	public String getBrand() {
		return "미국 타이어";
	}
}

 

package expert002;

public class Car {
	Tire tire;

	public Tire getTire() {
		return tire;
	}

	public void setTire(Tire tire) {
		this.tire = tire;
	}

	public String getTireBrand() {
		return "장착된 타이어: " + tire.getBrand();
	}
}

 

package expert002;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

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

		Car car = context.getBean("car", Car.class);

		Tire tire = context.getBean("tire", Tire.class);

		car.setTire(tire);

		System.out.println(car.getTireBrand());
	}
}

 

<?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="tire" class="expert002.KoreaTire"></bean>

	<bean id="americaTire" class="expert002.AmericaTire"></bean>

	<bean id="car" class="expert002.Car"></bean>

</beans>

 

 

 

2. 스프링 설정 파일(XML)에서 속성 주입

1) 위의 코드에서 Driver.java와 XML 파일만 변경된다.

2) Driver.java의 car.setTire(tire) 부분을 XML 파일의 property 태그로 대체한다.

→ 자동 의존성 주입

package expert003;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

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

		Car car = context.getBean("car", Car.class);

		System.out.println(car.getTireBrand());
	}
}

 

<?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="koreaTire" class="expert003.KoreaTire"></bean>

	<bean id="americaTire" class="expert003.AmericaTire"></bean>

	<bean id="car" class="expert003.Car">
		<property name="tire" ref="koreaTire"></property>
	</bean>

</beans>

 

 

 

3. @Autowired를 통한 속성 주입

1) 설정자 메서드를 이용하지 않고도 스프링이 설정 파일을 통해 설정자 메서드 대신 속성을 주입해준다.

→ 자동 의존성 주입 부분을 생략할 수 있게 된다.

2) XML 파일에서 Spring Config Editor 편집기 하단의 Namespaces에서 context를 체크하면 Autowired 어노테이션 사용을 위한 기본 설정 코드(제일 상위 beans)를 작성해준다.

3) @Autowired은 type 기준 매칭이므로, id보다 type 구현에 우선순위가 있다.

→ 같은 타입을 구현한 클래스가 여러 개일 땐 bean 태그의 id로 구분해서 매칭한다.

package expert004;

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

public class Car {
	@Autowired
	Tire tire;

	public String getTireBrand() {
		return "장착된 타이어: " + tire.getBrand();
	}
}

 

<?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-3.1.xsd">


	<context:annotation-config></context:annotation-config>

	<bean id="tire" class="expert004.KoreaTire"></bean>

	<bean id="americaTire" class="expert004.AmericaTire"></bean>

	<bean id="car" class="expert004.Car"></bean>

</beans>

 

 

 

4. @Resource를 통한 속성 주입

1) 어노테이션 이름 부분을 제외하곤 @Autowired와 완전히 동일하다.

2) @Autowired와는 반대로 id 기준 매칭이다.

package expert004;

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

public class Car {
	@Resource
	Tire tire;

	public String getTireBrand() {
		return "장착된 타이어: " + tire.getBrand();
	}
}

 

 

 

- @Autowired은 스프링의 어노테이션, @Resource는 자바 표준 어노테이션이다.

 

 

- 권고사항

1) @Autowired과 @Resource 중에선 @Resource를 사용하는 것이 좋다.

2) @Resource와 <property> 중에선 <property>를 사용하는 것이 좋다.

 

 

- 설정 파일(XML)을 사용하는 가장 큰 이유: 재컴파일/재배포 없이 프로그램의 실행 결과를 변경할 수 있음

 


 

2. AOP

 

 

- AOP(Aspect-Oriented Programming): 관점 지향 프로그래밍

→ 로직(코드) 주입이라고 할 수 있다.

 

 

- 관심사의 종류

1) 횡단 관심사:  다수의 모듈에 공통적으로 나타나는 부분

2) 핵심 관심사: 모듈마다 다른 부분

 

 

- 객체 지향에서 메서드에 로직(코드)를 주입할 수 있는 부분

1) Around: 메서드 전구역

2) Before: 메서드 시작 전

3) After: 메서드 종료 후

4) AfterReturning: 메서드 정상 종료 후

5) AfterThrowing: 메서드에서 예외가 발생하며 종료된 후

 

 

- AOP 구현 코드

1) @Aspect: 해당 클래스에서 AOP를 사용하겠다는 의미

2) @Before: 대상 메서드 실행 전에 해당 메서드를 실행하겠다는 의미

3) MyAspect 클래스의 JoinPoint는 @Before에서 선언된 메서드인 runSomething( )을 의미한다.

4) XML 파일의 <aop:aspectj-autoproxy />: 프록시 패턴을 이용해 횡단 관심사를 핵심 관심사에 주입하는 것

→ 즉, 스프링에게 AOP 프록시를 사용하라고 알려주는 지시자

package aop002;

public interface Person {
	void runSomething();
}

 

package aop002;

public class Boy implements Person {
	public void runSomething() {
		System.out.println("컴퓨터로 게임을 한다.");
	}
}

 

package aop002;

public class Girl implements Person {
	public void runSomething() {
		System.out.println("화장을 지운다.");
	}
}

 

package aop002;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
//import org.aspectj.lang.annotation.Pointcut;
//import org.springframework.stereotype.Component;

@Aspect
public class MyAspect {
//	@Before("execution(public void aop002.Boy.runSomething())")
	@Before("execution(* runSomething())")
	public void before(JoinPoint joinPoint) {
		System.out.println("얼굴 인식 확인: 문을 개방하라");
	}

//	@After("execution(public void aop002.Girl.runSomething())")
	@After("execution(* runSomething())")
	public void lockDoor(JoinPoint joinPoint) {
		System.out.println("주인님 나갔다: 문 잠가");
	}
}

//
//@Component
//@Aspect
//public class MyAspect {
//	@Pointcut("execution(* runSomething())")
//	private void iampc() {
//		// 여긴 무엇을 작성해도 의미가 없어요.
//	}
//
//	@Before("iampc()")
//	public void before(JoinPoint joinPoint) {
//		System.out.println("얼굴 인식 확인: 문을 개방하라");
//	}
//
//	@After("iampc()")
//	public void lockDoor(JoinPoint joinPoint) {
//		System.out.println("주인님 나갔다: 어이 문 잠가!!!");
//	}
//}

 

package aop002;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Start {

	public static void main(String[] args) {

		ApplicationContext context = new ClassPathXmlApplicationContext("aop002/aop002.xml");

		Person romeo = context.getBean("boy", Person.class);
		Person juliet = context.getBean("girl", Person.class);

		romeo.runSomething();
		juliet.runSomething();
	}

}

 

<?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:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">

	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
	<bean id="myAspect" class="aop002.MyAspect"></bean>
	<bean id="boy" class="aop002.Boy"></bean>
	<bean id="girl" class="aop002.Girl"></bean>
</beans>

 

 

 

- 스프링 AOP의 핵심

1) 인터페이스 기반이다.

2) 프록시(proxy) 기반이다.

3) 런타임 기반이다.

 

 

- AOP 용어 설명

1) Pointcut: 횡단 관심사를 적용할 타깃 메서드를 선택하는 지시자

→ 타깃 클래스의 타깃 메서드 지정자

[접근제한자패턴]리턴타입패턴[패키지&클래스패턴]메서드이름패턴(파라미터패턴)[throws 예외패턴]

2) JoinPoint: Aspect 적용이 가능한 모든 지점

→ 좁은 의미로는 호출된 객체의 메서드이다.

3) Advice: Pointcut에 언제, 무엇을 적용할지 정의한 메서드

4) Aspect: 여러 개의 Advice와 Pointcut의 결합체

→ 언제, 어디에, 무엇을

5) Advisor: 한 개의 Advice와 Pointcut

→ 최근엔 사용되지 않는다.

 


 

3. PSA

 

 

- PSA(Portable Service Abstraction): 일관성 있는 서비스 추상화

→ 어댑터 패턴을 적용해 같은 일을 하는 다수의 기술을 공통 인터페이스로 제어할 수 있게 한 것

 

728x90
반응형

댓글