프레임워크란? 소포트웨어나 애플리케이션 개발을 간단하게 해주는 뼈대라고 할 수 있다. 프레임워크를 사용하면 개발에 필요한 최소한의 기능을 제공하기 때무에 자신이 모든 기능을 작성할 필요가 없고, 애플리케이션 개발에 필요한 시간과 비용을 줄이고 유지보수까지 좋다는 장점을 가지고 있다.
스프링 프레임워크란? 자바 개발 환경에서 사용되는 프레임워크이다. 줄여서 스프링이라고도 한다. 스프링 프레임워크에서는 개발을 편리하게 해주기 위한 여러가지 기능을 제공한다.
스프링 부트, 스프링 MVC, 스프링 데이터, 스프링 DI, 스프링 AOP 등 여러가지 기능을 제공한다. (✏ 이부분에 대한 내용들은 천천히 포스팅을 올리겠다.)
스프링 프레임워크에 대해 공부하기에 앞서 기존에 쓰던 eclipse, visualStudio 등의 도구와 별개로 spring을 위한 도구를 설치해야 한다. ✏ https://spring.io/tools
소스 코드 : https://github.com/junghyeyoun/spring
스프링에서의 핵심 기능은 의존성 주입과 관점 지향 프로그래밍이다. 요번 포스팅에서는 의존성 주입에 대해 알아 보겠다.
의존성 주입이란? 약어로 DI (Dependency Injection) 라고도 부르며, 의존하는 부분을 외부에서 주입한다라는 뜻이다.
어떤 프로그램에서 '사용하는 객체 A'와 '사용되는 객체 B'가 있다고 가정하면 A 클래스에서 B 클래스를 사용하기 위해 new 키워드를 이용해서 B 클래스의 인스턴스를 생성하고 B 클래스의 메소드를 사용할 수 있게 된다. 이런 관계를 'A 클래스가 B 클래스에 의존한다' 라고 할 수 있다.
- 인터페이스를 사용하여 의존성을 만든다.
- 인스턴스를 명시적으로 생성하지 않는다.
- 어노테이션을 클래스에 부여한다.
- 스프링 프레임워크에서 인스턴스를 생성한다.
- 인스턴스를 이용하고 싶은 곳에 어노테이션을 부여한다.
Maven이란? 프로젝트의 전체적인 라이프사이클을 관리하는 관리도구이다. 프로젝트 객체모델(Project Object Model: POM) 이라는 개념을 바탕으로 프로젝트의 의존성관리, 라이브러리관리, 프로젝트 라이프사이클 관리 기능등을 제공하는 프로젝트 관리도구이다. 플러그인을 기반으로 소스코드로부터 배포 가능한 산출물을 만들어내는 빌드 기능을 제공한다.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
dependency 태그를 추가해서 maven library를 사용한다. 이외에도 annotation에 관한 library 등 여러가지가 있지만 지금 상태에서는 이 태그만으로도 충분하다.
1️⃣ 서비스 처리용 인터페이스를 만든다.
추상메소드를 생성한다. 인터페이스는 사용할 메소드에 대한 목록이라고 볼 수도 있다.
2️⃣ 인터페이스이 타입 클래스들을 만든다.
인터페이스를 상속받은 클래스들은 추상메소드의 오버라이딩을 강제로 하기 때문에 추상메소드를 오버라이딩 해주어야 한다. 오버라이딩을 하고 내부적인 시스템은 다를 수 있다. 또한, 생성자나 setter를 이용하여 외부(init.xml)에서 값을 주입할 수도 있다.
init.xml을 사용해야 한다. 이름은 다른걸로 해도 상관 없다. main/resource에 넣고 사용하면 된다. bean 설정 등의 작업을 한다.
<?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">
...
</beans>
<bean id="msgImpl" class="pack.MessageImpl" scope="singleton"></bean>
이런식으로 특정 class의 객체를 생성해 줄 수 있다.
scope은 기본값이 singleton이기 때문에 적어주지 않아도 상관없다. scope에는 prototype, request, session 도 있다. 싱글톤 타입과 달리 주소가 달라진다.
싱글톤 확인
public class ArrangeMain {
public static void main(String[] args) {
// spring이 생성한 객체를 호출 후 실행
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:arrange.xml");
MessageImpl impl1 = new MessageImpl("tom");
impl1.sayHi();
MessageImpl impl2 = new MessageImpl("oscar");
impl2.sayHi();
System.out.println("객체 주소 : "+impl1 + " "+impl2);
// 객체 주소 : pack.MessageImpl@7e6f74c pack.MessageImpl@dd05255 객체가 별도로 생성 : singleton이 아니라는 얘기
System.out.println("---------");
MessageImpl spr_impl1 = (MessageImpl)context.getBean("msgImpl");
spr_impl1.sayHi();
MessageImpl spr_impl2 = (MessageImpl)context.getBean("msgImpl");
spr_impl2.sayHi();
System.out.println("객체 주소 : "+spr_impl1 + " "+spr_impl2);
// 객체 주소(스프링) : pack.MessageImpl@6a78afa0 pack.MessageImpl@6a78afa0 두 변수의 주소가 동일 : singleton이라는 얘기
}
생성자를 이용해 외부에서 값을 주입할 수 있다.
init.xml
<bean id="msgImpl" class="pack.MessageImpl" scope="singleton">
<constructor-arg index="1">
<value>홍길동</value> <!-- index 안써주면 순서대로 들어감 -->
</constructor-arg>
<!-- <constructor-arg index="1"> <value>홍두께</value> </constructor-arg> -->
<constructor-arg index="0" type="java.lang.String"
value="홍두께" /> <!-- type 안적으면 기본이 String임 -->
<constructor-arg index="2" type="int" value="2000" />
<constructor-arg index="3" type="pack.MyInfo"
ref="myInfo" /> <!-- ref에 aa나 bb 써도 됨 -->
</bean>
<bean id="myInfo" name="aa, bb" class="pack.MyInfo" />
클래스의 생성자에 init.xml을 통해 값을 넣어줄 수 있다. 특정 객체 안에 constructor-arg 태그의 value에 값을 넣어줄 수 있다. ref는 다른 객체를 사용할때 사용한다.
setter를 이용해 외부에서 값을 주입할 수 있다.
init.xml
<bean id="msgImpl" class="pack.MessageImpl" scope="singleton">
<property name="spec">
<value>자바 전문 개발자</value>
</property>
<property name="myInfo">
<ref bean="myInfo" />
</property>
<property name="inter">
<ref bean="outDataImpl" />
</property>
</bean>
<bean id="myInfo" name="aa, bb" class="pack.MyInfo" />
<bean id="outDataImpl" class="etc.OutDataImpl">
<property name="filePath" value="c:/work/sprtest.txt"></property>
</bean>
클래스의 setter에 init.xml을 통해 값을 넣어줄 수 있다. 특정 객체 안에 property 태그의 value에 값을 넣어줄 수 있다. ref는 다른 객체를 사용할때 사용한다. property태그의 name에 써야할 것은 멤버변수가 아니다. 예를 들어 setNo이라는 setter가 있다면 no이 name이 되는 것이다.
강결합은 유지보수비가 많이 들기 때문에 클래스를 선언하는 형태의 약결합으로 이용할 수 있다.
public class MessageImpl implements MessageInter{
private MyInfo myInfo;
private String spec;
public void setSpec(String spec) {
this.spec = spec;
}
}
이와 같이 외부의 클래스를 선언하여 사용할 수 있다.
<bean id="msgImpl" class="pack.MessageImpl" scope="singleton">
<property name="myInfo">
<ref bean="myInfo" />
</property>
</bean>
<bean id="myInfo" name="aa, bb" class="pack.MyInfo" />
위에서 언급한 것 처럼 xml에서 property 태그에 ref를 통해서 다른 클래스(객체)를 포함해서 사용할 수 있다.
참고
스프링프레임워크 첫걸음 - 주식회사 후루네스 키노
시타 마사아키 지음, 전민수 옮김
https://spring.io/(스프링 프레임워크 구조 이미지)
https://cheershennah.tistory.com/151(Maven 개념)