<?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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
<bean id="bookService" class="me.freelife.BookService"> <!-- bookService 빈 등록 -->
<!-- bookRepository 프로퍼티에 bookRepository 빈을 레퍼런스로 주입 -->
<!-- bookRepository name은 setter에서 가지고 온 것 -->
<!-- ref는 레퍼런스로 다른 빈을 참조한다는 뜻 -->
<!-- ref에는 setter 에 들어갈 수 있는 다른 bean의 id가 와야됨 -->
<property name="bookRepository" ref="bookRepository"/>
</bean>
<bean id="bookRepository" class="me.freelife.BookRepository"/> <!-- bookRepository 빈 등록 -->
</beans>
public class BookRepository {
}
public class BookService {
BookRepository bookRepository;
public void setBookRepository(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
}
public class Application {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
String[] beanDefinitionNames = context.getBeanDefinitionNames();
System.out.println(Arrays.toString(beanDefinitionNames));
BookService bookService = (BookService) context.getBean("bookService");
System.out.println(bookService.bookRepository != null);
}
}
단점을 보완하기위해 패키지를 스캔해서 @Component @Service @Repository 처럼
@Component를 확장한 애노테이션들을 스캐닝해서 빈으로 자동으로 등록해줌
이렇게 등록된 빈은 @Autowired 나 @Inject를 통해 의존성을 주입하여 사용
애너테이션 기반에 빈을 등록하고 설정하는 기능은 스프링 2.5부터 가능한기능<!-- @Compnent @Service @Repository 애노테이션을 스캐닝 해서 빈으로 등록 해줌 --> <context:component-scan base-package="me.freelife"/>
@Repository
public class BookRepository {
}
@Service
public class BookService {
@Autowired
BookRepository bookRepository;
public void setBookRepository(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
}
@Configuration
public class ApplicationConfig {
@Bean
public BookRepository bookRepository() {
return new BookRepository();
}
@Bean
public BookService bookService() {
BookService bookService = new BookService();
bookService.setBookRepository(bookRepository());
return bookService;
}
}
public class BookRepository {
}
public class BookService {
BookRepository bookRepository;
public void setBookRepository(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
}
public class Application {
public static void main(String[] args) {
//ApplicationConfig 를 빈 설정으로 사용하겠다는 설정
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
// ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
String[] beanDefinitionNames = context.getBeanDefinitionNames();
System.out.println(Arrays.toString(beanDefinitionNames));
BookService bookService = (BookService) context.getBean("bookService");
System.out.println(bookService.bookRepository != null);
}
}
@Configuration
@ComponentScan(basePackageClasses = Application.class)
public class ApplicationConfig {
}
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@Autowired(required = false)
@Autowired
BookRepository myBookRepository;
BeanFactory 가 자신에게 등록된 BeanPostProcessor 들을 찾아서 일반적인 Bean들에게 로직을 적용함
AutowiredAnnotationBeanPostProcessor 가 기본적으로 Bean으로 등록되어있음
BeanPostProcessor 의 라이프사이클 구현체에 의해 동작함
@Autowired 는 postProcessBeforeInitialization 단계
ApplicationRunner 의 경우 애플리케이션 구동이 다 끝나고 동작함
Runner을 사용하지 않으면 애플리케이션 구동 중에 처리됨
BeanPostProcessor
- 아래의 세가지 라이프사이클 단계가 있다
- postProcessBeforeInitialization
- InitializingBean's afterPropertiesSet
- postProcessAfterInitialization
Bean을 Initializer(만들다)하여 인스턴스를 만든 다음에
Bean의 초기화 라이프사이클(Initialization LifeCycle) 이전 OR 이후에 부가적인 작업을 할 수 있는 또다른 라이프사이클 콜백
Initialization
@PostConstruct
등의 어노태이션으로 Bean이 만들어진 이후에 해야할일을 정의 해주는것
InitializingBean을 implement 해서 afterPropertiesSet 메서드를 구현
SpringApplication.run(Demospring51Application.class, args);
var app = new SpringApplication(Demospring51Application.class);
app.run(args);
var app = new SpringApplication(Demospring51Application.class);
app.addInitializers(new ApplicationContextInitializer<GenericApplicationContext>() {
@Override
public void initialize(GenericApplicationContext ctx) {
ctx.registerBean(MyService.class);
ctx.registerBean(ApplicationRunner.class, new Supplier<ApplicationRunner>() {
@Override
public ApplicationRunner get() {
return new ApplicationRunner() {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("Funational Bean Definition!!");
}
};
}
});
}
});
app.run(args);
var app = new SpringApplication(Demospring51Application.class);
app.addInitializers((ApplicationContextInitializer<GenericApplicationContext>) ctx -> {
ctx.registerBean(MyService.class);
ctx.registerBean(ApplicationRunner.class, () -> args1 -> System.out.println("Funational Bean Definition!!"));
});
app.run(args);
@Component @Scope(value="prototype" , proxyMode = ScopedProxyMode.TARGET_CLASS)
public class Proto {
}
@Component
public class Single {
@Autowired
private ObjectProvider<Proto> proto;
public Proto getProto() {
return proto.getIfAvailable();
}
}
프로파일과 프로퍼티를 다루는 인터페이스
ApplicationContext extends EnvironmentCapable
getEnvironment()
! (not)
& (and)
|(or)
특정 환경(test)에서의 예시
- Ultimate
- intelliJ - Edit Configuration - Environment - Active profiles 에 test 입력
- Community
- intelliJ - Edit Configuration - Environment - VM option 에 `-Dspring.profiles.active="test"` 입력
Configuration 으로 프로파일 설정하는 방법
@Configuration
@Profile("test")
public class TestConfiguration {
@Bean
public BookRepository bookRepository() {
return new TestBookRepository();
}
}
@Repository
@Profile("test")
public class TestBookRepository implements BookRepository {
}
@Repository
@Profile("!prod & test")
public class TestBookRepository implements BookRepository {
}
-Dapp.name=spring5
app.about=spring
@PorpertySource("classpath:/app.properties")
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext ctx;
@Override
public void run(ApplicationArguments args) throws Exception {
Environment environment = ctx.getEnvironment();
System.out.println(environment.getProperty("app.name"));
System.out.println(environment.getProperty("app.about"));
}
}
@Value("${app.name}")
String appName;
국제화(i18n) 기능을 제공하는 인터페이스
ApplicationContext 에서 상속 받은 인터페이스
MessageSource 직접설정 예시
ReloadableResourdeBundleMessageSource 로 메세지 변경 시 변경된 메세지를 반영
@Bean
public MessageSource messageSource() {
var messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:/messages");
messageSource.setDefaultEncoding("UTF-8");
messageSource.setCacheSeconds(3); //캐싱하는 시간을 최대 3초까지만 캐싱하고 다시 읽음
return messageSource;
}
ResourceBundleMessageSource
가 빈으로 등록되어있음 greeting=hello, {0}
greeting=안녕, {0}
ApplicationContext extends ApplicationEventPublisher
publishEvent(ApplicationEvent event)
ContextRefreshedEvent: ApplicationContext를 초기화 했더나 리프래시 했을 때 발생
ContextStartedEvent: ApplicationContext를 start()하여 라이프사이클 빈들이 시작
ContextStoppedEvent: ApplicationContext를 stop()하여 라이프사이클 빈들이 정지
ContextClosedEvent: ApplicationContext를 close()하여 싱글톤 빈 소멸되는 시점에 발생
RequestHandledEvent: HTTP 요청을 처리했을 때 발생
스프링 4.2 부터는 ApplicationEvent 클래스를 상속받지 않아도 이벤트로 사용할 수 있다
public class MyEvent extends ApplicationEvent {
private int data;
/**
* Create a new ContextStartedEvent.
*
* @param source the {@code ApplicationContext} that the event is raised for
* (must not be {@code null})
*/
public MyEvent(Object source) {
super(source);
}
public MyEvent(Object source, int data) {
super(source);
this.data = data;
}
public int getData() {
return data;
}
}
public class MyEvent {
private int data;
private Object source;
public MyEvent(Object source, int data) {
this.source = source;
this.data = data;
}
public Object getSource() {
return source;
}
public int getData() {
return data;
}
}
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationEventPublisher publisherEvent;
@Override
public void run(ApplicationArguments args) throws Exception {
publisherEvent.publishEvent(new MyEvent(this, 100));
}
}
@Component
public class MyEventHandler implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println("이벤트 받았다. 데이터는 " + event.getData());
}
}
@Component
public class MyEventHandler {
@EventListener
public void handle(MyEvent event) {
System.out.println("이벤트 받았다. 데이터는 " + event.getData());
}
}
@Order(Ordered.HIGHEST_PRECEDENCE)
라고 설정하면 가장 먼저 실행됨 @Order(Ordered.HIGHEST_PRECEDENCE + 2)
라고 설정하면 조금 더 늦게 실행됨 @Component
public class MyEventHandler {
@EventListener
@Order(Ordered.HIGHEST_PRECEDENCE)
public void handle(MyEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("이벤트 받았다. 데이터는 " + event.getData());
}
}
@SpringBootApplication
@EnableAsync
public class Ioccontainer8Application {
public static void main(String[] args) {
SpringApplication.run(Ioccontainer8Application.class, args);
}
}
@Component
public class MyEventHandler {
@EventListener
// @Order(Ordered.HIGHEST_PRECEDENCE)
@Async
public void handle(MyEvent event) {
System.out.println(Thread.currentThread().toString());
System.out.println("이벤트 받았다. 데이터는 " + event.getData());
}
}
ApplicationContext extends ResourceLoader
Resource getResource(java.lang.String location)
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ResourceLoader resourceLoader;
@Override
public void run(ApplicationArguments args) throws Exception {
Resource resource = resourceLoader.getResource("classpath:test.txt"); // 파일 읽어오기
System.out.println(resource.exists()); // 파일 유무
System.out.println(resource.getDescription()); // 설명
System.out.println(Files.readString(Path.of(resource.getURI()))); // URL로 읽어오기
}
}