환경
윈도우즈 10 / 이클립스 2022-06 (4.24.0) / Java SE 18 / 메이븐 3.8.6 / spring-context 5.3.22
교재
책 : 초보 웹 개발자를 위한 스프링5 프로그래밍 입문 챕터 5~6
영상 : 예제로 배우는 스프링 입문(개정판)
스프링 설정 코드에 @Bean 메소드를 직접 기술하지 않고 어떤 클래스를 Bean객체로 등록하는 방법
컴포넌트 스캔 기능 없이 설정파일에 수동으로 Bean객체를 등록하는 경우 아래와 같이 구성 가능
[코드 - 컴포넌트 스캔 - 1]
// pom.xml
// ** dependency 중 aspectjrt, aspectjweaver는 이후 등록 제외할 컴포넌트를 지정할 때 사용하기 위해 미리 넣어놓았음
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>in.wonj</groupId>
<artifactId>chap05-practice</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.22</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<release>18</release>
</configuration>
</plugin>
</plugins>
</build>
</project>
// AppCtx.java
package chap05practice;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
@Configuration
public class AppCtx {
@Bean
public GoraniDao goraniDao() { return new GoraniDao(); }
@Bean
public Person person() { return new Person(); }
}
// Main.java
package chap05practice;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static ApplicationContext ctx;
public static void main(String[] args) {
ctx = new AnnotationConfigApplicationContext(AppCtx.class);
Person person = ctx.getBean("person", Person.class);
person.buy(0, "young");
person.buy(11, "ilil");
person.buy(53, "osam");
person.sell(53);
person.say();
// [Gorani] #0 : young
// [Gorani] #11 : ilil
}
}
// GoraniDao.java
package chap05practice;
import java.util.HashMap;
public class GoraniDao {
private HashMap<Integer, Gorani> list = new HashMap<Integer, Gorani>(); // Database
public void add(Gorani gorani) {
list.put(gorani.getId(), gorani);
}
public void remove(int id) {
list.remove(id);
}
public void printAll() {
(list.values()).forEach(item -> System.out.println(item));
}
}
// Gorani.java
package chap05practice;
public class Gorani {
private int id;
private String name;
public Gorani(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() { return id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Override
public String toString() {
return "[Gorani] #" + id + " : " + name;
}
}
// Person.java
package chap05practice;
import org.springframework.beans.factory.annotation.Autowired;
public class Person {
@Autowired
GoraniDao goraniDao;
public void buy(int id, String name) {
goraniDao.add(new Gorani(id, name));
}
public void sell(int id) {
goraniDao.remove(id);
}
public void say() {
goraniDao.printAll();
}
}
아래와 같이 두 어노테이션을 붙여 자동 등록
@Component@ComponentScan(basePackages={"chap05practice"}) (컴포넌트를 검색할 패키지 지정)
@ComponentScan어노테이션 인수 중 이름이 복수형(-s,-es) 인 것은 값으로 한 개 또는 배열을 취함
셋 모두 컴파일 가능한 문장
- @ComponentScan(basePackages = "something")
- @ComponentScan(basePackages = {"something"})
- @ComponentScan(basePackages = {"something", "another"})
아래 코드의 경우 Person클래스를 자동 등록
등록할 Bean객체의 이름을 별도로 지정하지 않았음
→ 이 경우 Bean 이름 = 클래스 식별자에서 첫글자를 소문자로 바꾼 것
e.g.
| 클래스 이름 | Bean 이름 |
|---|---|
| Person | person |
| JavaSpringStudy | javaSpringStudy |
| lowercaseStart | lowercaseStart |
[코드 - 컴포넌트 스캔 - 2]
** [코드 - 컴포넌트 스캔 - 1] 에서 일부 변경
// AppCtx.java
package chap05practice;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; // import 추가
@Configuration
@ComponentScan(basePackages = {"chap05practice"}) // 어노테이션 추가
public class AppCtx {
@Bean
public GoraniDao goraniDao() { return new GoraniDao(); }
// Person 등록 부분 삭제
}
// Person.java
package chap05practice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; // import 추가
@Component // 어노테이션 추가
public class Person { // 자동 등록된 Bean객체의 이름은 "person"
@Autowired
GoraniDao goraniDao;
public void buy(int id, String name) {
goraniDao.add(new Gorani(id, name));
}
public void sell(int id) {
goraniDao.remove(id);
}
public void say() {
goraniDao.printAll();
}
}
@Component("새로운 이름")과 같이 Bean객체의 이름 지정 가능
[코드 - 컴포넌트 스캔 - 3]
** [코드 - 컴포넌트 스캔 - 1] 에서 일부 변경
// AppCtx.java
package chap05practice;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
@Configuration
@ComponentScan(basePackages = {"chap05practice"})
public class AppCtx {
@Bean
public GoraniDao goraniDao() { return new GoraniDao(); }
}
// Person.java
package chap05practice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("componentPerson") // Bean객체 이름 지정
public class Person {
@Autowired
GoraniDao goraniDao;
public void buy(int id, String name) {
goraniDao.add(new Gorani(id, name));
}
public void sell(int id) {
goraniDao.remove(id);
}
public void say() {
goraniDao.printAll();
}
}
// Main.java
package chap05practice;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static ApplicationContext ctx;
public static void main(String[] args) {
ctx = new AnnotationConfigApplicationContext(AppCtx.class);
// bean이름 person --> componentPerson으로 변경
Person person = ctx.getBean("componentPerson", Person.class);
person.buy(0, "young");
person.buy(11, "ilil");
person.buy(53, "osam");
person.sell(53);
person.say();
// [Gorani] #0 : young
// [Gorani] #11 : ilil
}
}
@ComponentScan에 excludeFilters = { @Filter } 인수를 넘겨 자동 등록하지 않을 클래스 지정 가능
@Filter는 아래 형태로 사용
@Filter(type=FilterType.REGEX, pattern="정규표현식")@Filter(type=FilterType.ASPECTJ, pattern="aspectJ패턴")@Filter(type=FilterType.ANNOTATION, classes={어노테이션 클래스명.class})Filter(type=FilterType.ASSIGNABLE_TYPE, classes={클래스명.class})[코드 - 컴포넌트 스캔 - 4] 정규식으로 클래스 제외
// AppCtx.java
@ComponentScan(basePackages = {"chap05practice"},
excludeFilters = {
@Filter(type=FilterType.REGEX, pattern="chap05practice\..*son")
// chap05practice 패키지 내 "son"으로 끝나는 클래스 제외 (e.g. Person클래스)
})
public class AppCtx { ... }
[코드 - 컴포넌트 스캔 - 5] aspectJ패턴으로 클래스 제외
// AppCtx.java
@ComponentScan(basePackages = {"chap05practice"},
excludeFilters = {
@Filter(type=FilterType.ASPECTJ, pattern="chap05practice.*son")
// chap05practice 패키지 내 "son"으로 끝나는 클래스 제외 (e.g. Person클래스)
})
public class AppCtx { ... }
[코드 - 컴포넌트 스캔 - 6] 특정 어노테이션 붙은 클래스 제외
// AppCtx.java
@ComponentScan(basePackages = {"chap05practice"},
excludeFilters = {
@Filter(type=FilterType.ANNOTATION, classes={MyAnnotation.class})
})
public class AppCtx { ... }
// Person.java
@MyAnnotation
@Component
public class Person { ... }
[코드 - 컴포넌트 스캔 - 7] 특정 클래스 및 자손 제외
// AppCtx.java
@ComponentScan(basePackages = {"chap05practice"},
excludeFilters = {
@Filter(type=FilterType.ASSIGNABLE_TYPE, classes={Person.class})
})
public class AppCtx { ... }
// Person.java
@Component
public class Person { ... }
스프링 컨테이너와 Bean객체는 아래의 라이프 사이클을 가짐
3번, 4번 시점에 호출되는 메소드를 정의하기 위해 세 가지 방법 사용 가능
각 방법의 코드에서 아래의 Main.java를 공통적으로 사용
package chap05practice;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
public class Main {
public static AbstractApplicationContext ctx;
public static void main(String[] args) {
ctx = new AnnotationConfigApplicationContext(AppCtx.class);
ctx.close();
}
}
InitializingBean, DisposableBean인터페이스의 afterPropertiesSet(), destroy()메소드 구현// AppCtx.java
package chap05practice;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
@Configuration
@ComponentScan(basePackages = {"chap05practice"})
public class AppCtx { }
// Wonjin.java
package chap05practice;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@Component
public class Wonjin implements InitializingBean, DisposableBean{
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("<afterPropertiesSet()> is called");
}
@Override
public void destroy() throws Exception {
System.out.println("<destroy()> is called");
}
}
@Bean어노테이션의 인수 initMethod, destoryMethod의 값으로 메소드 이름 입력// AppCtx.java
package chap05practice;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
@Configuration
@ComponentScan(basePackages = {"chap05practice"})
public class AppCtx {
@Bean(initMethod = "init", destroyMethod = "close")
public Wonjin wonjin() { return new Wonjin(); }
}
// Wonjin.java
package chap05practice;
import org.springframework.stereotype.Component;
@Component
public class Wonjin{
public void init() {
System.out.println("init() is called");
}
public void close() {
System.out.println("close() is called");
}
}
@PostConstruct, @PreDestroy 어노테이션 사용javax.annotation-api 디펜던시 필요 )// AppCtx.java
package chap05practice;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
@Configuration
@ComponentScan(basePackages = {"chap05practice"})
public class AppCtx { }
// Wonjin.java
package chap05practice;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.stereotype.Component;
@Component
public class Wonjin{
@PostConstruct
public void init() {
System.out.println("init() is called");
}
@PreDestroy
public void close() {
System.out.println("close() is called");
}
}
참고