@SpringBootApplication
메인 실행 파일
package com.rubypaper;
)를 베이스 패키지로 지정하고 있다package com.rubypaper;
.
.
.
@SpringBootApplication
public class Chapter01Application {
public static void main(String[] args) {
//SpringApplication.run(Chapter01Application.class, args);
SpringApplication application = new SpringApplication(Chapter01Application.class); // 자기자신을 인자로 넘겨준다
application.setWebApplicationType(WebApplicationType.SERVLET);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
@SpringBootApplication
은 아래 어노테이션들이 포함되어 실행되는 것이다. @SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(basePackages= {”해당 패키지 경로”}) // 컴포넌트 스캔 대상이 된다
해당 패키지 경로
의 모든 클래스 중 @Controller
가 있는 클래스가 있다면?@Controller
BoardController.java
@Controller // 컴포넌트 스캔이 내부적으로 동작해서 메모리에 뜨게 한다.
public class BoardController {
public BoardController() {
System.out.println("===> BoardController 생성");
}
@RequestMapping(value = "/hello.do", method = RequestMethod.GET)
@ResponseBody // 리턴 값을 JSON 으로 변환하여 응답 프로토콜 Body에 출력한다.
public String hello(String name) {
System.out.println("---> hello() 실행");
return "Hello : " + name;
}
}
@Controller
가 있는 클래스이다@ResponseBody
return "Hello : " + name;
을 JSON 으로 변환해서 브라우저로 출력해준다@RestController
크롬에 jsonviewer 설치
메모리에 띄워지는 클래스
BoardController.java
package com.rubypaper.controller;
.
.
.
@RestController
public class BoardController {
public BoardController() {
System.out.println("===> BoardController 생성");
}
@RequestMapping("/hello.do")
public String hello(String name) {
return "Hello: " + name;
}
@RequestMapping("/getBoard.do")
public BoardVO getBoard() {
BoardVO board = new BoardVO();
board.setSeq(1);
board.setTitle("테스트 제목");
board.setWriter("테스터");
board.setContent("테스트 내용");
board.setCreateDate(new Date());
board.setCnt(0);
return board;
}
}
@RestController
를 사용하면?
바로 위에서 사용했던 @ResponseBody
를 사용하지 않아도 됨
application-properties
파일에 dependency
를 복사해서 간편하게 설정해주면 된다.spring-boot-starter
사용관련된 라이브러리들의 묶음
spring-boot-starter-모듈명
spring-boot-starter-data-jpa
로 관련된 많은 라이브러리를 추가할 수 있음
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
JPA 활용은 다른 시리즈에서 배운 것을 정리해보겠다
<dependency>
<groupId>com.rubypaper</groupId>
<artifactId>board-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<parent>
코드를 ctrl
키를 누르고 타고 들어가면,<spring-framework.version>
을 알 수 있다<spring-framework.version>
로 내가 원하는 버전으로 변경 가능<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
.
.
.
<properties>
<java.version>17</java.version>
<spring-framework.version>5.3.0</spring-framework.version> // 변경
</properties>
SpringBoot 의 메인 클래스에서는
@SpringBootApplication
때문에
자동으로 웹 애플리케이션이 동작한다.
스프링 부트는 객체 생성을 두 번 한다 = 스프링 컨테이너 생성이 두 번 된다.
총 2개
이 때 2개가 생성 될 때, @CoponentScan
과 @EnableAutoConfiguration
이 필요하다
@CoponentScan
@EnableAutoConfiguration
❗ 중요한 것은 ❗
내가 만든 @CoponentScan
이 먼저 동작하고,
나중에 스프링이 제공하는 @EnableAutoConfiguration
가 실행된다는 것이다.
코드 및 설명
//@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class Chapter02Application {
public static void main(String[] args) {
SpringApplication.run(Chapter02Application.class, args);
}
}
Chapter02Application
클래스가 동작하는 순간, @ComponentScan
스캔이 동작한다@EnableAutoConfiguration
이 동작해서, 스프링이 제공하는 클래스 빈의 객체가 메모리에 뜬다@SpringBootConfiguration
으로 Bean Factory
에게 알려준다.@Bean
메서드가 있다면 메모리에 올려준다@Configuration
과 @SpringBootConfiguration
는 거의 같은 것@Annotation
@EnableAutoConfiguration
@Bean
들)이 메모리에 뜬다@Annotation
들을 아래에서 살펴보자@ConditionalOnWebApplication
메인 클래스의 웹애플리케이션 타입이 none
인지 servlet
인지
servlet
: 디폴트 값, servlet 웹으로 실행none
: 일반 자바 어플리케이션으로 실행 즉, 웹인지 자바 프로젝트로 실행하는 지 정하는 것
@ConditionalOnWebApplication(type = Type.SERVLET) public class 어떤자동설정클래스{. . .}
@EnableAutoConfiguration
가 적용된 어떤 클래스 에서 SERVLET
으로 실행할 때만,
위 ‘어떤자동설정클래스’가 동작한다.
❗ 자동 설정 클래스라고 해서 무조건 자동으로 다 뜨는 것은 아니다 ❗
NONE
으로 실행되면 자동으로 뜨지 않는다@ConditionalOnClass
↔ @ConditionalOnMissingBean
특정 클래스가 클래스 패스에 있을 때만, 자동 설정 클래스를 동작시킨다
@ConditionalOnClass(DispatcherServlet.class) public class 어떤자동설정클래스{. . .}
DispatcherServlet
클래스가 있을 때만,DispatcherServlet
클래스가 클래스 패스 상에 존재하면?@ConditionalOnMissingBean
어떤 클래스의 객체가 메모리에 없다면, 자동 설정 클래스를 동작시킨다
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) public class 어떤자동설정클래스{. . .}
WebMvcConfigurationSupport.class
클래스의 객체가 메모리에 없다면,WebMvcConfigurationSupport.class
클래스와 ‘어떤자동설정클래스’
가 비슷한 객체들을 가지고 있다면?WebMvcConfigurationSupport.class
클래스가 메모리에 안떴을 때,‘어떤자동설정클래스’
가 메모리에 뜨도록 해서@AutoConfigureOrder
@AutoConfigureAfter
@EnableAutoConfiguration
에 의해서 엄청나게 많은 자동 설정 클래스들이 로딩 되는 데,
로딩이 됐다고 해서 전부 적용되는 것은 아니다!
위에서 설명한 것처럼 특정 조건을 만족해야만 자동설정클래스가 동작한다!
INFO
DEBUG
로 설정하여, 디테일한 로그를 볼 수 있다spring-boot-autoconfigure
을 추가한다 <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.7.1</version>
</dependency>
<dependency>
를 추가하면? 아래처럼 jar 파일이 생성된 것을 확인할 수 있다.@EnableAutoConfiguration
이 들어있다.scr/main/resources
폴더를 새로 만든 후, spring-factories
파일을 만들어 아래 코드를 넣어준다 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.rubypaper.jdbc.config.BoardAutoConfiguration
@EnableAutoConfiguration
은 해당 jar
안에 META-INF
의 spring.factories
파일을 무조건 로딩하도록 되어있다.(아래 두 번째 사진)jar
파일 안에 autoConfigure
패키지 안에 EnableAutoConfiguration.class
(@EnableAutoConfiguration
) 가src/main/resources
의 META-INF
의 spring.factories
파일도 로딩한다.(아래 첫 번째 사진)👉 즉, 내가 많은 자동 설정 클래스도 이제 로딩한다.(첫 번째 사진의 spring.factories
)
<dependency>
로 <dependency>
<groupId>com.rubypaper</groupId>
<artifactId>board-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
board-spring-boot-starter 의 jar 파일에 동록된
자동 설정 클래스(BoardAutoConfiguration.class
)가 로딩이 되고,
해당 클래스에 등록된 @Bean
이 메모리에 뜨게된다
따라서 해당 jar 파일(board-spring-boot-starter) 을 다운 받으면?
→ 환경 설정 클래스(BoardAutoConfiguration.class
)가 다운이 되고
→ @EnableAutoConfiguration
이 해당 환경 설정 클래스를 로딩하면
→ com.rubypaper.jdbc.util 의 JDBCConnectionManger.class
객체가 자동으로 메모리에 뜬다
package com.rubypaper.service;
.
.
.
@Service
public class JDBCConnectionManagerService implements ApplicationRunner{
@Autowired
private JDBCConnectionManager manager;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("생성된 커넥션 매니저 : " + manager.toString());
}
}
implements
하고, run()
메서드를 오버라이딩하면?run()
메서드가 무조건 실행@Autowired private JDBCConnectionManager manager;
위 '2) 자동 설정 @Annotation' 에서 설명했던 것 처럼,
메모리에 동일한 객체가 떴을 때는 중복되지 않도록 해야한다.
중복 객체가 발생했을 때, 덮어쓰는 실습을 진행해보자
chapter02 에 환경 설정 클래스 파일(NewBoardConfiguration.class
)을 만들었다
package com.rubypaper.config;
.
.
.
@Configuration
public class NewBoardConfiguration {
@Bean
public JDBCConnectionManager jdbcConnectionManager() {
JDBCConnectionManager manager = new JDBCConnectionManager();
manager.setDriverClass("org.h2.Driver");
manager.setUrl("jdbc:h2:tcp://localhost/~/test");
manager.setUsername("sa");
manager.setPassword("");
return manager;
}
}
@Configuration
은 @Componet
를 포함하고 있으므로,@Bean
설정으로 JDBCConnectionManager 객체가 메모리에 뜬다앞전 실습에서 board-spring-boot-starter 를 등록하여
com.rubypaper.jdbc.util 의 JDBCConnectionManager.class 객체가 메모리에 이미 떠있는 상태였다.
즉, board-spring-boot-starter 의 자동 설정 클래스( @EnableAutoConfiguration
)에 의해서
메모리에 떠있던 JDBCConnectionManager 객체(Oracle 연결)가
바로 위의 새로운 JDBCConnectionManager 객체(h2 연결)로 인해 덮어씌워진다.
👉 동일한 객체는 오버라이딩된다
appication.properties
파일에 아래처럼 설정해준다.# 중복 객체 덮어쓰기 설정
spring.main.allow-bean-definition-overriding=true
실행되는 메인 코드
package com.rubypaper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//@SpringBootApplication
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Chapter02Application {
public static void main(String[] args) {
SpringApplication.run(Chapter02Application.class, args);
}
}
@ComponentScan
은 사용자가 정의한 객체(컨트롤러)를 초기화@EnableAutoConfiguration
은 프레임워크가 제공하는 객체(MultipartResolver
)를 초기화@ComponentScan
이 @EnableAutoConfiguration
보다 먼저 동작한다이러한 이유 때문에
내가 만든 클래스(위 1. 사용자가 정의한 객체
)인 JDBCConnectionManager 객체(h2)가 @ComponentScan
으로 인해 메모리에 먼저 뜬 것이다.
그 후 @EnableAutoConfiguration
로 앞 전에 등록한 자동 설정 클래스의 JDBCConnectionManager 객체(oracle)가 생성된다.
👉 따라서 oracle 이 h2 를 덮어씌워버려서 oracle 이 출력되었다.
@ConditionalOnMissingBean
을 사용하여, JDBCConnectionManager 객체(h2)가 메모리에 없을 때만 oracle 이 출력되도록 설정한다.
package com.rubypaper.jdbc.config;
.
.
.
// 자동 설정 클래스 만들기
@Configuration
public class BoardAutoConfiguration {
public BoardAutoConfiguration() {
System.out.println("===> BoardAutoConfiguration 생성");
}
@Bean
@ConditionalOnMissingBean // JDBCConnectionManager 타입의 객체가 메모리에 없다면
public JDBCConnectionManager jdbcConnectionManager() {
JDBCConnectionManager manager = new JDBCConnectionManager();
manager.setDriverClass("oracle.jdbc.driver.OracleDriver");
manager.setUrl("jdbc:oracle:thin:@localhost:1521:xe");
manager.setUsername("scott");
manager.setPassword("tiger");
return manager;
}
}
application.properties
로 중복 객체 변경NewBoardConfiguration.class
를 지우고,board-spring-boot-starter 에 JDBCConnectionManagerProperties.java
파일을 생성하여, @ConfigurationProperties
을 설정해준다
접두사에 있는 properties
정보(해당 객체를 사용하는 쪽, 가장 아래글 참조)를 읽어서 맴버변수에 세팅해준다
package com.rubypaper.jdbc.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Data;
@Data
@ConfigurationProperties(prefix = "board.jdbc")
public class JDBCConnectionManagerProperties {
private String driverClassName;
private String url;
private String username;
private String passwrod;
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.6.6</version>
<optional>true</optional>
</dependency>
앞전 만들었던 BoardAutoConfiguration.java
클래스(원래 oracle 이 출력되던 파일)를 열어서 @EnableConfigurationProperties(JDBCConnectionManagerProperties.class)
를 설정하기위해 아래 코드처럼 바꿔준다
package com.rubypaper.jdbc.config;
.
.
.
// 자동 설정 클래스 만들기
@Configuration
@EnableConfigurationProperties(JDBCConnectionManagerProperties.class)
public class BoardAutoConfiguration {
@Autowired
private JDBCConnectionManagerProperties properties; // 의존성 주입
public BoardAutoConfiguration() {
System.out.println("===> BoardAutoConfiguration 생성");
}
@Bean
@ConditionalOnMissingBean // JDBCConnectionManager 타입의 객체가 메모리에 없다면
public JDBCConnectionManager jdbcConnectionManager() {
JDBCConnectionManager manager = new JDBCConnectionManager();
manager.setDriverClass(properties.getDriverClassName());
manager.setUrl(properties.getUrl());
manager.setUsername(properties.getUsername());
manager.setPassword(properties.getPasswrod());
return manager;
}
}
이제 board-spring-boot-starter 를 사용하는 chapter02 프로젝트에서 application.properties
에 아래 처럼 설정해주면?
# 데이터소스 : Oracle
#board.jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
#board.jdbc.url=jdbc:oracle:thin:@localhost:1521:xe
#board.jdbc.username=hr
#board.jdbc.password=hr
# 데이터소스 : H2
board.jdbc.driverClassName=org.h2.Driver
board.jdbc.url=jdbc:h2:tcp://localhost/~/test
board.jdbc.username=sa
board.jdbc.password=
properties
정보들을, board-spring-boot-starter 의 JDBCConnectionManagerProperties 객체가 읽어들인 후set
메서드로 값을 전부 세팅해준다.BoardAutoConfiguration.java
클래스가 JDBCConnectionManagerProperties 객체를 @Autowired
로 의존성 주입을 한 후get
메서드로 1번에서 세팅된 값을 이용한다.@Configuration
VS @Configurable
🧐@Configuration
과 @Configurable
은 어떤 차이가 있나?@Configuration
은 @Componet-scan
안에 포함이 된다@Componet-scan
안에 @Conponet
, @Repository
, @Service
, @Controller
, @RestController
, @Configuration
이 포함된다.@Configuration
는 객체 호출 시 같은 메모리 주소에서 값을 가져오고@Configurable
는 객체 호출할 때마다 다른 메모리 주소에서 값을 가져온다.@Configuration
과 @Configurable
는 다르다!