반응형

3.2.1 BeanFactory

빈 팩토리는 여러 개의 빈 객체를 생성하고, 이를 구성하고, 관리하는 컨테이너이다. 

 

빈들끼리는 서로 협력하고 서로 간의 의존성을 가진다. 이 의존성들은 빈 팩토리의 구성 데이터에 반영이 된다. (어떠한 의존성들은 보이지 구성 데이터에 보이지 않지만, 런타임 때 함수로서 작동이 된다.)

 

빈 팩토리는 org.springframework.beans.factory.BeanFactory 라는 인터페이스에 나타나며 이곳에는 여러가지 구현이 되어 있다. ApplicationContext도 빈 팩토리의 하위 클래스이나, 가장 많이 사용되는 구현은 org.springframework.beans.factory.xml.XmlBeanFactory이다. 이처럼 빈 팩토리에 의해 관리되는 구현을 사용할 때에도 빈 팩토리에 대해 알 필요는 없다. 빈 팩토리가 어떤 방식으로든 객체화 되어 있기 때문이다.

 

많은 경우, 빈 팩토리와 Application Context 객체를 생성할 필요가 없다. 스프링 프레임워크에서 해주기 때문이다. 처음에 dependencies 설정할 때 wep-app 부분에 포함되어 있는 듯 하다.

 

 

 

 

빈 팩토리의 구성은 빈 팩토리가 관리해야 하는 빈의 정의로 이루어져 있다. XmlBeanFactory의 경우, 상위 빈 요소 안의 빈들로 구성되어 있다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
  
  <bean id="..." class="...">
    ...
  </bean>
  <bean id="..." class="...">
    ...
  </bean>

  ...

</beans>

XmlBeanFactory

 

 

 

 

 

3.2.2 빈 정의

빈 정의는 다음의 세부사항을 포함한다.

 

1)  클래스 이름

2) 빈이 컨테이너에서 어떻게 동작해야 하는지를 나타내는 빈 동작 구성 요소

3) 새로 생성된 빈에 설정할 생성자 인수 및 속성 값

4) 빈이 작업을 수행하는 데 필요한 다른 빈 즉, 협력자

 

이 개념들은 빈 정의가 구성하는 요소 집합으로 직접 변환된다.

 

1) 클래스

2) 아이디, 이름

3) 싱글톤, 또는 프로토타입

4) 생성자 인수

5) 빈 속성

6) AutoWiring 모드

7) 의존성 검사 모드

8) 초기화 방법

9) 파기 방법

 

빈 정의는 실제 인터페이스 org.springframework.beans.factory.config.BeanDefinition 에 구현이 되어 있다는 점을 참고하자.

빈 팩토리 외에서 커스텀하게 만들어진 빈을 등록하는 것도 허용한다. DefaultListableBeanFactory는 registerSingleton method를 통해 이 등록과정을 지원한다.

 

 

 

 

 

 

3.2.3 빈 클래스

클래스 속성은 일반적으로 필수적이다. 그리고 두 가지 중 하나의 목적으로 사용된다. 빈 팩토리가 생성자를 호출하여 스스로 빈을 만들어내는 경우(자바에서 new 연산자와 동일하다), 클래스 속성은 생성할 빈을 지정한다. 빈 팩토리가 static factory 방법을 사용하는 드문 상황에서는 클래스 속성이 실제 static factory를 포함하는 클래스를 지정한다.

 

3.2.3.1 생성자를 통한 빈 생성

생성자를 통해 빈을 생성한 경우, 스프링이 모든 일반적인 클래스를 사용할 수 있고 호환이 가능하다. 즉, 이렇게 만들어진 클래스는 인터페이스를 직접 구현하거나 특정 방식으로 코딩할 필요가 없다. 다만, 어떤 IoC 컨테이너를 사용하느냐에 따라 빈 생성자를 추가해줘야 할 때도 있다.

 

게다가, 빈 팩토리는 자바 빈 말고도 다른 어떤 클래스도 관리할 수 있다. 대부분의 사람들이 자바 빈을 빈 팩토리에 등록하여 사용하는 것을 선호하지만, 빈이 아닌 클래스도 스프링이 관리하여 사용할 수 있다.

 

 

 

3.2.3.2 static factory를 이용한 빈 생성

