dependency injection
객체에 대한 생성(초기화), 소멸의 관리
xml
: spring, bean, configuration fileannotation
: (@pathName)java file : POJO
POJO
오래된 방식의 간단한 자바 오브젝트
➡ 특정 기술에 종속되어 동작하는 것이 아닌 순수한 자바 객체
POJO 위키백과
POJO의 개념은 MyBatis에서도 사용해봤다.
xml
로 사용할 때도 있었고, annotation
으로 사용할 때도 있었다.
annotation
을 사용할때 바로 POJO의 개념을 사용했다.
Hello.java
에서 Interface를 구현하고HelloBean1.java
와 HelloBean2.java
의 내용을 실행시켜줄 것이다.BeanConfig.java
의 입력된 내용으로 호출할 방식을 지정해줄 것이다.App.java
에서 내용을 호출해줄 것이다.package com.exam.spring01.model;
public interface Hello {
void sayHello(String name);
}
package com.exam.spring01.model;
public class HelloBean1 implements Hello {
@Override
public void sayHello(String name) {
// TODO Auto-generated method stub
System.out.println(name + "님 안녕하세요");
}
}
package com.exam.spring01.model;
public class HelloBean2 implements Hello {
@Override
public void sayHello(String name) {
// TODO Auto-generated method stub
System.out.println("Hello " + name);
}
}
package com.exam.spring01.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.exam.spring01.model.Hello;
import com.exam.spring01.model.HelloBean1;
import com.exam.spring01.model.HelloBean2;
@Configuration
public class BeanConfig {
@Bean
public HelloBean1 helloBean1() {
return new HelloBean1();
}
@Bean
public HelloBean2 helloBean2() {
return new HelloBean2();
}
@Bean
public Hello hello1() {
return new HelloBean1();
}
@Bean
public Hello hello2() {
return new HelloBean2();
}
}
package com.exam.spring01;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.exam.spring01.config.BeanConfig;
import com.exam.spring01.model.Hello;
import com.exam.spring01.model.HelloBean1;
import com.exam.spring01.model.HelloBean2;
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx
= new AnnotationConfigApplicationContext(BeanConfig.class);
HelloBean1 helloBean1 = (HelloBean1)ctx.getBean("helloBean1");
helloBean1.sayHello("이순신");
HelloBean2 helloBean2 = (HelloBean2)ctx.getBean("helloBean2");
helloBean2.sayHello("강감찬");
// 다형성 활용
Hello hello = (Hello)ctx.getBean("helloBean1");
hello.sayHello("홍길동");
hello = (Hello)ctx.getBean("helloBean2");
hello.sayHello("척준경");
hello = (Hello)ctx.getBean("hello1");
hello.sayHello("허준");
hello = (Hello)ctx.getBean("hello2");
hello.sayHello("허난설헌");
ctx.close();
}
}
App.java의 실행결과
09:59:35.117 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0
09:59:35.131 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
09:59:35.244 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
09:59:35.247 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
09:59:35.249 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
09:59:35.255 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig'
09:59:35.261 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'helloBean1'
09:59:35.278 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'helloBean2'
09:59:35.281 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'hello1'
09:59:35.282 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'hello2'
이순신님 안녕하세요
Hello 강감찬
홍길동님 안녕하세요
Hello 척준경
허준님 안녕하세요
Hello 허난설헌
09:59:35.321 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0, started on Mon Jul 04 09:59:35 KST 2022
이렇게 사용하는 것을 Annotation 기법이라고 한다.
Config 파일의 Method 명이 xml
파일에서의 bean name
과 동일하게 사용되는 것을 확인할 수 있다.
Config
파일에서 Method를 추가하지 않고 App.java
실행시켜 보면 어떻게 될까?
hello = (Hello)ctx.getBean("helloBean3");
hello.sayHello("백경");
실행시켜 보면, No bean name Exception이 발생하는 것을 확인할 수 있다.
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'helloBean3' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:816)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1288)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1109)
at com.exam.spring01.App.main(App.java:34)
그렇다면 등록된 bean으로 변경해서 실행시키면?
hello = (Hello)ctx.getBean("helloBean2");
hello.sayHello("백경");
Exception이 발생하지 않고 정상적으로 실행된다.
Hello 백경
객체의 생성 시점을 지정해줄 수 있다.
프로그램 코드는 인터페이스로 구현받는 부분은 동일하기 때문에 Pass.
package com.exam.spring02.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.exam.spring02.model.Hello;
import com.exam.spring02.model.HelloBean1;
import com.exam.spring02.model.HelloBean2;
@Configuration
public class BeanConfig {
@Bean
public HelloBean1 helloBean1() {
return new HelloBean1();
}
@Bean
@Scope("singleton")
public HelloBean2 helloBean2() {
return new HelloBean2();
}
@Bean
@Scope("prototype")
public HelloBean2 helloBean3() {
return new HelloBean2();
}
}
package com.exam.spring02;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.exam.spring02.model.HelloBean1;
import com.exam.spring02.model.HelloBean2;
import com.exam.spring02.config.BeanConfig;
public class App {
public static void main(String[] args) {
// TODO Auto-generated method stub
AnnotationConfigApplicationContext ctx
= new AnnotationConfigApplicationContext(BeanConfig.class);
// Scope : Default
HelloBean1 helloBean11 = (HelloBean1)ctx.getBean("helloBean1");
HelloBean1 helloBean12 = (HelloBean1)ctx.getBean("helloBean1");
// Scope : singleton
HelloBean2 helloBean21 = (HelloBean2)ctx.getBean("helloBean2");
HelloBean2 helloBean22 = (HelloBean2)ctx.getBean("helloBean2");
// Scope : Prototype
HelloBean2 helloBean31 = (HelloBean2)ctx.getBean("helloBean3");
HelloBean2 helloBean32 = (HelloBean2)ctx.getBean("helloBean3");
System.out.println("helloBean11 : "+helloBean11);
System.out.println("helloBean12 : "+helloBean12);
System.out.println("helloBean21 : "+helloBean21);
System.out.println("helloBean22 : "+helloBean22);
System.out.println("helloBean31 : "+helloBean31);
System.out.println("helloBean32 : "+helloBean32);
ctx.close();
}
}
10:31:19.927 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0
10:31:19.940 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
10:31:20.056 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
10:31:20.058 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
10:31:20.059 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
10:31:20.064 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig'
10:31:20.067 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'helloBean1'
10:31:20.079 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'helloBean2'
helloBean11 : com.exam.spring02.model.HelloBean1@169bb4dd
helloBean12 : com.exam.spring02.model.HelloBean1@169bb4dd
helloBean21 : com.exam.spring02.model.HelloBean2@1f9e9475
helloBean22 : com.exam.spring02.model.HelloBean2@1f9e9475
helloBean31 : com.exam.spring02.model.HelloBean2@3aa078fd
helloBean32 : com.exam.spring02.model.HelloBean2@d23e042
10:31:20.115 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0, started on Mon Jul 04 10:31:19 KST 2022
실행결과를 확인해보면,
Scope
를 입력하지 않은 bean
은 두 객체의 주소값이 동일하다.
Scope
를 Singleton
으로 입력한 bean
도 마찬가지다.
그런데 Scope
를 Prototype
으로 입력한 bean
은 두 객체의 주소값이 틀린 것을 확인할 수 있다.
Annotation
으로 생성자를 주입해보자.
package com.exam.spring03.model;
public class HelloBean {
private String name;
public HelloBean() {
// TODO Auto-generated constructor stub
System.out.println("HelloBean() 호출");
this.name = "홍길동";
}
public HelloBean(String name) {
System.out.println("HelloBean(String name) 호출");
this.name = name;
}
public void sayHello() {
System.out.println(name + "님 안녕하세요");
}
}
package com.exam.spring03.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.exam.spring03.model.HelloBean;
@Configuration
public class BeanConfig {
@Bean
public HelloBean helloBean1() {
return new HelloBean();
}
@Bean
public HelloBean helloBean2() {
return new HelloBean("척준경");
}
}
package com.exam.spring03;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.exam.spring03.config.BeanConfig;
import com.exam.spring03.model.HelloBean;
public class App {
public static void main(String[] args) {
// TODO Auto-generated method stub
AnnotationConfigApplicationContext ctx
= new AnnotationConfigApplicationContext(BeanConfig.class);
HelloBean helloBean1 = (HelloBean)ctx.getBean("helloBean1");
helloBean1.sayHello();
HelloBean helloBean2 = (HelloBean)ctx.getBean("helloBean2");
helloBean2.sayHello();
ctx.close();
}
}
10:42:10.391 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0
10:42:10.407 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
10:42:10.513 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
10:42:10.515 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
10:42:10.516 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
10:42:10.521 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig'
10:42:10.525 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'helloBean1'
HelloBean() 호출
10:42:10.543 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'helloBean2'
HelloBean(String name) 호출
홍길동님 안녕하세요
척준경님 안녕하세요
10:42:10.569 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0, started on Mon Jul 04 10:42:10 KST 2022
실행 결과를 보면서 다시 코드를 확인해보자.
홍길동
이 결과로 나온 경우홍길동
으로 지정해줬다.척준경
이 경과로 나온 경우척준경
으로 값을 주입해주었다.여기까지 배웠던 Annotation 기법을 활용해보자.
어제 배웠던 xml
파일로 생성했던 프로젝트 중에서,
게시판 형식을 annotation
으로 구현해보자.
DTO
: BoardTO.java
DAO
: WriteAction.java
Config
: BeanConfig.java
호출
: App.java
package com.exam.spring04.model;
public class BoardTO {
private int seq;
private String subject;
public int getSeq() {
return seq;
}
public void setSeq(int seq) {
System.out.println("setSeq(int seq)");
this.seq = seq;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
System.out.println("setSubject(String subject)");
this.subject = subject;
}
}
package com.exam.spring04.model;
public class WriteAction {
private BoardTO to;
public BoardTO getTo() {
return to;
}
public void setTo(BoardTO to) {
this.to = to;
}
public void execute() {
System.out.println("execute() 호출");
System.out.println(to.getSeq());
System.out.println(to.getSubject());
}
}
package com.exam.spring04.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.exam.spring04.model.BoardTO;
import com.exam.spring04.model.WriteAction;
@Configuration
public class BeanConfig {
private BoardTO dto;
@Bean
public WriteAction action() {
this.dto = new BoardTO();
this.dto.setSeq(1);
this.dto.setSubject("제목");
WriteAction writeAction = new WriteAction();
writeAction.setTo(this.dto);
return writeAction;
}
}
package com.exam.spring04;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.exam.spring04.config.BeanConfig;
import com.exam.spring04.model.WriteAction;
public class App {
public static void main(String[] args) {
// TODO Auto-generated method stub
AnnotationConfigApplicationContext ctx
= new AnnotationConfigApplicationContext(BeanConfig.class);
WriteAction action = (WriteAction)ctx.getBean("action");
action.execute();
ctx.close();
}
}
10:58:44.789 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0
10:58:44.802 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
10:58:44.899 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
10:58:44.901 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
10:58:44.903 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
10:58:44.908 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig'
10:58:44.911 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
setSeq(int seq)
setSubject(String subject)
execute() 호출
1
제목
10:58:44.955 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0, started on Mon Jul 04 10:58:44 KST 2022
xml
파일로 생성했을때와 동일하게 구현이 되었다.
코드를 보면서 실행결과를 다시 되짚어 보자.
BeanConfig.java
에서 BoardTO를 인스턴스화 하고seq
값과 subject
값을 강제로 넣어주었다.action
)을 호출시에는 방금 값이 들어간 WriteAction을 return
하도록 한다.App.java
AnnotationConfigApplicationContext
를 사용해 BeanConfig.java
를 클래스화 해서 가져왔다.action
이라는 이름의 Bean을 호출해서 가져온다.action
의 메서드를 실행시켰다.annotation 기법을 사용해서 DB와 연동하는 프로그램을 생성한다.
package com.exam.spring01.model2;
public interface BoardAction {
void execute();
}
package com.exam.spring01.model2;
import java.util.ArrayList;
import com.exam.spring01.model1.BoardDAO;
public class ListAction implements BoardAction {
private BoardDAO dao;
public ListAction(BoardDAO dao) {
// TODO Auto-generated method stub
this.dao = dao;
}
@Override
public void execute() {
// TODO Auto-generated method stub
ArrayList<String> lists = dao.boardList();
for(String list : lists) {
System.out.println(list);
}
}
}
package com.exam.spring01.model1;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
public class BoardDAO {
private Connection conn;
public BoardDAO() {
// TODO Auto-generated constructor stub
String url = "jdbc:mariadb://localhost:3306/board";
String user = "board";
String password = "!123456";
try {
Class.forName("org.mariadb.jdbc.Driver");
this.conn = DriverManager.getConnection(url, user, password);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
System.out.println("[Error] "+e.getMessage());
} catch (SQLException e) {
// TODO Auto-generated catch block
System.out.println("[Error] "+e.getMessage());
}
}
public ArrayList<String> boardList() {
PreparedStatement pstmt = null;
ResultSet rs = null;
ArrayList<String> lists = new ArrayList<String>();
try {
String sql = "SELECT * "
+ "FROM board1 "
+ "LIMIT 0,5 ";
pstmt = this.conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while(rs.next()) {
lists.add(String.format("%s [%s] %s",
rs.getString("seq"),
rs.getString("writer"),
rs.getString("subject")));
}
} catch(SQLException e) {
// TODO Auto-generated catch block
System.out.println("[Error] "+e.getMessage());
} finally {
if(rs != null) try {rs.close();} catch(SQLException e) {}
if(pstmt != null) try {pstmt.close();} catch(SQLException e) {}
if(conn != null) try {conn.close();} catch(SQLException e) {}
}
return lists;
}
}
package com.exam.spring01.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.exam.spring01.model1.BoardDAO;
import com.exam.spring01.model2.ListAction;
@Configuration
@Scope("prototype")
public class BeanConfig {
public BoardDAO dao() {
return new BoardDAO();
}
@Bean
public ListAction action() {
return new ListAction(dao());
}
}
package com.exam.spring01;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.exam.spring01.config.BeanConfig;
import com.exam.spring01.model2.BoardAction;
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx
= new AnnotationConfigApplicationContext(BeanConfig.class);
BoardAction action = (BoardAction)ctx.getBean("action");
action.execute();
ctx.close();
}
}
11:48:45.907 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0
11:48:45.921 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
11:48:46.021 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
11:48:46.024 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
11:48:46.025 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
11:48:46.030 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'action'
1 [이름] 제목
3 [test1] subject11
5 [글쓴이당] 제목이당33
6 [추가추가글쓴이를] 추가한다제목을
8 [22] 11
11:48:46.198 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0, started on Mon Jul 04 11:48:45 KST 2022
작성한 코드를 보면서 다시 확인해보자.
1. BoardAction
인터페이스를 만들어줬다.
2. BoardAction을 구현한 ListAction.java
을 만들었다.
3. DB 와 연동하여 데이터를 가져올 코드인 BoardDAO.java
를 생성
4. BeanConfig.java
에서 BoardDAO를 호출하고 ListAction에 넣어준다.
ListAction은
5. 4️⃣ 항목을 어노테이션으로 Bean 지정해준다.
6. App.java
에서 AnnotationConfigApplicationContext
을 통하여 Bean을 호출한다.
Spring Container
의 관리 항목Bean Object
를 생성Property
를 할당Spring Container
의 관리 방식Spring
이 제공하는 특정 인터페이스를 상속받아 Bean
을 구현Spring
설정에서 특정 메서드 호출을 지정Spring은 Life Cycle에 특화된 인터페이스를 제공한다.
ex)
Bean 객체의 클래스가 InitalizingBean 인터페이스를 구현하고 있으면 InitalizingBean 인터페이스에 정의된 메서드를 호출해서 Bean 객체가 초기화를 진행할 수 있도록 한다.
또한 스프링 설정에서 초기화 메서드를 지정하면 스프링은 그 메서드를 호출해서 Bean이 초기화를 수행할 수 있도록 한다.
참고한 블로그 : Spring - 빈 객체의 라이프 사이클
참고하면 좋을 영문 사이트 : Spring Bean Life Cycle
Spring이 객체를 만들기 위해서 흘러가는 과정을 확인해 보자.
package com.exam.spring01.model;
public interface BoardAction {
void execute();
}
package com.exam.spring01.model;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class WriteAction implements BoardAction, BeanNameAware, BeanClassLoaderAware,
BeanFactoryAware, ApplicationContextAware, DisposableBean, InitializingBean {
private String writer;
public WriteAction() {
// TODO Auto-generated constructor stub
System.out.println("1. 빈의 생성자 실행");
}
public void setWriter(String writer) {
System.out.println("2. setWriter(String Writer) 실행");
this.writer = writer;
}
@Override
public void execute() {
// TODO Auto-generated method stub
System.out.println("*. execute() 실행");
System.out.println(this.writer);
}
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("8. afterPropertiesSet() 실행");
}
@Override
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("11. destroy() 실행");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// TODO Auto-generated method stub
System.out.println("6. setApplicationContext(ApplicationContext applicationContext) 실행");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
// TODO Auto-generated method stub
System.out.println("5. setBeanFactory(BeanFactory beanFactory) 실행");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
// TODO Auto-generated method stub
System.out.println("4. setBeanClassLoader(ClassLoader classLoader) 실행");
}
@Override
public void setBeanName(String name) {
// TODO Auto-generated method stub
System.out.println("3. setBeanName(String name) 실행");
}
}
package com.exam.spring01.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.exam.spring01.model.WriteAction;
@Configuration
@Scope("prototype")
public class BeanConfig {
@Bean
public WriteAction writeAction() {
WriteAction action = new WriteAction();
action.setWriter("홍길동");
return action;
}
}
package com.exam.spring01;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.exam.spring01.config.BeanConfig;
import com.exam.spring01.model.BoardAction;
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx
= new AnnotationConfigApplicationContext(BeanConfig.class);
BoardAction action = (BoardAction)ctx.getBean("writeAction");
action.execute();
ctx.close();
}
}
12:33:32.073 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0
12:33:32.089 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
12:33:32.208 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
12:33:32.210 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
12:33:32.212 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
12:33:32.216 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'writeAction'
1. 빈의 생성자 실행
2. setWriter(String Writer) 실행
3. setBeanName(String name) 실행
4. setBeanClassLoader(ClassLoader classLoader) 실행
5. setBeanFactory(BeanFactory beanFactory) 실행
6. setApplicationContext(ApplicationContext applicationContext) 실행
8. afterPropertiesSet() 실행
*. execute() 실행
홍길동
12:33:32.269 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0, started on Mon Jul 04 12:33:32 KST 2022
11. destroy() 실행
코드와 실행결과를 비교해보자.
내가 호출은 안해도 Bean 객체의 Life Cycle과 관련된 메서드는 Spring이 자동적으로 호출해주는 것을 확인할 수 있다.
Spring Bean Life Cycle 사진 출저
1. IoC 컨테이너 내에서 클래스 생성자 를 사용하여 스프링 빈을 생성한다.
2. 이제 setter 메서드를 사용하여 종속성 주입을 수행한다.
3. 의존성 주입이 완료되면 BeanNameAware.setBeanName()
가 호출된다.
이 Bean을 생성한 Bean Factory에 Bean의 이름을 설정한다.
4. 이제 <code>BeanClassLoaderAware.setBeanClassLoader()
가 호출되어 빈 인스턴스에 빈 클래스 로더를 제공한다.
5. 이제 빈 인스턴스에 소유 팩토리를 제공하는 <code>BeanFactoryAware.setBeanFactory()
가 호출된다.
6. 이제 IoC 컨테이너가 .BeanPostProcessor.postProcessBeforeInitialization
를 Bean에 호출한다.
이 방법을 사용하면 원래 빈에 래퍼를 적용할 수 있다.
7. 이제 어노테이션 @PostConstruct
가 호출된다.
8. 이후 @PostConstruct
메소드 InitializingBean.afterPropertiesSet()
가 호출된다.
9.init-method 이제 XML 설정에서 Bean의 속성으로 지정한 메소드 를 호출한다.
10. 그리고 나서 BeanPostProcessor.postProcessAfterInitialization()
호출된다.
또한 Bean에 래퍼를 적용하는 데 사용할 수도 있다.
11. 이제 빈 인스턴스를 사용할 준비가 되었다. Bean을 사용하여 작업을 수행한다.
12. 이제 다음 ApplicationContext
을 사용하여 종료할 때 registerShutdownHook()
어노테이션 @PreDestroy
가 호출된다.
13. DisposableBean.destroy()
메소드가 bean에서 호출된 후
14. 이제 XML 설정에서 Bean의 속성으로 지정된 메소드 destroy-method
가 호출된다.
15. garbage conllection
전에 finalize()
의 메소드 Object
가 호출된다.
간략하게 정리한 것으로 보자.
package com.exam.spring01.model;
import org.springframework.beans.BeansException;
… 생략 …
import org.springframework.context.ApplicationContextAware;
public class WriteAction implements BoardAction, BeanNameAware, BeanClassLoaderAware,
BeanFactoryAware, ApplicationContextAware, DisposableBean, InitializingBean {
private String writer;
@Override
public void execute() {
// TODO Auto-generated method stub
System.out.println("*. execute() 실행");
System.out.println(this.writer);
}
… 생략 …
// 전처리
public void init_method() {
System.out.println("9. init_method() 전처리메서드 실행");
}
// 후처리
public void destory_method() {
System.out.println("12. destory_method() 후처리메서드 실행");
}
}
package com.exam.spring01.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.exam.spring01.model.WriteAction;
@Configuration
@Scope("prototype")
public class BeanConfig {
@Bean(initMethod = "init_method", destroyMethod = "destory_method")
public WriteAction writeAction() {
WriteAction action = new WriteAction();
action.setWriter("홍길동");
return action;
}
}
14:23:26.636 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0
14:23:26.651 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
14:23:26.751 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
14:23:26.753 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
14:23:26.754 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
14:23:26.758 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'writeAction'
1. 빈의 생성자 실행
2. setWriter(String Writer) 실행
3. setBeanName(String name) 실행
setBeanName : writeAction
4. setBeanClassLoader(ClassLoader classLoader) 실행
5. setBeanFactory(BeanFactory beanFactory) 실행
6. setApplicationContext(ApplicationContext applicationContext) 실행
8. afterPropertiesSet() 실행
9. init_method() 전처리메서드 실행
*. execute() 실행
홍길동
11. destroy() 실행
12. destory_method() 후처리메서드 실행
14:23:26.804 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0, started on Mon Jul 04 14:23:26 KST 2022
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-4.3.xsd">
<bean id="writeAction" class="com.exam.spring01.model.WriteAction" scope="prototype" init-method="init_method" destroy-method="destroy_method">
<property name="writer" value="홍길동"></property>
</bean>
</beans>
package com.exam.spring01;
import org.springframework.context.support.GenericXmlApplicationContext;
import com.exam.spring01.model.BoardAction;
public class App02 {
public static void main(String[] args) {
GenericXmlApplicationContext ctx
= new GenericXmlApplicationContext("classpath:com/exam/spring01/context.xml");
BoardAction action = (BoardAction)ctx.getBean("writeAction");
action.execute();
ctx.removeBeanDefinition("writeAction");
ctx.close();
}
}
14:41:35.592 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [com/exam/spring01/context.xml]
14:41:35.594 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@1d7acb34
1. 빈의 생성자 실행
2. setWriter(String Writer) 실행
3. setBeanName(String name) 실행
3. writeAction
4. setBeanClassLoader(ClassLoader classLoader) 실행
5. setBeanFactory(BeanFactory beanFactory) 실행
6. setApplicationContext(ApplicationContext applicationContext) 실행
8. afterPropertiesSet() 실행
9. init_method() 전처리메서드 실행
*. execute() 실행
홍길동
14:41:35.711 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@1d7acb34, started on Mon Jul 04 14:41:35 KST 2022
왜 어노테이션@
과는 다르게 xml
로 실행하면 destroy가 보이지 않을까?
강사님 말로는 시점 차이라고 한다.
따라서 destroy를 사용할때는 어노테이션 말고 xml로 하는 것이 좋다고 한다.
package com.exam.spring02.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("초기화 전처리");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("초기화 후처리");
return bean;
}
}
package com.exam.spring02.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.exam.spring02.model.WriteAction;
@Configuration
@ComponentScan(basePackageClasses = {CustomBeanPostProcessor.class})
public class BeanConfig {
@Bean()
@Scope("prototype")
public WriteAction writeAction() {
WriteAction action = new WriteAction();
action.setWriter("홍길동");
return action;
}
}
15:18:05.854 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0
15:18:05.868 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
15:18:05.921 [main] DEBUG o.s.c.a.ClassPathBeanDefinitionScanner - Identified candidate component class: file [D:\Java\spring-workspace\spring07\target\classes\com\exam\spring02\config\CustomBeanPostProcessor.class]
15:18:06.006 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
15:18:06.008 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
15:18:06.009 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
15:18:06.012 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'customBeanPostProcessor'
15:18:06.020 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig'
초기화 전처리
초기화 후처리
1. 빈의 생성자 실행
2. setWriter(String Writer) 실행
초기화 전처리
초기화 후처리
*. execute() 실행
홍길동
15:18:06.057 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0, started on Mon Jul 04 15:18:05 KST 2022
package com.exam.spring01.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.exam.spring01.model.WriteAction;
@Configuration
public class BeanConfig01 {
@Bean
public WriteAction writeAction() {
return new WriteAction();
}
}
package com.exam.spring01.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.exam.spring01.model.ListAction;
@Configuration
public class BeanConfig02 {
@Bean
public ListAction listAction() {
return new ListAction();
}
}
package com.exam.spring01;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.exam.spring01.config.BeanConfig01;
import com.exam.spring01.config.BeanConfig02;
import com.exam.spring01.model.ListAction;
import com.exam.spring01.model.WriteAction;
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx
= new AnnotationConfigApplicationContext(BeanConfig01.class, BeanConfig02.class);
WriteAction writeAction = (WriteAction)ctx.getBean("writeAction");
writeAction.execute();
ListAction listAction = (ListAction)ctx.getBean("listAction");
listAction.execute();
ctx.close();
}
}
15:29:31.502 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0
15:29:31.516 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
15:29:31.619 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
15:29:31.622 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
15:29:31.623 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
15:29:31.628 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig01'
15:29:31.631 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig02'
15:29:31.632 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'writeAction'
WriteAction() 실행
15:29:31.646 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'listAction'
ListAction() 실행
execute() 실행
execute() 실행
15:29:31.686 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0, started on Mon Jul 04 15:29:31 KST 2022
@Autowired
의 개념package com.exam.spring02.model;
public class BoardDAO {
public BoardDAO() {
// TODO Auto-generated constructor stub
System.out.println("BoardDAO() 실행");
}
}
package com.exam.spring02.model;
public class WriteAction {
public WriteAction() {
// TODO Auto-generated constructor stub
System.out.println("WriteAction() 실행");
}
public WriteAction(BoardDAO dao) {
// TODO Auto-generated constructor stub
System.out.println("WriteAction(BoardDAO dao) 실행");
}
}
package com.exam.spring02.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.exam.spring02.model.BoardDAO;
@Configuration
public class BeanConfig01 {
@Bean
public BoardDAO dao() {
return new BoardDAO();
}
}
package com.exam.spring02.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.exam.spring02.model.BoardDAO;
import com.exam.spring02.model.WriteAction;
@Configuration
public class BeanConfig02 {
// new(X)
@Autowired
private BoardDAO dao;
@Bean
public WriteAction writeAction1() {
return new WriteAction();
}
@Bean
public WriteAction writeAction2() {
return new WriteAction(dao);
}
}
package com.exam.spring02;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.exam.spring02.config.BeanConfig01;
import com.exam.spring02.config.BeanConfig02;
import com.exam.spring02.model.WriteAction;
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx
= new AnnotationConfigApplicationContext(BeanConfig01.class, BeanConfig02.class);
WriteAction writeAction1 = (WriteAction)ctx.getBean("writeAction1");
WriteAction writeAction2 = (WriteAction)ctx.getBean("writeAction2");
ctx.close();
}
}
15:48:50.349 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0
15:48:50.365 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
15:48:50.478 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
15:48:50.480 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
15:48:50.482 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
15:48:50.487 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig01'
15:48:50.489 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig02'
15:48:50.505 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'dao'
BoardDAO() 실행
15:48:50.517 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'writeAction1'
WriteAction() 실행
15:48:50.526 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'writeAction2'
WriteAction(BoardDAO dao) 실행
15:48:50.551 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0, started on Mon Jul 04 15:48:50 KST 2022
BeanConfig02.java에서는 @Bean
으로 BoardDAO 객체 생성을 해주지 않았음에도 불구하고,
해당 객체를 writeAction2에서 사용하는 것을 확인할 수 있다.
Autowire
의 호출 관계를 알아보자.BeanConfig01.java 를 다음과 같이 수정한다.
package com.exam.spring02.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.exam.spring02.model.BoardDAO;
@Configuration
public class BeanConfig01 {
@Bean
public BoardDAO dao() {
System.out.println("dao() 호출");
return new BoardDAO();
}
}
BeanConfig02.java 를 다음과 같이 수정한다.
package com.exam.spring02.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.exam.spring02.model.BoardDAO;
import com.exam.spring02.model.WriteAction;
@Configuration
public class BeanConfig02 {
// new(X)
//@Autowired
private BoardDAO dao;
@Bean
public WriteAction writeAction1() {
return new WriteAction();
}
@Bean
public WriteAction writeAction2() {
System.out.println(dao.toString());
return new WriteAction(dao);
}
}
실행시키면?
16:11:07.077 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0
16:11:07.090 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
16:11:07.200 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
16:11:07.203 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
16:11:07.205 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
16:11:07.210 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig01'
16:11:07.213 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig02'
16:11:07.214 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'dao'
dao() 호출
BoardDAO() 실행
16:11:07.228 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'writeAction1'
WriteAction() 실행
16:11:07.232 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'writeAction2'
16:11:07.236 [main] WARN o.s.c.a.AnnotationConfigApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'writeAction2' defined in com.exam.spring02.config.BeanConfig02: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.exam.spring02.model.WriteAction]: Factory method 'writeAction2' threw exception; nested exception is java.lang.NullPointerException: Cannot invoke "Object.toString()" because "this.dao" is null
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'writeAction2' defined in com.exam.spring02.config.BeanConfig02: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.exam.spring02.model.WriteAction]: Factory method 'writeAction2' threw exception; nested exception is java.lang.NullPointerException: Cannot invoke "Object.toString()" because "this.dao" is null
… 이후 Exception 내용 생략 …
Exception 에러가 발생한다.
하지만 그 전에, BoardDAO()가 실행되기 전에 dao()를 먼저 호출해주는 것을 볼 수 있다.
이제 다시 Autowire를 활성화 한 후, 프로그램을 재실행 해보자.
16:12:27.472 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0
16:12:27.485 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
16:12:27.612 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
16:12:27.614 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
16:12:27.615 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
16:12:27.620 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig01'
16:12:27.622 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanConfig02'
16:12:27.635 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'dao'
dao() 호출
BoardDAO() 실행
16:12:27.648 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'writeAction1'
WriteAction() 실행
16:12:27.652 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'writeAction2'
com.exam.spring02.model.BoardDAO@2dfaea86
WriteAction(BoardDAO dao) 실행
16:12:27.677 [main] DEBUG o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4f51b3e0, started on Mon Jul 04 16:12:27 KST 2022
정상적으로 실행되는 것을 확인할 수 있다.
AOP
란?여러 객체에 공통으로 적용할 수 있는 기능을 분리하여 재사용성을 높여주는 프로그래밍 기법.
AOP는 다음 두 가지의 기능 구현을 분리힌다.
AOP
의 목적핵심 기능을 구현한 코드의 수정 없이 공통 기능을 적용할 수 있도록 만들어 주는 것
핵심 기능의 코드는 수정하지 않으면서 공통 기능의 구현을 추가하는 것.
참고 블로그 : AOP 프로그래밍 분석하기 tistory
AOP
의 구현법xml
@
용어 | 의미 |
---|---|
Advice | 공통 기능을 핵심 로직에 어느 시점에 적용할지를 정의 ex) 메서드 호출 전, 메서드 종료 후 |
Weaving | Advice를 핵심 로직 코드에 적용하는 것 |
Aspect | 공통 기능을 의미. 여러 객체에 공통으로 적용되는 기능을 Aspect 라고 함ex) 트랜잭션 시작, 트랜잭션 종료, 수행 시간 구하기 |
Jointpoint | Advice를 적용 가능한 지점을 의미한다. 메서드 호출, 필드 값 변경 등이 Joinpoint에 해당. 스프링은 Proxy를 이용해서 AOP를 구현하기 때문에 메서드 호출에 대한 Joinpoint만 지원함. |
용어 | 설명 |
---|---|
Before Advice | 대상 객체의 메서드 호출 전에 공통 기능을 실행한다. |
After Returning Advice | 대상 객체의 메서드가 익셉션 없이 실행된 이후에 공통 기능을 실행한다. |
After Throwing Advice | 대상 객체의 메서드를 실행하는 도중 익셉션이 발생한 경우에 공통 기능을 실행한다. |
After Advice | 익셉션 발생 여부에 상관없이 대상 객체의 메서드 실행 후 공통 기능을 실행한다. |
Around Advice | 대상 객체의 메서드를 실행 전, 후 또는 익셉션 발생 시점에 공통 기능을 실행한다. |
스프링은 Proxy를 이용해서 메서드를 호출 시점에 Aspect(공통 기능)를 적용한다.
물론 Around Advice가 가장 널리 사용되고 있다.
참고 블로그 : AOP 프로그래밍 분석하기 tistory
xml
Proxy를 이용한 전처리와 후처리package com.exam.aop01;
public interface BoardAction {
void execute();
}
package com.exam.aop01;
public class WriteAction implements BoardAction {
private String writer;
public WriteAction() {
// TODO Auto-generated constructor stub
System.out.println("WriteAction() 실행");
}
public void setWriter(String name) {
this.writer = name;
}
@Override
public void execute() {
// TODO Auto-generated method stub
System.out.println("execute() 시작");
System.out.println("writer : " + this.writer);
System.out.println("execute() 종료");
}
}
package com.exam.aop01.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class BasicAdvice1 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// TODO Auto-generated method stub
System.out.println("전처리 : 공통 관심");
Object rtnObj = invocation.proceed();
System.out.println("후처리 : 공통 관심");
return null;
}
}
<?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-4.3.xsd">
<!-- AOP 에 대한 인스턴스화 -->
<bean id="basicAdvice1" class="com.exam.aop01.advice.BasicAdvice1" />
<!-- 내부꺼를 설정 위치를 찾아준다. -->
<bean id="pointcutAdvisor1" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="basicAdvice1" />
<property name="pointcut">
<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*execute.*"></property>
</bean>
</property>
</bean>
<!-- 기존꺼 -->
<!-- <bean id="writeAction" class="com.exam.aop01.WriteAction" scope="prototype"/> -->
<bean id="writeAction" class="com.exam.aop01.WriteAction" scope="prototype">
<property name="writer" value="홍길동" />
</bean>
<!-- AOP 적용 -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="writeAction"></property>
<property name="interceptorNames">
<list>
<value>pointcutAdvisor1</value>
</list>
</property>
</bean>
</beans>
package com.exam.aop01;
import org.springframework.context.support.GenericXmlApplicationContext;
public class App {
public static void main(String[] args) {
GenericXmlApplicationContext ctx
= new GenericXmlApplicationContext("classpath:com/exam/aop01/context.xml");
//BoardAction action = (BoardAction)ctx.getBean("writeAction");
BoardAction action = (BoardAction)ctx.getBean("proxy");
// Java Source로 처리
System.out.println("전처리 구간");
action.execute();
System.out.println("후처리 구간");
ctx.close();
}
}
17:14:13.480 [main] DEBUG o.s.b.f.xml.XmlBeanDefinitionReader - Loaded 6 bean definitions from class path resource [com/exam/aop01/context.xml]
17:14:13.483 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Refreshing org.springframework.context.support.GenericXmlApplicationContext@1d7acb34
17:14:13.522 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice1'
17:14:13.532 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'pointcutAdvisor1'
17:14:13.581 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'basicAdvice2'
17:14:13.581 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'pointcutAdvisor2'
17:14:13.582 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'proxy'
WriteAction() 실행
17:14:13.631 [main] DEBUG o.s.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
17:14:13.631 [main] DEBUG o.s.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
전처리 구간
전처리 : 공통 관심
execute() 시작
writer : 홍길동
execute() 종료
후처리 : 공통 관심
후처리 구간
17:14:13.646 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@1d7acb34, started on Mon Jul 04 17:14:13 KST 2022
전처리 및 후처리 추가하기.
xml 파일 수정해주기만 하면 되는 간단한 문제다.
package com.exam.aop01.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class BasicAdvice2 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// TODO Auto-generated method stub
System.out.println("전처리2 : 공통 관심");
Object rtnObj = invocation.proceed();
System.out.println("후처리2 : 공통 관심");
return null;
}
}
<?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-4.3.xsd">
<bean id="basicAdvice1" class="com.exam.aop01.advice.BasicAdvice1" />
<bean id="pointcutAdvisor1" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="basicAdvice1" />
<property name="pointcut">
<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*execute.*"></property>
</bean>
</property>
</bean>
<bean id="basicAdvice2" class="com.exam.aop01.advice.BasicAdvice2" />
<bean id="pointcutAdvisor2" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="basicAdvice2" />
<property name="pointcut">
<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*execute.*"></property>
</bean>
</property>
</bean>
<bean id="writeAction" class="com.exam.aop01.WriteAction" scope="prototype">
<property name="writer" value="홍길동" />
</bean>
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="writeAction"></property>
<property name="interceptorNames">
<list>
<value>pointcutAdvisor1</value>
<value>pointcutAdvisor2</value>
</list>
</property>
</bean>
</beans>
… 앞부분 생략 …
17:15:38.385 [main] DEBUG o.s.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
전처리 구간
전처리 : 공통 관심
전처리2 : 공통 관심
execute() 시작
writer : 홍길동
execute() 종료
후처리2 : 공통 관심
후처리 : 공통 관심
후처리 구간
17:15:38.399 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@1d7acb34, started on Mon Jul 04 17:15:38 KST 2022
아까의 xml 파일로 돌아가서, <list>
태그 내부 value 값의 순서를 변경해준다.
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="writeAction"></property>
<property name="interceptorNames">
<list>
<value>pointcutAdvisor2</value>
<value>pointcutAdvisor1</value>
</list>
</property>
</bean>
실행해보면?
pointcutAdvisor2 처리가 먼저 실행되도록 순서가 바뀐 것을 확인할 수 있다.
17:32:54.275 [main] DEBUG o.s.aop.framework.ProxyFactoryBean - Advice has changed; re-caching singleton instance
전처리 구간
전처리2 : 공통 관심
전처리 : 공통 관심
execute() 시작
writer : 홍길동
execute() 종료
후처리 : 공통 관심
후처리2 : 공통 관심
후처리 구간
17:32:54.291 [main] DEBUG o.s.c.s.GenericXmlApplicationContext - Closing org.springframework.context.support.GenericXmlApplicationContext@1d7acb34, started on Mon Jul 04 17:32:54 KST 2022