code5753 / spring-core-principles-basic Goto Github PK
View Code? Open in Web Editor NEWSpring 학습을 위한 공간
Spring 학습을 위한 공간
@Configuration
public class AppConfig {}
Configuration을 부착 후 실행 했을 때 콘솔창
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$ddfbf8e6
memberRepository
가 중복 호출되는데 모두 싱글톤으로 관리 됨CGLIB
이라는 코드 생성 라이브러리를 통해 순수한 AppConfig
가 아닌 CGLIB
이 재생성한 AppConfig
가 생성되는 것으로 유추AppConfig@CGLIB
에서 호출되는(new) 객체가 빈 컨테이너에 등록이 되어있는지 확인하고, 있다면 해당 빈을 없다면 새롭게 추가하여 반환하는 것으로 유추됨Configuration을 제거 했을 때 콘솔창
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.memberRepository
call AppConfig.orderService
call AppConfig.memberRepository
bean = class hello.core.AppConfig
memberRepository
가 중복 호출되어 더 이상 싱글톤을 유지하지 않음Configuration을 제거 했을 때 Warning
Bean 네이밍은 크게 두 가지가 있다.
자동 네이밍
@Component // 자동으로 "naming"으로 빈 네임이 설정된다 (lowerCamelCase)
public class Naming { }
수동 네이밍
@Component("naming")
public class NoName { }
스프링 부트 환경인 곳과 아닌 곳에서 위 네이밍이 충돌하는 경우에 어떻게 쓰이는지 살펴보자.
정상실행되지만 Overriding 되는 것을 볼 수 있다
01:04:19.317 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Overriding bean definition for bean 'memoryMemberRepository' with a different definition: replacing [Generic bean: class [hello.core.member.MemoryMemberRepository]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null;
... 생략
런타임시에 에러가 발생하고 아예 종료된다
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
위 메세지를 보면 default option으로 오버라이딩이 false인 것을 알 수 있고, 오버라이딩 기능을 원하는 경우 옵션을 변경할 수 있다.
수동 네이밍과 자동 네이밍이 충돌하는 경우는 절대 의도한 경우는 아닐 것이고 개발자의 실수인 경우가 대부분일 것이라 예상된다.
즉, 오버라이딩된 경우도 의도한 경우가 아닌데 실행에는 문제가 없다는 것은 큰 문제를 야기할 수 있다.
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
한 클래스는 하나의 책임만 가져야 한다
클라이언트 객체는 직접 구현 객체를 생성, 연결, 실행하는 다양한 책임을 갖고 있음
구현체가 아닌 역할에 의존
OrderServiceImpl
이 interface인 DiscountPolicy를 의존하는 것 처럼 보이지만,
실제론 FixDiscountPolicy
에도 의존하고 있음
수정에 닫혀있고(코드 수정 X) 확장하는 것
FixDiscountPolicy
를 RateDiscountPolicy
로 변경하는 순간 OrderServiceImpl
의 코드도 함께 변경해야 한다는 문제 발생
구현(class)과 역할(interface)을 분리한 것 처럼 보이지만 기존 코드를 수정했기에 완벽한 분리가 아님
OrderServiceImpl
에 DiscountPolicy
의 구현 객체를 대신 생성하고 주입해야 한다.
// ThreadA: A 사용자가 1만원 주문
statefulService1.order("userA", 10000);
// ThreadB: B 사용자가 2만원 주문
statefulService1.order("userB", 20000);
// ThreadA: A 사용자가 주문 금액 조회
int price = statefulService1.getPrice();
System.out.println("price = " + price);
public class StatefulService {
private int price; // 상태를 유지하는 필드
public void order(String name, int price) {
System.out.println("name = " + name + " price = " + price);
this.price = price; // 여기가 문제!
}
public int getPrice() {
return price;
}
}
StatefulService
에서 price라는 필드값이 바뀌어 발생하는 문제이므로 각 클라이언트별로 상태(stateful)를 유지하지 않는 형태로 변경 필요
request
scope는 요청이 들어온 직후 ~ 끝나는 시점까지만 생존하는 주기를 갖고 있다.
하지만 스프링 구동 시 logger(request scope)를 찾게 되는데 bean이 비어있으므로 에러를 발생시킨다.
logger 찾는 것을 지연시킬 수 있다면 어떨까?
ObjectProvider
를 이용해 가능하다.
쉽게 얘기하자면,
ObjectProvider를 사용하지 않고 스프링을 구동시키면 빈을 생성하고 주입하는 과정에서 불가피하게 request scope bean을 주입하게된다. 하지만 request scope이 아니기 때문에 주입에 실패하여 에러가 발생한다.
따라서, ObjectProvider로 선언하여 실제 Request가 들어 왔을 때 Request scope bean을 생성하도록 하는 것이다.
orderServiceImpl
에서 DiscountPolicy
를 주입 받는다.
기존 코드에서는 RateDiscountPolicy
만 Component
로 등록되어 문제가 없었으나
만약 FixDiscountPolicy
도 Component
로 등록하게 되는 경우
DiscountPolicy
를 조회하는 경우 2개의 빈이 조회되는 문제가 발생한다.
Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'hello.core.discount.DiscountPolicy' available: expected single matching bean but found 2: fixDiscountPolicy,rateDiscountPolicy
조회 대상 빈이 2개 이상일 때 대처법
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy fixDiscountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = fixDiscountPolicy;
}
필드명을 fixDiscountPolicy
로 지정하는 경우 문제 없이 돌아가는 것을 알 수 있다.
Qualify
는 추가 구분자를 붙여주는 방법이다.
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("fixDiscountPolicy") DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
Qualifier
끼리 매칭NoSuchBeanDefinitionException
발생Qualifier
는 Qualifier
가 붙은 애들끼리 찾을 때 사용하는게 유용하다.단점
@Qualifer
를 쓰는 쪽도 등록하는 쪽도 모두 붙여주어야 함 @Qualifier("mainn")
@Qualifier
를 포함한 커스텀 어노테이션으로 해결이 가능함Primary
는 우선순위를 정하는 방법이다.
우선 순위가 높은 빈에 @Primary
를 붙여주면 된다.
늘 그렇지만 수동에 가까운 쪽이 더 높은 우선권을 갖게 된다.
이 경우 @Qualifier
가 @Primary
보다 높은 우선권을 갖는다.
Singleton Bean, Prototype Bean을 함께 사용하면 아래와 같은 문제가 발생한다
@Autowired
Applicationcontext ac;
public int logic() {
PrototypeBean prototypeBean = ac.getBean(PrototypeBean.class);
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
Applicationcontext
으로 빈을 가져오는 방식이다. 당연히 prototype 빈이 가져와지고 매번 생성/주입 할 수 있다. private final ObjectProvider<PrototypeBean> prototypeBeanProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
DL 기능만 해주는 ObjectProvider
를 사용하는 방법이다.
ObjectFactory
의 하위 인터페이스인데 ObjectFactory
는 getObject
만 수행 가능하고, 여기서 추가적인 편의 기능이 생긴 것이 ObjectProvider
이다.
하지만 스프링에 종속적이라는 단점이 있다.
자바 표준 Provider 사용
private final Provider<PrototypeBean> prototypeBeanProvider;
public int logic() {
PrototypeBean prototypeBean = prototypeBeanProvider.get();
prototypeBean.addCount();
int count = prototypeBean.getCount();
return count;
}
implementation 'javax.inject:javax.inject:1'
ObjectProvider
와 사용법은 동일하지만 라이브러리를 추가해야하고 기능이 get
밖에 없다는 특징이 있다.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.