static factory나 static factory 방법을 포함하는 클래스를 지정하는 클래스 속성을 사용할 때에, 또 다른 속성인 factory-method에는 factory 자기자신의 이름을 명시에 주어야 한다. 스프링이 이 이름을 부르고, 다시 생성자를 통해 만든 객체 상태로 되돌리기를 기대하기 때문이다. 빈 정의를 사용하는 이유 중 하나가 정적 팩토리를 부르기 위해서이다. 밑에 코드에서 createInstance는 static 이어야 한다.

 

<bean id="exampleBean"
      class="examples.ExampleBean2"
      factory-method="createInstance"/>

 

 

 

 

 

3.2.3.3 instance factory를 이용한 빈 생성

이것은 위의 static factory를 이용한 빈 생성과 유사하지만, instance는 non-static이라는 점에서 차이가 있다. 여기에서 기존 빈의 팩토리 메소드를 호출하여 새로운 빈을 생성한다. 이 메커니즘을 사용하려면 클래스 속성을 비워두어야 하며, factory-bean 속성은 팩토리 메소드를 포함하는 조상빈의 이름으로 해야 한다.

 

<!-- The factory bean, which contains a method called
     createInstance -->
<bean id="myFactoryBean"
      class="...">
  ...
</bean>
<!-- The bean to be created via the factory bean -->
<bean id="exampleBean"
      factory-bean="myFactoryBean"
      factory-method="createInstance"/>

 

 

 

 

 

 

3.2.4 빈 식별자

모든 빈은 한 개나 두 개의 id를 가지고 있다. 대부분 한 개를 가지고 있지만, 두 개라면 이는 본질적으로 별칭으로 고려된다. id 속성을 사용하면 하나의 id를 입력할 수 있다. 만약 id가 XML에서 유효하지 않거나, 두 개의 id를 작성하고 싶다면 콜론이나 세미콜론으로 구분하거나 이름 속성에 작성하면 된다.

 

 

 

3.2.5 싱글톤으로, 혹은 비 싱글통

빈은 싱글톤이거나 비 싱글통 중 하나의 모드로 결정이 된다. 빈이 싱글톤인 경우, 하나의 공유 인스턴스만이 빈에 의해 관리된다

반응형
반응형

로깅? 로그 찍기

 

 

1. 로킹 퍼사드 vs 로깅

 

로깅 퍼사드

: Commons Logging, SLF4j

: 로거 api를 추상화해 놓은 인터페이스

: 로거를 바꿔 끼울 수 있게 해줌

 

로깅

: JUL, Log4j2, Logback

 

스프링 프레임워크는 Commons Logging, Logback을 사용함

 

 

 

2. Spring-JCL

스프링 5부터 추가된 기능

Commons Logging -> SLF4j 로 보내도록 함

- pom.xml 에 exclusion 안해도됨

 

 

 

3. 더 자세한 로그를 찍고 싶다면?

-> program argument 에 --debug 로 설정(일부 핵심라이브러리만 디버깅 모드로)

-> program argument 에 --trace 로 설정(전부 다 디버깅 모드로)

 

 

 

4. 디버깅 컬러로 찍기

spring.output.ansi.enabled=always

appliction.properties

 

 

5. 파일 출력

logging.path=logs

appliction.properties

 

 

6. 로그 레벨 조정

logging.level.com.example.springapplication=DEBUG

appliction.properties

 

-> 자신의 파일 경로를 설정해주기

 

@Component
public class SampleRunner implements ApplicationRunner {

    private Logger logger = LoggerFactory.getLogger(SampleRunner.class);

    @Autowired
    private String hello;

    @Autowired
    private NaeunProperties naeunProperties;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        logger.info("=====================");
        logger.info(hello);
        logger.info(naeunProperties.getName());
        logger.info(naeunProperties.getFullName());
        logger.info("=====================");
    }
}

SampleRunner.java

 

결과 로그

 

 

7. 커스텀 로그 설정 파일 이용하기

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
    </root>
    <logger name="com.example.springapplication" level="DEBUG"/>
</configuration>

logback-spring.xml

 

-> logger name 에 경로 설정해주기

 

 

 

8. 로거를 log4j2 로 변경하기

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
	<version>2.3.4.RELEASE</version>
	<exclusions>
		<exclusion>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-logging</artifactId>
		</exclusion>
	</exclusions>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

pom.xml

반응형
반응형

1. Profile의 활용 방법

@Profile("prod")
@Configuration
public class BaseConfiguration {

    @Bean
    public String hello(){
        return "hello";
    }
}

