Open skarltjr opened 3 years ago
public class Soldier {
private Gun gun;
public Soldier() {
gun = new Gun();
}
}
둘 중 사용하고자 하는 구체 클래스를 @Component로 등록함으로써 언제든지 갈아끼울 수 있다. // 혹은 @Primary .. @Qualifier ...
ex)스프링이 빈으로 관리하는 클래스를( 여기선 rateDiscountPolicy를 빈으로 등록했다고 가정했을 때 ) 필요할 때 알아서 주입시켜준다.
@Service
public class OrderService {
private DiscountPolicy discountPolicy;
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
}
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(
memberRepository(),
discountPolicy());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
}
싱글톤 패턴이든, 스프링 같은 싱글톤 컨테이너를 사용하든, 객체 인스턴스를 하나만 생성해서 공유하는 싱 글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지 (stateful)하게 설계하면 안된다.
무상태(stateless)로 설계해야 한다! 특정 클라이언트에 의존적인 필드가 있으면 안된다. 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다! 가급적 읽기만 가능해야 한다. 필드 대신에 자바에서 공유되지 않는, 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다. 스프링 빈의 필드에 공유 값을 설정하면 정말 큰 장애가 발생할 수 있다!!!
만약 stateful로인해 공유되는 자원이 있다면 A사람의 요청으로 만들어진 객체의 price필드가 +10000이 된 상태로 B의 요청에 의한 객체가 생성될 떄 price가 +된 상태로 보내진다. 문제!
@Component
public class FixDiscountPolicy implements DiscountPolicy {}
@Component
public class RateDiscountPolicy implements DiscountPolicy {
처럼 2개의 구현체가있을 때
@Autowired
private DiscountPolicy discountPolicy
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {}
스프링 빈의 이벤트 라이프사이클 :
스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존관계 주입 ->초기화 콜백 -> 사용-> 소멸전 콜백 -> 스프링 종료
스프링은 크게 3가지 방법으로 빈 생명주기 콜백을 지원한다. -인터페이스(InitializingBean, DisposableBean) -설정 정보에 초기화 메서드, 종료 메서드 지정 -@PostConstruct, @PreDestory 애노테이션 지원 . 스프링이 권장하는 방법이며 스프링이 아닌 자바의 표준스펙
@Scope("prototype")
@Component
public class HelloBean {}
트랜잭션이 다르면 다른 영속성 컨텍스트를 사용한다. 스프링 컨테이너는 스레드마다 각각 다른 트랜잭션을 할당한다. 따라서 만약 같은 엔티티 매니저를 호출해도 접근하는 영속성 컨텍스트가 다르므로 멀티스레드 환경에서 안전하다.
스프링 컨테이너의 가장 큰 장점은 트랜잭션과 복잡한 멀티 스레드 상황을 컨테이너가 처리해주기 때문에 개발자는 싱글 스레드 애플리케이션 처럼 단순하게 개발할 수 있다.
즉 하나의 쓰레드는 같은 트랜잭션, -> 트랜잭션이 같으면 같은 영속성컨텍스트를 사용 -> @Transactional이 포함된 매서드가 호출될 때 트랜잭션 시작 -> 영속성컨텍스트가 열린다
readOnly는 현재 해당 그 트랜잭션 내에서 데이터를 읽기만 할건지 설정
의도치 않은 변경을 막을 수 있다.
public ~ method1(){
AAA()
~~ 여긴 123
BBB()
}
public ~ method2(){
AAA()
~~ 여긴 456
BBB()
}
public ~ method3(){
AAA()
~~ 여긴 789
BBB()
}
내 생각은 독립적인 기능을 수행하는 코드를 뽑아내서 필요한곳에 매 번 작성할 필요없이 쉽게 적용시킬 수 있도록 만든것이라고 생각
@LogExecutionTime
을 @Transaction
이라고 생각해보자 그러기위해서 우선 어노테이션을 만들고
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}
해당 어노테이션이 붙은 곳에서 실행될 독립적인 로직을 작성
@Component
@Aspect
public class LogAspect {
Logger logger = LoggerFactory.getLogger(LogAspect.class);
// - @Around를 통해 어디서 수행될지 지정
@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object proceed = joinPoint.proceed();
stopWatch.stop();
logger.info(stopWatch.prettyPrint());
return proceed;
}
}
@Controller
class OwnerController {
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
private final OwnerRepository owners;
... 여기선 다 필요없고
★여기 @LogExecutionTime!!!을 보자 - 위에서 해당 어노테이션이 붙은곳에선 어떤 동작을 수행하도록 설정
즉 해당 어노테이션이 붙은. 어떤 독립적인 기능을 수행할 코드를 꽂아 넣어 줄 곳을 지정하고 지정된 곳에서 그 기능을 수행 -> 필요한곳마다 독립적인 기능을 수행하는 코드를 작성할 필요 없이
@GetMapping("/owners/new")
@LogExecutionTime
public String initCreationForm(Map<String, Object> model) {
Owner owner = new Owner();
model.put("owner", owner);
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
}
@PostMapping("/owners/new")
@LogExecutionTime
public String processCreationForm(@Valid Owner owner, BindingResult result) {
if (result.hasErrors()) {
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
}
else {
this.owners.save(owner);
return "redirect:/owners/" + owner.getId();
}
}
@GetMapping("/owners/find")
@LogExecutionTime
public String initFindForm(Map<String, Object> model) {
model.put("owner", new Owner());
return "owners/findOwners";
}
....
Spring
스프링부트 : 스프링을 더욱 편리하게 사용하도록
spring ?
- 가장 중요한 스프링의 탄생배경과 핵심 : 왜 spring?
- 객체 지향의 특징
- 객체 지향 프로그래밍
- 핵심은 유연하게, 변경이 용이하게 -> 언제든지 쉽게 갈아끼울 수 있는
- 다형성 - 역할과 구현을 구분하자 - 인터페이스 / 오버라이딩
- 객체는 협력관계
- 스프링과 객체지향. 제어의역전 IOC - 의존관계 주입 DI 를 통해 역할과 구현을 구분
- 좋은 객체지향 설계의 5원칙 / SOLID
SRP: 단일 책임 원칙(single responsibility principle) -한 클래스는 하나의 책임 -중요한것은 변경 - 변경 시 파급효과가 적으면 단일 책임원칙을 잘 따른것
OCP: 개방-폐쇄 원칙 (Open/closed principle) -확장에는 열려있고 / 변경에는 닫혀있어야 한다. -무슨말인가? 인터페이스와 오버라이딩을 생각해보자 - 변경이 아니라 확장(갈아끼우기)
LSP: 리스코프 치환 원칙 (Liskov substitution principle)
ISP: 인터페이스 분리 원칙 (Interface segregation principle)
DIP: 의존관계 역전 원칙 (Dependency inversion principle) ★ -★프로그래머는 추상화에 의존해야지 구체화에 의존해서는 안된다. -구현클래스가아닌 인터페이스(역할)에 의존해라! - 유연함
- 스프링을 통해서