spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/demo_jpa_scheme?serverTimezone=UTC&characterEncoding=UTF-8
username : ~~
passsword : ~~
jpa:
hibernate:
ddl-auto: create
show-sql: false
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
database: sql-server
--- 임, ___ 아님,
spring:
profiles:
active: local
- //어떤 프로그램 사용해서 active 될 지
---
spring:
config: //설정
activate: //실행
on-profile: local //현재 프로파일이 로컬일 때 실행
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/demo_jpa_scheme?serverTimezone=UTC&characterEncoding=UTF-8
username : ~~
passsword : ~~
jpa:
hibernate:
ddl-auto: create
show-sql: false
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
database: sql-server
//원래 설정 아래에 또 추가
---
spring:
config:
activate:
on-profile: test
datasource:
driver-class-name: ord.h2.Driver //내장 메모리, 따로 설치 필요 없음
url: jdbc:h2:mem:testdb
username : sa
passsword : password
jpa:
hibernate:
ddl-auto: create
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect
이 테스트를 실행되게 하기 위해서는 아까 맨 위에 active: local
로 설정해놨었는데 active: test
로 진행해주어야 한다.
근데 지금 이 부분 test로 고치고 실행해도 우리가 h2드라이버 사용하겠다고 선언해놓고 gradle 상에 정의 안해놨기 때문에 에러가 나는 것 -> 따라서 buld.gradle에 추가해줘야 함
build.gradle
dependencies 부분에 추가해주깅
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
runtimeOnly 'mysql:mysql-connector-java'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
spring:
profiles:
active: test
---
spring:
config:
activate:
on-profile: local
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/demo_jpa_scheme?serverTimezone=UTC&characterEncoding=UTF-8
username : demo_jpa
passsword : Qpqpqp0614!
jpa:
hibernate:
ddl-auto: create
show-sql: false
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
database: sql-server
---
spring:
config:
activate:
on-profile: test
datasource:
driver-class-name: ord.h2.Driver //내장 메모리, 따로 설치 필요 없음
url: jdbc:h2:mem:testdb
username : sa
passsword : password
jpa:
hibernate:
ddl-auto: create
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect
이렇게 test mode로 구동된다고 써있음
spring:
config:
activate:
on-profile: docker
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver //내장 메모리, 따로 설치 필요 없음
//도커로 컴퓨터 실행 경우 컨테이너 내부의 ip를 찾게 됨
//아래와 같이 아이피 해주면 도커가 자기 컨테이너 탐색해
//우리 아이피 주소 파악해냄
//127.0.0.1 로 하면 컨테이너 내부에 있는 애플리케이션 찾게 돼서 문제 생기게 딤
url: jdbc:mysql://172.17.0.1:3306/demo_jpa_scheme
username : sa
passsword : password
jpa:
hibernate:
ddl-auto: create
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect
아래와 같은 폴더 구조로 정리
local을 따로 빼주면 아래와 같아짐
1) application.yml
spring:
profiles:
active: local
---
spring:
config:
activate:
on-profile: test
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:testdb
username : sa
passsword : password
jpa:
hibernate:
ddl-auto: create
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect
---
spring:
config:
activate:
on-profile: docker
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://172.17.0.1:3306/demo_jpa_scheme
username : sa
passsword : password
jpa:
hibernate:
ddl-auto: create
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect
2) application-local.yml
spring:
config:
activate:
on-profile: local
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/demo_jpa_scheme?serverTimezone=UTC&characterEncoding=UTF-8
username : demo_jpa
passsword : Qpqpqp0614!
jpa:
hibernate:
ddl-auto: create
show-sql: false
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
database: sql-server
application-빼주는애
이름으로 설정해줘야 함on-profile: 현재모드
되어있을 때application-빼준 이름
으로 그 애를 찾아서 실행시키기 가능이기 때문외부 파일 탐색 순서
1) active 파일 보고
2) application.yml 쑥 보고
3) 2)에 내가 찾는 active 파일이 없다면
4) application-내가찾는이름 애를 외부에서 찾아서 걔를 사용하게 되는 것
대쉬 프로파일
명으로 지정해주는 것이 중요PS C:\Users\DONGYUN\Desktop\spring\jpa> java -jar .\build\libs\jpa-0.0.1-SNAPSHOT.jar
java -jar -Dspring.profiles.active=local -jar build/libs/jpa-0.0.1-SNAPSHOT.jar
java 실행 명령어 입력 시
application.properties의 내용 중 profiles > active의 값을 변경할 경우
옵션을 다음과 같이 준다.
-Dspring.profiles.active = local
java -jar "-Dspring.profiles.active=local" [name].jar
java -jar "-Dspring.profiles.active=local" -jar build/libs/jpa-0.0.1-SNAPSHOT.jar
ProfileComponent 클래스 만들기
@Component
@Profile("local")
public class ProfileComponent {
private static final Logger logger = LoggerFactory.getLogger(ProfileComponent.class);
public ProfileComponent(){
logger.info("profile component profile: local");
}
}
java -jar .\build\libs\jpa-0.0.1-SNAPSHOT.jar
-> 이렇게 하면 우리가 방금 위에서 만들어두었던 로거가 찍히지 않아
(2)
SPRING_PROFILES_ACTIVE=local java -jar .\build\libs\jpa-0.0.1-SNAPSHOT.jar
java -jar "-Dspring.profiles.active=local" -jar build/libs/jpa-0.0.1-SNAPSHOT.jar
(2) 코드 터미널 창에 치게 되면 이렇게 로거가 찍히게 된다, 즉 profile로 빈이 걸린 이 ProfileComponent도 함께 작동이 되는 것이당
MessageQueueInterface
인터페이스 만들기
package jsbdy.jpa;
public interface MessageQueueInterface {
String readMessage();
}
RabbitMQ
@Component
@Profile("!prod")
public class RabbitMQ implements MessageQueueInterface {
@Override
public String readMessage(){
//code for communicating with RabbitMq
return "message from rabbitmq";
}
}
KafkaQ
package jsbdy.jpa;
@Component
@Profile("prod")
public class KafkaQ implements MessageQueueInterface {
@Override
public String readMessage(){
return "message from kafkamq";
}
}
Rabbit, Kafka 차이
- 래빗은 개발 단계
- 카프카는 상용 단계
어떤 Profile이 현재 Bean일 때만 그것 실행가능하다는 점 기억해두기
- 위의 래빗, 카프카 보면 Profile 다름, 즉 각 Profile이 어떤 상황으로 진행되고 있는지 따라서 실행되는 아이들도 달라진다.
서로 다른 소프트웨어를 각기 다른 상황에서 실행시키고 싶을 때 래빗과 카프카 분리한 것처럼 하면 된다
테스트를 위해서 로거 붙이고, 카프카가 local/래빗은 local 아닐 때로 설정하고 돌리기
KafkaQ
@Profile("!local")
RabbitMq
@Profile("local")
=> profile 상태를 local로 해두고 돌리면 local 상태에 해당하는 래빗이 찍힘
=> 이번에는 profile 상태를 지정해두지 않고 돌려보기 -> 우리가 기본으로 설정해둔 test 상태가 실행 -> 그럼 local이 아니므로 래빗이 아니라 카프카만 로그가 찍히게 되겠지
우리가 지금까지 클래스에서의 bean만 사용해왔었음
디펜던시에는 어떨까?
자주 jsb에서 사용하는 구글 gson이라는 아이를 dependency에 추가
build.gradle
-> 그 다음 한번 빌드
dependencies {
implementation 'com.google.code.gson:gson:2.8.2'
...
@Component
public class GsonComponent {
private final Gson gson;
public GsonComponent(){
this.gson=new Gson();
}
public Gson getGson(){
return this.gson;
}
}
(+) gson 은 어떤 라이브러린가?
PostController
public class PostController {
private static final Logger logger = LoggerFactory.getLogger(PostController.class);
private final PostService postservice;
private final Gson gson;
public PostController(
@Autowired PostService postservice,
@Autowired GsonComponent gson
){
this.postservice = postservice;
this.gson = gson.getGson()
}
즉 외부의 라이브러리를 사용하기 위해서 위의 과정 거쳐서 그 라이브러리를 ioc 컨테이너의 관리 하에 두어야 하는데 so complicated ;;
따라서 configuration
과 bean
이라는 annotation으로 진행해보자
DemoConfig
라는 클래스 생성
@Configuration
설명출처
이 어노테이션을 단 클래스는 빈 설정을 담당하는 클래스가 된다.
이 클래스 안에서 @Bean 어노테이션이 동봉된 메소드를 선언하면, 그 메소드를 통해 스프링 빈을 정의하고 생명주기를 설정하게 된다. 자바 기반의 config가 싫다면 XML 쓰면 된다
(+)설명
Spring Bean 등록 방법(@Bean, @Configuration, @Component)
- 어떤 임의의 클래스를 만들어서 @Bean 어노테이션을 붙인다고 되는 것이 아니고, @Bean을 사용하는 클래스에는 반드시 @Configuration 어노테이션을 활용하여 해당 클래스에서 Bean을 등록하고자 함을 명시해주어야 한다.
- 위의 예제에서도 클래스 이름 위에 @Configuration 어노테이션을 명시하여 해당 클래스에서 1개 이상의 Bean을 생성하고 있음을 명시하고 있다. 그렇기 때문에 @Bean 어노테이션을 사용하는 클래스의 경우 반드시 @Configuration과 함께 사용해주어야 한다.
이러한 @Bean 어노테이션의 경우 아래와 같은 상황에서 주로 사용한다.1) 개발자가 직접 제어가 불가능한 라이브러리를 활용할 때
2) 초기에 설정을 하기 위해 활용할 때
- 위의 예제에서는 Spring에서 제공하여 개발자가 직접 제어할 수 없는 라이브러리를 위해 @Bean을 사용하고 있고,
- 설정을 위해 개발자가 개발한 AuthenticateInterceptor, HeaderFilter를 위해 @Bean을 사용하고 있다.
- @Configuration없이 @Bean만 사용해도 스프링 빈으로 등록이 된다. 대신 메소드 호출을 통해 객체를 생성할 때 싱글톤을 보장하지 못한다. 그렇기 때문에 Spring 설정 정보를 위해서는 반드시 @Configuration을 사용해주어야 한다.
싱글톤 :
설명 출처
- Singleton(이하 싱글톤) 패턴은 자바에서 많이 사용한다.
- 싱글톤이란 어떤 클래스가 최초 한번만 메모리를 할당하고(Static) 그 메모리에 객체를 만들어 사용하는 디자인 패턴= 을 의미한다.
- => 생성자의 호출이 반복적으로 이뤄져도 실제로 생성되는 객체는 최초 생성된 객체를 반환
■ 싱글톤 패턴을 사용하는 이유
=> 한번의 객체 생성으로 재 사용이 가능하기 때문에 메모리 낭비를 방지
=> 또한 싱글톤으로 생성된 객체는 무조건 한번 생성으로 전역성을 띄기에 다른 객체와 공유가 용이
■ 싱글톤의 문제점
=> 싱글톤도 위에서 언급된 것 처럼 전역성을 띄면서 다른 객체와 공통으로 사용하는 경우와 같은 몇 가지 케이스에서만 사용할 때 효율적
=> but 일단 싱글톤으로 만든 객체의 역할이 간단한 것이 아닌 역할이 복잡한 경우 -> 해당 싱글톤 객체를 사용하는 다른 객체간의 결함도가 높아져서 객체 지향 설계 원칙에 어긋나게 된다. (개방-폐쇄)
=> 또한 해당 싱글톤 객체를 수정할 경우 싱글톤 객체를 사용하는 곳에서 사이드 이팩트 발생 확률이 생기게 되며, 멀티 쓰래드환경에서 동기화 처리 문제등이 생기게 된다.- 그리고 여기서 빈의 설정을 담당하는
@Configuration 어노테이션도 내부적으로 @Component를 가지고 있어 @Configuration이 붙은 클래스도 Spring의 빈으로 등록이 되는 것 역시 알아 둘 필요가 있다
[ @Component 어노테이션 ]- 개발자가 직접 개발한 클래스를 Bean으로 등록하고자 하는 경우에는 @Component 어노테이션을 활용하면 된다. 예를 들어 폴더 생성, 파일 저장 등을 처리하기 위해 직접 개발한 FileUtils를 Bean으로 등록하고자 한다면 아래와 같이 @Component를 사용
- 직접 개발한 클래스 단위의 빈을 등록할 때에는 @Component를 통해 해당 클래스를 빈으로 등록함을 노출하는 것이 좋을 것, 왜냐하면 해당 클래스에 있는 @Component 만 보면 해당 빈이 등록되도록 잘 설정되었는지를 찾지 않아고 확인할 수 있기 때문이다. 반면에 @Bean을 이용하면 해당 메소드를 찾는 번거로움이 생길 수 있다.
- 추가로 @Component를 이용한다면 Main 또는 App 클래스에서 @ComponentScan으로 컴포넌트를 찾는 탐색 범위를 지정해주어야 한다. 하지만 SpringBoot를 이용중이라면 @SpringBootConfiguration 하위에 기본적으로 포함되어 있어 별도의 설정이 필요 없다.
DemoConfiguration
@Configuration
public class DemoConfig {
@Bean
//이렇게 Bean 붙이면 아래 함수의 반환 값이 ioc 컨테이너 관리 하에 들감
//원래는 우리가 Controller에 가서 autowired를 통해서 등록 시켜주고
//일일히 관리를 해주었는데 그러지 않아두 된다는 것
//바아로 Controller 가서 autowired 하면 불러와진다
public Gson gson(){
return new Gson();
}
}
PostController
public PostController(
@Autowired PostService postservice,
@Autowired Gson gson
) {this.postservice = postservice;
}
application.yml
custom:
property:
single: custom-property
comlist: comma, sparated,list,to,value
ncp:
id: api-id
key: api-key
url: https://ncp.com/
# 이는 외부 api 사용할 때 라던지
#custom.property.single
#custom.property.comlist
DemoConfig
@Configuration
public class DemoConfig {
@Value("${custom-property.single}")
//yml에 설정돼있는 값을 찾아서 얘가 들어가게 됨
private String customProperty;
@Value("${custom.property.comlist}")
private List<String> customCommaList;
@Bean
public Gson gson(){
return new Gson();
}
}
@Configuration
public class DemoConfig {
private static final Logger logger = LoggerFactory.getLogger(DemoConfig.class);
@Value("${custom.property.single}")
//yml에 설정돼있는 값을 찾아서 얘가 들어가게 됨
private String customProperty;
@Value("${custom.property.comlist}")
private List<String> customCommaList;
@Value("${custom.property.default:default-property}")
//저 default 모시기는 우리가 yml에서 정의안해둔 것
//그래서 콜론 뒤에 쟤가 호출되면 불러와질 것 우리가 써줌(default-property를 디폴트로)
private String propertyDefault;
@PostConstruct
public void init() {
logger.info("custom property:{}",customProperty);
for(String commaListItem:customCommaList){
logger.info("Comma list item:{}",commaListItem);
}
logger.info("default property:{}", propertyDefault);
}
@Bean
public Gson gson(){
return new Gson();
}
}
결과
export SPRING_PROFILES_ACTIVE=test
java -jar spring-boot.jar
#or
SPRING_PROFILES_ACTIVE=test java -jar spring-boot.jar
custom:
property:
single: custom-property
list: comma,seperated,list,to,value
@Value("${custom.property.single}")
private String customProperty;
@Value("${custom.property.list}")
private List<String> customPropertyList;
Exception in thread "main" java.lang.UnsupportedClassVersionError: jsby/jpa/Jpa Application has been compiled by a more recent version of the Java Runti me (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 5
this version of the Java Runtime only recognizes class file versions up to 54.0
-> 얘가 필요로 하는 버전을 설치해주면 된다
Java SE 10 = 54 (0x36 hex),[3]