-> prod 라는 profile 일 때만 빈을 사용가능함

 

 

 

@Component
public class SampleRunner implements ApplicationRunner {

    @Autowired
    private String hello;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("====================");
        System.out.println(hello);
        System.out.println("====================");
    }

}

SampleRunner.java

 

-> profile 설정이 없기 때문에 오류가 난다

 

 

 

2. 어떤 profile을 활성화시킬 것인가?

spring.profiles.active = prod

-> application.properties에 이렇게 설정해주면 실행 가능

 

 

 

java -jar target/spring-application-0.0.1-SNAPSHOT.jar --spring.profiles.active=test

[ terminal ]

 

-> 커맨드라인 아규먼트가 application.properties 보다 우선순위가 높기 때문에 test 가 실행된다.

 

 

 

 

3. 어떤 profile을 추가할 것인가? 프로파일용 프로퍼티

spring.profiles.include=proddb

application.properties

 

 

- application-proddb.properties 라는 파일 만들어서 

naeun.fullName=dbdbdb 으로 설정해주면 profile이 추가가 됨

 

 

 

 

4. 매번 패키징하기 귀찮을 때 

 

 

-> Program arguments 에 --spring.profiles.active=prod 라고 설정해주고 IDE에서 실행시킴

반응형
반응형

1. 타입 세이프 프로퍼티 @ConfigurationProperties

- 여러 프로퍼티들을 묶어서 읽어올 수 있음

- @AutoWired 로 간단하게 가져올 수 있음

- @Value 보다는 @ConfigurationProperties 로 쓰는게 나음

 

package com.example.springapplication;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties("naeun")
public class NaeunProperties {
    String name;
    int age;
    String fullName;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }
}

NaeunProperties.java

 

-> 새로운 파일 생성

-> @Component로 빈으로 등록해주기

-> @ConfigurationProperties 로 이름붙여주기?

 

 

@Component
public class SampleRunner implements ApplicationRunner {

    @Autowired
    NaeunProperties naeunProperties;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("====================");
        System.out.println(naeunProperties.getName());
        System.out.println(naeunProperties.getAge());
        System.out.println("====================");
    }

}

SampleRunner.java

<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-configuration-processor</artifactId>
		<optional>true</optional>
</dependency>

의존성 추가

 

-> 메타정보를 생성해주는 의존성 추가, 자동완성 사용가능

 

 

 

 

2. 프로퍼티 타입 컨버젼

 

application.properties 에 적은 문자열이

int형으로 선언하면 int 형으로 자동으로 타입이 바뀜

 

application.properties 에 적은 문자열이

Duration형으로 선언하면 Duration 형으로 자동으로 타입이 바뀜

 

 

 

3. 검증

@Component
@ConfigurationProperties("naeun")
@Validated
public class NaeunProperties {

    @NotEmpty
    String name;
    int age;
    String fullName;

    @DurationUnit(ChronoUnit.SECONDS)
    private Duration sessionTimeout = Duration.ofSeconds(30);

    public Duration getSessionTimeout() {
        return sessionTimeout;
    }

    public void setSessionTimeout(Duration sessionTimeout) {
        this.sessionTimeout = sessionTimeout;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }
}

-> @Validated, @NotEmpty 사용해서 값이 없으면 오류가 나게 함

 

 

 

반응형
반응형

 

 

[ 프로퍼티 우선순위 ]

 

  1. 유저 홈 디렉토리에 있는 spring-boot-dev-tools.properties
  2. 테스트에 있는 @TestPropertySource
  3. @SpringBootTest 애노테이션의 properties 애트리뷰트
  4. 커맨드 라인 아규먼트
  5. SPRING_APPLICATION_JSON (환경 변수 또는 시스템 프로티) 에 들어있는 프로퍼티
  6. ServletConfig 파라미터
  7. ServletContext 파라미터
  8. java:comp/env JNDI 애트리뷰트
  9. System.getProperties() 자바 시스템 프로퍼티
  10. OS 환경 변수
  11. RandomValuePropertySource
  12. JAR 밖에 있는 특정 프로파일용 application properties
  13. JAR 안에 있는 특정 프로파일용 application properties
  14. JAR 밖에 있는 application properties
  15. JAR 안에 있는 application properties
  16. @PropertySource
  17. 기본 프로퍼티 (SpringApplication.setDefaultProperties)

-> 높은게 낮은걸 오버라이딩 함

 

 

 

 

