spring profile 사용시 주의점

최근 삽질


application.yml에 값을 설정 해두고

spring:
profiles: local
sleep:
min: 100
max: 500
---

spring:
profiles: dev

sleep:
min: 500
max: 1000


application.properties에 아래와 같이 프로파일을 설정해주었다

spring.profiles.active=dev


어플리케이션 구동했으나 계속 발생하는 오류.  심지어 다른 서버에서는 정상 동작


org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sleepAspect': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'sleep.min' invalue "${sleep.min}"


오류를 유심히 보니 프로파일이 다른것으로 적용이 되고 있었음
INFO(6347)[main] [n.d.TestApplication:656] The following profiles are active: dev1


원인은 서버 환변경수에 다음과 같이 프로파일이 적용되고 있었음

$ env | grep PROFILE

SPRING_PROFILES_ACTIVE=dev1


결론은 프로퍼티보다 서버 환경변수의 값이 먼저 적용하기 때문에 쉘 스크립트에서 환변경수를 초기화

프로그램안에만 적용되기 때문에 다른 프로그램에는 영향 없음

SPRING_PROFILES_ACTIVE=

nohup java -cp application.properties -jar donnert.jar > console.log 2>&1 &



0  Comments,   0  Trackbacks
댓글 쓰기
@Scope 어노테이션 사용하기(request+ProxyMode)

[개발/JAVA] - Threadlocal을 이용하여 사용자별 요청 처리하기


ThreadLocal과 같이 보시면 좋습니다.

스프링에서 빈 Scope 타입은 여러가지 방식이 있으며 아래를 참고해 주세요.

https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html


별도의 옵션이 없다면 빈은 싱글톤으로 관리되며 부모의 속성을 따라갑니다.

즉 controller는 따로 설정을 하지 않았기 때문에 singleton으로 동작하게 되며 그 안에서 주입받은 빈 역시 scope를 request로 줘도 singleton으로 동작하게 됩니다.

빈을 생성해 주고 위에 보이는것처럼 scope를 request로 선언해줍니다. 


그리고 위처럼 컨트롤러를 구현합니다.

위 경우 Cart가 매 요청마다 생성될것 같지만 이미 Controller(부모)가 싱글톤으로 잡혀있고 빈을 주입받은 상태이기 때문에 Cart 역시 싱글톤으로 동작합니다.  결국 Cart와 Mart는 동작 상 다른 점이 없습니다.


이럴땐 아래처럼 ProxyMode를 지정하게 되면 싱글톤 객체를 중간 프록시에서 새로운 오브젝트로 전달을 해주게 됩니다.

이전 글에서 Threadlocal을 이용했지만 스프링의 빈 만으로도 쉽게 개발이 가능합니다.


https://github.com/donnert/spring-boot/tree/master/scope-proxy

0  Comments,   0  Trackbacks
댓글 쓰기
Threadlocal을 이용하여 사용자별 요청 처리하기





개발시 소스 전체에서 사용할 수 있는 전역변수처럼 데이터를 할당하고 싶을떄가 있습니다.

Threadlocal을 이용하여 이를 구현해 봅니다.


ShoppingController.java

@RestController
public class ShoppingController {

	private int melon;

	@RequestMapping("/")
	private String test() {
		this.addMelon();
		
		return String.valueOf(melon);
	}
	
	private void addMelon() {
		melon++;
	}
}

사용자별로 요청 시 카트에 멜론을 담는 기능을 구현하려고 합니다.  이 떄 카트 수량은 여러 메소드에서 사용할 수 있기 때문에 멤버변수로 선언을 합니다.


이렇게 작성 후 실행을 해보면 카운트가 초기화가 되지 않고 계속 증가하는 것을 볼 수 있습니다.  스프링의 경우 빈 관리를 싱글톤으로 하기 때문에 ShoppingController 객체는 요청 시 재사용 됩니다.  멤버변수 역시 초기화가 되지 않습니다.




아래는 Threadlocal을 이용해 사용자별 카트를 구현한 소스입니다.


ThreadRepository.java

public class ThreadRepository {
	private static ThreadLocal<Cart> threadLocal = new ThreadLocal<Cart>() {
		@Override
		protected Cart initialValue() {
			return new Cart();
		};
	};
	
	public static Cart getCart() {
		return threadLocal.get();
	}
	
	public static void remove() {
		threadLocal.remove();
	}
}

카트를 담을 저장소를 만들어 줍니다.  Thradlocal을 이용하여 Thread별로 저장소가 생성이 됩니다.


Cart.java

public class Cart {
	private int apple = 0;
	private int orange = 0;
	private int melon = 0;
	
	public void addApple() {
		this.apple++;
	}

	public void addOrange() {
		this.orange++;
	}

	public void addMelon() {
		this.melon++;
	}

	@Override
	public String toString() {
		return "Cart [apple=" + apple + ", orange=" + orange + ", melon="
				+ melon + "]";
	}
}

카트를 생성해줍니다.


ShoppingController.java

@RestController
public class ShoppingController {

	@RequestMapping("/")
	private String test() {
		ThreadRepository.remove();
		
		this.addMelon();
		
		return ThreadRepository.getCart().toString();
	}
	
	private void addMelon() {
		ThreadRepository.getCart().addMelon();
	}
}

컨트롤러가 위처럼 수정되었습니다.  ThreadRepository에서 getCart를 했을때 해당 요청 쓰래드에 대한 Cart가 나오기 때문에 소스 어떤부분에서든 해당 카트의 데이터를 넣고 빼는 것이 가능합니다.

이때 ThreadLocal을 이용하였기 때문에 요청 시 새로운 스래드가 생성되며 그 스래드에서만 사용할 수 있게 관리됩니다.


* 일반으로 WAS는 쓰래드풀을 쓰기 때문에 요청이나 응답 시 해당 Threadlocal을 초기화 하지 않을 경우 이전에 사용한 정보가 남아 있습니다.  쓰래드 재사용시 반드시 remove를 통해 초기화 시켜주어야 합니다.



참고소스

https://github.com/donnert/spring-boot/tree/master/thread-local




0  Comments,   0  Trackbacks
댓글 쓰기