엔터프라이즈 급 애플리케이션을 만들기 위한 모든 기능을 종합적으로 제공하는 경량화 된 솔루션
JEE(Java Enterprise Edition)가 제공하는 다수의 기능을 지원하고 있기 때문에, JEE를 대체하는 Framework로 자리잡고 있다
SpringFramework는 JEE가 제공하는 다양한 기능을 제공하는 것 뿐만 아니라, DI(Dependency Injection)나 AOP(Aspect Oriented Programming)와 같은 기능도 지원한다
SpringFramework는 자바로 Enterprise Application을 만들 때 포괄적으로 사용하는 Programming 및 Configuration Model을 제공해주는 Framework로 Application 수준의 인프라 스트럭쳐를 제공
개발자가 복잡하고 실수하기 쉬운 Low Level에 신경 쓰지 않고 Business Logic 개발에 전념할 수 있도록 해준다
IoC/DI
객체지향 언어에서 Object간의 연결 관계를 런타임에 결정
객체 간의 관계가 느슨하게 연결됨(loose coupling)
IoC의 구현 방법 중 하나가 DI (Dependency Injection)
컨테이너가 lookup context를 통해서 필요한 Resource나 Object를 얻는 방식
JNDI 이외의 방법을 사용한다면 JNDI 관련 코드를 오브젝트 내에서 일일이 변경해줘야 함
Lookup 한 Object를 필요한 타입으로 Casting 해줘야 함
Naming Exception을 처리하기 위한 로직이 필요
Object에 lookup 코드를 사용하지 않고 컨테이너가 직접 의존 구조를 Object에 설정할 수 있도록 지정해주는 방식
Object가 컨테이너의 존재 여부를 알 필요가 없음
Lookup 관련된 코드들이 Object 내에서 사라짐
Setter Injection과 Constructor Inject
Container란?
Container 기능
오브젝트의 생성과 관계설정, 사용, 제거 등의 작업을 애플리케이션 코드 대신 독립된 컨테이너가 담당
컨테이너가 코드 대신 오브젝트에 대한 제어권을 갖고 있어 IoC라고 부름
이런 이유로, 스프링 컨테이너를 IoC 컨테이너라고 부르기도 함
스프링에서 IoC를 담당하는 컨테이너에는 BeanFactory, ApplicationConext가 있음
Spring DI Container가 관리하는 객체를 Bean이라 하고, 이 빈들의 생명주기를 관리하는 의미로 BeanFactory라 한다
Bean Factory에 여러 가지 컨테이너 기능을 추가하여 ApplicationContext라 한다.
객체 간의 결합을 느슨하게 하는 스프링의 핵심 기술이다
객체 사이의 의존관계를 자기자신이 아닌 외부에 의해서 설정된다는 개념이다
제어의 역행(IoC)이라는 의미로 사용한다
DI 컨테이너는 어떤 클래스가 필요로 하는 인스턴스를 자동으로 생성, 취득하여 연결해주는 역할을 한다
느슨한 결합의 주요강점
HelloApp 에서 MessageBean을 직접 객체 생성하여 사용하고 있다
MessageBean 클래스를 다른 클래스로 변경할 경우 HelloApp의 소스를 같이 수정해주어야 한다
HelloApp은 MessageBean 이라는 인터페이스를 통해서 객체를 사용한다
일반적으로 팩토리 메서드를 활용하여 사용할 객체 (MessageBeanKr or MessageBeanEn)를 생성한다. MessageBean이라는 MessageBeanKr가 생성되든 MessageBeanEn가 생성되든 HelloApp은 수정될 사항이 없다
프로그램에서 필요한 객체를 스프링 컨테이너가 미리 생성하여 이 객체를 필요로 하는 프로그램에 생성자 또는 Setter 메서드를 통해서 주입한다
어떠한 객체를 생성하여 전달할 지는 디스크립터 파일(XML로 작성)을 사용한다
Constructiuon Injection: 생성자를 통해서 의존관계를 연결시키는 것을 말함
Setter Injection: 클래스 사이의 의존관계를 연결시키기 위해서 setter 메소드를 이용하는 방법을 말함
HelloSpring.java
//결합도가 높은 프로그램 연결되어있는 파일을 다 바꿔줘야한다
public class HelloSpring {
public static void main(String[] args) {
MessageBean bean=new MessageBean();
//bean.sayHello(" 스프링!!");
bean.sayHello(" Spring!!");
}
}
MessageBean.java
public class MessageBean {
public void sayHello(String name) {
//System.out.println("헬로우 " + name);
System.out.println("Hello " + name);
}
}
HelloSpring.java
//의존 관계를 약하게 설정하는 프로그램(결합도를 낮춤)
main만 바꿔주면 된다 의존도가 낮아짐
public class HelloSpring {
public static void main(String[] args) {
// MessageBean bean=new MessageBeanKr();
// bean.sayHello(" 스프링!!");
MessageBean bean=new MessageBeanEn();
bean.sayHello(" Spring!!");
}
}
MessageBean.java
public interface MessageBean {
public void sayHello(String name);
}
MessageBeanEn.java
public class MessageBeanEn implements MessageBean{
@Override
public void sayHello(String name) {
System.out.println("Hello," + name);
}
}
MessageBeanKr.java
public class MessageBeanKr implements MessageBean{
@Override
public void sayHello(String name) {
System.out.println("헬로우," + name);
}
}
HelloSpring.java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
//Spring Framework 이용
public class HelloSpring {
public static void main(String[] args) {
ApplicationContext factory = new ClassPathXmlApplicationContext("sample3/bean.xml");
MessageBean bean = (MessageBean)factory.getBean("messageBean2");
bean.sayHello(" Spring");
MessageBean bean2 = (MessageBean)factory.getBean("messageBean1");
bean2.sayHello(" 스프링");
((ClassPathXmlApplicationContext)factory).close();
}
}
MessageBean.java
public interface MessageBean {
public void sayHello(String name);
}
MessageBeanEn.java
public class MessageBeanEn implements MessageBean{
@Override
public void sayHello(String name) {
System.out.println("Hello," + name);
}
}
MessageBeanKr.java
public class MessageBeanKr implements MessageBean{
@Override
public void sayHello(String name) {
System.out.println("헬로우," + name);
}
}
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 = "messageBean1" class = "sample3.MessageBeanKr"/>
<bean id = "messageBean2" class = "sample3.MessageBeanEn"/>
</beans>
HelloSpring.java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class HelloSpring {
public static void main(String[] args) {
//ApplicationContext factory=new ClassPathXmlApplicationContext("sample4/bean1.xml");
ApplicationContext factory=new ClassPathXmlApplicationContext("sample4/bean2.xml");
System.out.println("** container 초기화 작업 **");
MessageBean bean=(MessageBean)factory.getBean("messageBean");
bean.sayHello(); // 주입(DI)
bean.sayHello("banana", 1500); // 대입
((ClassPathXmlApplicationContext)factory).close();
}
}
MessageBean.java (Interface)
public interface MessageBean {
public void sayHello();
public void sayHello(String a, int b);
}
MessageBeanImpl.java
public class MessageBeanImpl implements MessageBean {
private String fruit;
private int cost;
public MessageBeanImpl(String fruit) {
this.fruit = fruit;
System.out.println(fruit+" MessageBeanImpl 생성자");
}
public void setCost(int cost) {
this.cost = cost;
System.out.println("setCost 호출");
}
@Override
public void sayHello() { // XML를 통한 주입 (Melon 출력)
System.out.println(fruit+"\t"+cost);
}
@Override
public void sayHello(String a, int b) { // 메서드에 의한 대입 (Banana 출력)
System.out.println(a+"\t"+b);
}
}
bean1.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 = "messageBean" class = "sample4.MessageBeanImpl">
<constructor-arg>
<value>melon</value>
</constructor-arg>
<property name="cost">
<value>1000</value>
</property>
</bean>
</beans>
<!-- 위 코드와 동일
MessageBeanImpl messageBean = new MessageBeanImpl("melon");
messageBean.setCist(1000);
-->
bean2.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:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="messageBean" class="sample4.MessageBeanImpl" c:fruit="orange" p:cost="2500" />
</beans>
EmpMain.java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class EmpMain {
public static void main(String[] args) {
ApplicationContext factory=new ClassPathXmlApplicationContext("sample6/bean2.xml");
Developer bean=(Developer)factory.getBean("developer");
System.out.println(bean.toString());
Engineer bean2=(Engineer)factory.getBean("engineer");
System.out.println(bean2.toString());
((ClassPathXmlApplicationContext)factory).close();
}
}
Emp.java
public class Emp {
private String name;
private int salary;
public Emp() {
}
public Emp(String name, int salary) {
this.name = name;
this.salary = salary;
}
@Override
public String toString() {
return "이름 : " + name + " 급여 :" + salary+"원 ";
}
}
Developer.java
public class Developer extends Emp{
private String dept;
public Developer(){}
public Developer(String name, int salary) {
super(name,salary);
}
public void setDept(String dept) {
this.dept = dept;
}
@Override
public String toString() {
return super.toString()+" 부서 : " + dept;
}
}
Engineer.java
public class Engineer extends Emp{
private String dept;
public Engineer(){}
public Engineer(String name, int salary) {
super(name,salary);
}
public void setDept(String dept) {
this.dept = dept;
}
@Override
public String toString() {
return super.toString()+ " 부서 : " + dept;
}
}
beans1.java
<?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 = "developer" class = "sample6.Developer">
<constructor-arg value= "강호동" />
<constructor-arg value= "1500000"/>
<property name="dept">
<value>개발1팀(개발부)</value>
</property>
</bean>
<bean id = "engineer" class = "sample6.Engineer">
<constructor-arg value= "이순신" />
<constructor-arg value= "2500000"/>
<property name="dept">
<value>기술1팀(기술부)</value>
</property>
</bean>
</beans>
beans2.java
<?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:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id = "developer" class = "sample6.Developer" c:name="강호동" c:salary="1500000"
p:dept="개발1팀(개발부)" />
<bean id = "engineer" class = "sample6.Engineer" c:name="이순신" c:salary="2500000"
p:dept="기술1팀(기술부)" />
</beans>
EmpMain.java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class EmpMain {
public static void main(String[] args) {
ApplicationContext factory=new ClassPathXmlApplicationContext("sample7/bean1.xml");
Developer bean=(Developer)factory.getBean("developer");
System.out.println(bean.toString());
Engineer bean2=(Engineer)factory.getBean("engineer");
System.out.println(bean2.toString());
((ClassPathXmlApplicationContext)factory).close();
}
}
Emp.java
public class Emp {
private String name;
private int salary;
public Emp() {
}
public Emp(String name, int salary) {
this.name = name;
this.salary = salary;
}
@Override
public String toString() {
return "이름 : " + name + " 급여 :" + salary+"원 ";
}
}
Developer.java
public class Developer{
private String dept;
private Emp emp;
public Developer(){}
public Developer(Emp emp, String dept) {
this.emp = emp;
this.dept = dept;
}
@Override
public String toString() {
return emp.toString()+" 부서 : " + dept;
}
}
Engineer.java
public class Engineer{
private String dept;
private Emp emp = null;
public Engineer(){}
public Engineer(Emp emp, String dept) {
this.emp = emp;
this.dept = dept;
}
@Override
public String toString() {
return emp.toString()+ " 부서 : " + dept;
}
}
beans1.java
<?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="developer" class="sample7.Developer">
<constructor-arg value="Demp" />
<constructor-arg value="개발1팀(개발부)" />
</bean>
<bean id="Demp" class="sample7.Emp">
<constructor-arg value="강호동" />
<constructor-arg value="1500000" />
</bean>
<bean id="engineer" class="sample7.Engineer">
<constructor-arg value="Eemp" />
<constructor-arg value="기술1팀(기술부)" />
</bean>
<bean id="Eemp" class="sample7.Emp">
<constructor-arg value="이순신" />
<constructor-arg value="2500000" />
</bean>
</beans>
beans2.java
<?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:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id = "developer" class = "sample7.Developer" c:emp-ref="Demp"
c:dept="개발1팀(개발부)" />
<bean id = "engineer" class = "sample7.Engineer" c:emp-ref="Eemp"
c:dept="기술1팀(기술부)" />
<bean id="Demp" class="sample7.Emp" c:name="강호동" c:salary="1500000"/>
<bean id="Eemp" class="sample7.Emp" c:name="이순신" c:salary="2500000"/>
</beans>
FoodTest,java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class FoodTest {
public static void main(String[] args) {
ApplicationContext factory=new ClassPathXmlApplicationContext("anno02/bean.xml");
// MyFoodMgr ob=(MyFoodMgr)factory.getBean("myFood");
//@Component로 자동생성된 클래스 명 or 정해준 이름으로 적어줘야한다
MyFoodMgr ob=factory.getBean("myFood", MyFoodMgr.class);
System.out.println(ob);
((ClassPathXmlApplicationContext)factory).close();
}
}
///MyFoodMgr [좋아하는 음식=Food [foodName=스파게티, foodPrice=7500], 싫어하는 음식=Food [foodName=보신탕, foodPrice=12000]]
Food.java
public class Food {
private String foodName;
private int foodPrice;
public void setFoodName(String foodName) {
this.foodName = foodName;
}
public void setFoodPrice(int foodPrice) {
this.foodPrice = foodPrice;
}
@Override
public String toString() {
return "Food [foodName=" + foodName + ", foodPrice=" + foodPrice + "]";
}
}
FoodMgr.java
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component(value = "myFood")
public class MyFoodMgr {
@Autowired
//unfavoriteFood 객체가 favoritefood 객체로
@Qualifier(value="unfavoriteFood") //-> Auto wire로 바꾼 객체를 다른 객체로 바꿀 수 있다
//@Resource(name = "unfavoriteFood")
private Food favoriteFood;
@Autowired
private Food unfavoriteFood;
@Override
public String toString() {
return "MyFoodMgr [좋아하는 음식=" + favoriteFood + ", 싫어하는음식=" + unfavoriteFood + "]";
}
}
출력: 좋아하는 음식 "추어탕" "12000" 싫어하는음식 "추어탕" "12000"
// 기본값: myFoodMgr로 객체 생성 (첫번째 문자만 소문자로 변환되어 객체를 만듦)
//-> (value= "myFood")로 주면 myFood로 생성됨
@Component(value= "myFood")
public class MyFoodMgr {
@Autowired
//<bean id="myFood" class="anno02.MyFoodMgr" p:favoriteFood-ref="favoriteFood">
private Food favoriteFood;
@Autowired
//<bean id="myFood" class="anno02.MyFoodMgr" p:unfavoriteFood-ref="unfavoriteFood">
private Food unfavoriteFood; // @Autowired는 setter를 생략할수 있다
@Override
public String toString() {
return "MyFoodMgr [좋아하는 음식=" + favoriteFood + ", 싫어하는음식=" + unfavoriteFood + "]";
}
}
출력: 좋아하는 음식 "냉면" "6000" 싫어하는음식 "추어탕" "12000"
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"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
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">
<!-- anno02라는 패키지를 스캔하면서 @Component를 만나면 객체를 생성해준다 -->
<context:component-scan base-package = "anno02"/>
<!-- <bean id="myFood" class="anno02.MyFoodMgr"
p:favoriteFood-ref="favoriteFood" p:unfavoriteFood-ref="unfavoriteFood"/> -->
<bean id="favoriteFood" class="anno02.Food" p:foodName="냉면" p:foodPrice="6000"/>
<bean id="unfavoriteFood" class="anno02.Food" p:foodName="추어탕" p:foodPrice="12000"/>
</beans>
<!--
<context:component-scan base-package="anno02"/>
: 특정 패키지 안의 클래스들을 스캔하고 빈 인스턴스를 생성한다.
@Component/@Controller/@Service/@Repository를 사용하려면 이 태그를 넣어줘야 한다.
객체를 지정해주지 않아도 알아서 생성해준다.
base-package는 어느 범위에있는 객체를 생성해 줄것인지를 선언한다
선언된 패키지 내에 있는 객체들을 다 생성해준다.
또한 패키지내의 클래스 위에 @Component/@Controller/@Service/@Repository어노테이션을 써주어야야 한다.
@Component
|
|______________|________________|
| | |
@Controller @Service @Repository
(presentation) (Service) (Persistence/DAO)
<context:annotation-config/>
: 어플리케이션 컨텍스트안에 이미 등록된 빈들의 어노테이션을 활성화를 위해 사용한다.
@Required, @Autowired, @Configuration, @Resource 등
<context:component-scan> 를 선언했다면 <context:annotation-config>를 선언할필요가 없다
-->
EmpTest.java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class EmpTest {
public static void main(String[] args) {
ApplicationContext factory =
new ClassPathXmlApplicationContext("anno03/bean.xml");
Engineer ob=factory.getBean(Engineer.class);
System.out.println(ob.toString());
((ClassPathXmlApplicationContext)factory).close();
}
}
//홍길동은 25세이고 입사점수는 75.4입니다
//개발부에 근무합니다
Emp.java
public class Emp {
private String name;
private int age;
private double score;
public Emp() {
super();
}
public Emp(String name, int age, double score) {
super();
this.name = name;
this.age = age;
this.score = score;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String toString() {
return name+"은 " +age+"세 이고 입사점수는 " + score +"점 입니다";
}
}
Engineer.java
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class Engineer {
@Autowired // Autowired는 type을 따라 간다 (class의)
//얘를 명시해주지 않으면 emp1인지 emp2인지 알 수 없음
//같은 타입일 때 id이름을 명시하지 않으면 ambiguous Error 발생
@Qualifier(value = "emp1")
//or @Resource(name = "emp1")
private Emp emp;
@Autowired
//얘가 없어도 상관x String은 xml에서 하나만 정의 되어있음
@Qualifier(value = "dev_dept")
private String Dept;
// Autowired로 연결 돼서 setter가 필요가 없다
// public void setDept(String dept) {
// this.Dept = dept;
// }
//
// public void setEmp(Emp emp) {
// this.emp = emp;
// }
@Override
public String toString() {
return emp.toString() ;
}
}
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"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
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">
<!-- <bean id = "engineer" class = "anno03.Engineer" p:dept="개발부" p:emp-ref="emp"/> -->
<context:component-scan base-package="anno03"></context:component-scan>
<bean id = "dev_dept" class = "java.lang.String" c:value="개발부"/>
<bean id = "emp1" class= "anno03.Emp" p:name="홍길동" p:age="25" p:score="75.4"/>
<bean id = "emp2" class= "anno03.Emp" p:name="이순신" p:age="27" p:score="80.5"/>
</beans>
<!--
Engineer engineer = new Engineer();
engineer.setDept("개발부");
-->
<!-- @Autowired :스프링 전용
1)의존관계를 자동으로 설정해주는 어노테이션:
Autowired 어노테이션이 붙은 인스턴스 변수는 해당 변수 타입과 일치하는 컨텍스트 내의
빈을 찾아 인스턴스 변수에 주입해 준다
2)타입을 이용하여 의존객체를 자동으로 설정한다(byType to byName)
3)생성자, 필드(변수), 메서드 세곳에 적용가능하다
의존성 주입을 위해선 생성자나 setter가 필요한데 이 어노테이션을 사용할 경우 없어도 가능하다.
@Resource : 자바에서 지원 ,이름으로 연결(byName to byType)
@Qualifier : 빈 객체중 특정빈을 사용하도록 선언
ex)@Autowired는 메서드 앞에서 사용가능하다,
다만 인젝션 가능한 형이 2개 이상이면 오류가 발생한다
이때 @Qualifier를 사용한다
@Inject : 자바에서 지원 ,타입에 맞춰서 연결한다 -->