[ application.properties 파일 우선 순위 (높은게 낮은걸 덮어 씁니다.) ]

 

  1. file:./config/
  2. file:./
  3. classpath:/config/
  4. classpath:/

 

 

 

[ 15번 우선순위 - JAR 안에 있는 application properties ]

naeun.name = naeun

application.properties

 

@Component
public class SampleRunner implements ApplicationRunner {

    @Value("${naeun.name}")
    private String name;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("====================");
        System.out.println(name);
        System.out.println("====================");
    }

}

SampleRunner.java

 

 

 

 

 

 

 

 

[ 4번 우선순위 - 커맨드라인 아규먼트 ]

mvn clean package 

mvn package 

java -jar target/spring-application-0.0.1-SNAPSHOT.jar --naeun.name=keesun

터미널

 

 

-> keesun으로 변경되어 찍힘

 

 

 

 

 

 

 

 [ test 파일 안의 application.properties ]

@RunWith(SpringRunner.class)
@SpringBootTest
class ApplicationTests {

	@Autowired
	Environment environment;
	// 스프링에 있는 Environment import 하

	@Test
	void contextLoads() {
		assertThat(environment.getProperty("naeun.name"))
				.isEqualTo("whiteship");
	}

}

ApplicationTests

 

naeun.name = whiteship

test 파일 안의 application.properties

 

-> whiteship 이라고 찍힘

 

 

 

 

 

[ 2, 3번 우선순위 - 테스트에 있는 @TestPropertySource, @SpringBootTest 애노테이션의 properties 애트리뷰트 ]

@TestPropertySource(properties = "naeun.name=keesun3")
@SpringBootTest(properties = "naeun.name=keesun2")

 

 

 

* 너무 관리하기 힘들면 test.properties 라는 다른 이름의 프로퍼티스 생성해서 관리하면됨

 

 

 

[ random value 사용하기 ]

naeun.age = ${random.int}

 

 

 

[ 궁금한 점 ]

AssertThat 이 뭔지?

test 는 왜 있는지?

classpath 가 src?

 

반응형
반응형

빈과 일반 객체의 차이는 !

ApplicationContext 에서 가져오는지 아닌지

즉, ApplicationContext가 만들어서 그 안에 담고 있는 객체를 빈(Bean) 이라고 할 수 있음

 

 

1. 빈으로 등록하는 방법

1) ComponentScan

-> 모든 @Component를 찾아서 빈으로 등록함

: @Controller @Repository @Service @Configuration

 

-> @Component라는 메타 어노테이션을 사용한 어노테이션이므로 @Component라고 볼 수 있으므로 똑같이 빈으로 등록됨

( Repository는 어노테이션이 아니라 인터페이스 상속 받는 방식)

 

 

 

 

- 빈으로 등록되었는지 샘플로 테스트

package org.springframework.samples.petclinic.sample;

import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

@RunWith(SpringRunner.class)
@SpringBootTest
class SampleControllerTest {

	@Autowired
	ApplicationContext applicationContext;

	@Test
	public void testDI(){
		SampleController bean = applicationContext.getBean(SampleController.class);
		assertThat(bean).isNotNull();
	}
}

 

 

 

2) 직접 빈으로 등록

 

@Configuration
public class SampleConfig {

	@Bean
	public SampleController sampleController(){
		return new SampleController();
	}
}

-> SampleController 클래스가 빈으로 등록이 된다.

 

 

 

 

 

2. 적용해보기

1) ApplicationContext에서 직접 꺼내오기

private final OwnerRepository owners;
	private final ApplicationContext applicationContext;

	private VisitRepository visits;

	public OwnerController(OwnerRepository clinicService, VisitRepository visits, ApplicationContext applicationContext) {		// IoC
		this.owners = clinicService;
		this.visits = visits;
		this.applicationContext = applicationContext;
	}

	@GetMapping("/bean")
	@ResponseBody
	public String bean() {
		return "bean : " + applicationContext.getBean(OwnerController.class) + "\n"  // 애플리케이션컨텍스트에서 직접 꺼낸 오너리파지토리
			+  "owners : "  + this.visits;											// 애플리케이션컨텍스트가 알아서 주입해준 오너리파지토리?
																					// ? 왜 다르지, 왜 밑에꺼는 jpa를 반환하지
	}

 

2) Autowired 로 가져오기

@Autowired
private OwnerRepository owners;
반응형

+ Recent posts