반제품을 이용해서 자신이 원하는 제품을 만드는 것을 상상해보자. 나무집을 만든다고 생각하면 언제 나무를 자르고 페인트칠하고.. 복잡하니깐 반제품, 즉 이미 다 잘려져 있고 물감도 주는 제품을 사서 완제품을 만든다.
이처럼 프로그래밍을 할 때 처음부터 만드는것이 아닌 이미 구축되어있는거에 뭔가를 붙여 완성시키는 것을 Freamwork라고 한다.
인스턴스를 생성하고 소멸되는 과정을 직접하지 않고, 컨테이너가 관리해준다.
Servlet 클래스를 인스턴스화하는 과정을 tomcat이 대신 해준 것과 같다.
리모컨을 생각해보면 거의 모든 리모컨의 디자인이 동일하다. 그래서 어떤 리모컨이던 쉽게 사용할 수 있다. 이러한 디자인을 Interface라고 한다. Interface를 통일하면 TV가 바뀌더라도 쉽게 사용할 수 있다.
TV = new TV() 를 안써도 된다는 말.Java 클래스를 Bean 클래스라고 부른다.
package or.kr.boostcourse;
public class UserBean {
private String name;
private int age;
private boolean male;
public UserBean(){ }
public UserBean(String name, int age, boolean male) {
this.name = name;
this.age = age;
this.male = male;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isMale() {
return male;
}
public void setMale(boolean male) {
this.male = male;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
UserBean 클래스에 getter, setter, 생성자를 만들었다.
<?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="userBean" class="or.kr.boostcourse.UserBean"></bean>
</beans>
resource/applicationContext.xml 파일을 만들고 bean을 등록하였다.
<bean id="userBean" class="or.kr.boostcourse.UserBean"></bean> 이 부분이 중요하다.
package or.kr.boostcourse;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextExam01 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("초기화 완료");
UserBean userBean = (UserBean)ac.getBean("userBean");
userBean.setName("kang");
System.out.println(userBean.getName());
UserBean userBean2 = (UserBean)ac.getBean("userBean");
if(userBean == userBean2) {
System.out.println("same instance");
}
}
}
ApplicationContextExam01에 실제로 Bean을 만들어보았다.
ClassPathXmlApplicationContext에 등록된 Bean들을 모두 생성한다. ApplicationContext의 getBean 파라미터로 Bean을 불러올 수 있고, 리턴은 object 타입으로 되기 때문에 형변환을 해줘야 한다.
Bean은 싱글톤 패턴으로, 하나의 객체만 생성된다. 때문에 userBean == userBean2이다.
객체를 대신 생성해주고, 싱글톤 패턴으로 관리해주는 것을 IoC/DI라고 한다.
public class Engine {
public Engine(){
System.out.println("Engine 생성자");
}
public void exec(){
System.out.println("엔진이 동작합니다.");
}
}
Engine.java
public class Car {
private Engine v8;
public Car() {
System.out.println("Car 생성자");
}
public void setEngine(Engine e) {
this.v8 = e;
}
public void run(){
System.out.println("엔진을 이용하여 달립니다.");
v8.exec();
}
public static void main(String[] args) {
Engine e = new Engine();
Car c = new Car();
c.setEngine(e);
c.run();
}
}
Car.java
Engine 생성자
Car 생성자
엔진을 이용하여 달립니다.
엔진이 동작합니다.
이렇게 작동이 된다.
하지만 Car.java에 있는 Engine e = new Engine(); Car c = new Car(); 이 코드가 마음에 들지 않는다. 이건 spring IoC 컨테이너가 생성해줄 것이다.
이런 과정을 spring이 해줄려면 bean을 등록해주어야 한다.
<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="userBean" class="or.kr.boostcourse.UserBean"></bean>
<bean id="e" class="or.kr.boostcourse.Engine"></bean>
<bean id="c" class="or.kr.boostcourse.Car">
<property name="engine" ref="e"></property>
</bean>
</beans>
ApplicatonContext.xml에 bean을 등록하였다.
Car 인스턴스에 Engine을 set하기 위해 property를 넣어준다.
property는 getter, setter을 의미한다고 했다. 즉, setEngine()을 말하고 있는 것이다. 근데 위의 Car.java를 확인하면 setEngine() 메서드에 Engine을 파라미터로 넣고 있다. 그래서 ref로 아이디가 e로 선언된 Engine을 파라미터로 넣으라고 말하는 것이다.
public class ApplicationContextExam02 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Car car = (Car) ac.getBean("c");
car.run();
}
}
ApplicationContextExam02.java 파일을 만들어 보자.
applicationContext.xml을 읽으면서 bean등록을 했고, getBean()으로 Car를 읽어왔다. 이제 car.run()을 통해,
Engine 생성자
Car 생성자
엔진을 이용하여 달립니다.
엔진이 동작합니다.
이런 결과값을 얻을 수 있다. DI를 설정하여 Engine 클래스가 등장하지 않기 때문에 이는 사용자가 Engine을 알지 못하고, Car만 안다면 된다.
지금까지는 일일이 xml에 bean을 등록하였다. 이를 자동으로 해보자.
@Configuration
public class ApplicationConfig {
@Bean
public Car car(Engine e){
Car c = new Car();
c.setEngine(e);
return c;
}
@Bean
public Engine engine(){
return new Engine();
}
}
public class ApplicationContextExam03 {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);
Car car = (Car) ac.getBean(Car.class);
car.run();
}
}
ac.getBean(Car.class)는 ApplicationContext가 관리하는 객체 중 Car라는 객체가 있으면 그걸 리턴해준다.
@Configuration
@ComponentScan("or.kr.boostcourse")
public class ApplicationConfig2 {
}
@Configuration아래에 @ComponentScan이라는 어노테이션을 추가했다.
@ComponentScan어노테이션은 파라미터로 들어온 패키지 이하에서 @Controller, @Service, @Repository, @Component 어노테이션이 붙어 있는 클래스를 찾아 메모리에 몽땅 올려줍니다.
Spring에서 사용하기에 알맞게 @Controller, @Service, @Repository, @Component 어노테이션이 붙어 있는 객체들은 ComponentScan을 이용해서 읽어들여 메모리에 올리고 DI를 주입하도록 하고, 이러한 어노테이션이 붙어 있지 않은 객체는 @Bean어노테이션을 이용하여 직접 생성해주는 방식으로 클래스들을 관리하면 편리하다.