3. Bean
스프링 IoC 컨테이너가 관리하는 객체
IoC컨테이너를 생성하고 @어노테이션
으로 Bean을 등록할 때 사용하는 다양한 인터페이스들 -> 라이프사이클 콜백
다양한 라이프사이클 콜백중 @Component
어노테이션이 등록된 클래스를 찾아 해당 인스턴스를 Bean으로 등록하는 annotation process 처리기가 설정되어 있습니다.
@ComponentScan
이 Bean객체를 스캔할 루트를 지정해주고 하위 패키지 범위에서 모든 클래스의 Bean 등록 여부를 확인합니다.
@Component
- @Controller
- @Repository
- @Service
- @Configuration
이를 통해 Controller 클래스를 구현시 사용했던 @Controller
어노테이션은 우리가 직접 해당 클래스를 Bean으로 등록하지 않아도 스프링 IoC컨테이너에서 직접 라이프사이클 콜백
에 @ComponentScan
의 기능을 통해 Bean으로 등록하여 사용할 수 있도록 도와주는 것입니다.
SampleController
@Controller
public class SampleController {
}
SampleControllerTest
@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleControllerTest {
@Autowired
ApplicationContext applicationContext;
@Test
public void testDI() {
//SampleController의 @Controller 어노테이션의 Bean 객체 등록이 되어있는지 확인
SampleController bean = applicationContext.getBean(SampleController.class);
assertThat(bean).isNotNull();
}
}
Bean의 직접 등록
@Configuration
public class SampleConfig {
@Bean
public SampleController sampleController() {
return new SampleController();
}
}
위와 같은 코드로 Bean을 직접 등록하면 Controller에 등록한 @Controller
어노테이션을 제거해도 Bean으로 등록된 것을 확인할 수 있습니다.
4. 의존성 주입
- 생성자
- 필드
- Setter
OwnerController
에 PetRepository
주입하기
생성자 사용
@Controller
class OwnerController {
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
private final OwnerRepository owners;
private PetRepository pets;
public OwnerController(OwnerRepository clinicService, PetRepository petRepository) {
this.owners = clinicService;
this.pets = petRepository;
}
}
필드
@Controller
class OwnerController {
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
private final OwnerRepository owners;
@Autowired
private PetRepository pets;
}
Setter
class OwnerController {
private static final String VIEWS_OWNER_CREATE_OR_UPDATE_FORM = "owners/createOrUpdateOwnerForm";
private final OwnerRepository owners;
// private final ApplicationContext applicationContext;
private PetRepository petRepository;
@Autowired
public void setPetRepository(PetRepository petRepository) {
this.petRepository = petRepository;
}
5. AOP
관점 지향 프로그래밍(Aspect Oriented Programming)은 공통 처리 메소드나 기능을 별도의 클래스에 구현하여 관리하는 것을 말합니다.
쉽게 말해 구현하고자 하는 기능의 코드를 해당 메소드에 작성하지 않았지만 기능이 별도로 외부에서 관리하며 처리되는 것으로 방법은 크게 3가지로 나눌 수 있습니다.
컴파일
A.java ---(AOP)---> A.class
- 컴파일 도중 AOP 추가
바이트코드 조작
A.java -> A.class ---(AOP)—> 메모리(AspectJ)
- 클래스 로더가 클래스파일을 읽으면서 메모리에 적재시 AOP 추가
프록시 패턴(스프링 AOP)
- 프록시는 어떤 일을 대신 명령하는 것을 말하며 기존 코드를 건드리지 않고 새로운 기능을 추가
5-1 프록시 패턴
Payment.java
public interface Payment {
void pay(int amount);
}
Cash.java
public class Cash implements Payment {
@Override
public void pay(int amount) {
System.out.println("현금 : " + amount + "원");
}
}
CashPerf.java
public class CashPerf implements Payment{
//프록시 클래스
Payment cash;
@Override
public void pay(int amount) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
cash = new Cash();
cash.pay(amount);
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
}
}
Store.java
public class Store {
private Payment payment;
public Store (Payment payment) {
this.payment=payment;
}
public void buySomething(int amount) {
payment.pay(amount);
}
}
SampleTest.java
public class StoreTest {
@Test
public void testPay() {
//Payment Cash = new Cash();
Payment cashPerf = new CashPerf();
Store store = new Store(cashPerf);
store.buySomething(100);
}
}
위에 코드를 보면 인터페이스로 Payment
클래스를 작성하였고 기존 코드의 영향을 받지 않도록 프록시 패턴을 작성한 예제입니다.
실제로 Client 코드인 Store를 호출하게 되면 이러한 처리는 프록시 역할을 하는 클래스 CashPerf
를 주입받게 됩니다. 직접 접근하지 않고 CashPerf
(프록시) 객체에 Cash
클래스의 인스턴스를 작성함과 동시에 StopWatch
로 성능측정을 구현하였습니다. 이를 통해 Cash
클래스에 직접 접근하는 방법이 아닌 기존 코드는 건드리지 않는 프록시 패턴을 확인할 수 있습니다.
5-2. AOP 적용 예제
- 사용할 @어노테이션 표기(메소드, 필드 등등)
- IDE IntelliJ (option + Enter) -> @어노테이션 생성
- @Target(어느 영역에 적용할지)
- @Retention(어노테이션의 정보 유지 기준)
Aspect.java
@Component
@Aspect
public class LogAspect {
Logger logger = LoggerFactory.getLogger(LogAspect.class);
@Around("@annotation(LogExecutionTime)")
public Object LogExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object proceed = joinPoint.proceed();
logger.info(stopWatch.prettyPrint());
stopWatch.stop();
return proceed;
}
}
OwnerController.java
@GetMapping("/owners/find")
@LogExecutionTime
public String initFindForm(Map<String, Object> model) {
model.put("owner", new Owner());
return "owners/findOwners";
}
LogExecutionTime
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}
결과
6. PSA
Portable Service Abstraction 는 쉽게 말하여 환경에 제약없이 유연하고 확장성 있는 방식의 추상화 구조를 말합니다.
조금 더 유연한 코드를 작성할 수 있다는 의미로 볼 수 있습니다.
ex) 스프링에서 어노테이션을 통해 @GetMapping,
@PostMapping
등 추상화 계층을 사용하여 코딩을 하지만 내부적으로는 HttpServlet을 상속받아 서블릿 기반으로 동작
하게 됩니다.
Spring 웹 MVC(@Controller, @GetMapping)
Spring 트랜잭션(@Transactinal)
setAutoCommit(false);
와commit();
등의 자동처리- JPA, mybatis, hibernate 등 구현체가 변해도 코드는 동일하게
@Transactional
을 사용
Spring 캐시(@Cacheable)
- 사용하는 구현체는 변해도 사용하는 어노테이션은 변화 x
Reference
'TIL' 카테고리의 다른 글
20190517_모듈패턴 및 JSON 기초 (0) | 2019.05.18 |
---|---|
20190515_예제로 배우는 스프링 입문 (0) | 2019.05.15 |
20190512_RestAPI 기초 개념 설정 (0) | 2019.05.15 |
20190410 [mac] 깃허브(Github) remote:Permission to 에러 (183) | 2019.04.10 |