@ExceptionHandler(Exception.class) ← 처리할 예외 적기
•
()안의 예외가 발생했을 때 어노테이션이 달린 메서드가 호출된다.
•
예외가 여러개일땐 {예외1, 예외2} 이런식으로 배열로 처리한다
@ControllerAdvice(”패키지”) ← 패키지 안 적으면 모든 패키지에 적용
•
모든 컨트롤러에 대한 ‘클래스'
•
전역 예외 처리 메서드들을 정의해놓는다 (예외처리가 중복일 경우 컨트롤러 내 메서드가 우선)
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
•
응답의 상태 코드를 변경할 때 사용한다.
web.xml에서 errorpage 지정
•
상태 코드별로 매핑
•
jsp 페이지 맨 위 선언문에 isErrorPage=”true” 추가하면 Exception 사용가능
◦
상태 코드가 강제로 500으로 고정됨
SimpleMappingExceptionResolver
•
예외 종류별로 매핑
•
servlet.context.xml에 등록
DispatcherServlet
•
서블릿들의 입력부분을 공통적으로 처리해주는 서블릿 (전처리)
•
중간에 나눠놓은건 느슨한 결합을 위함 → 변경에 유리하게 하려고 → 관심사의 분리
viewresolver
•
registerForm이라는 view 이름을 주면 prefix, suffix 접두사 접미사를 붙여서 실제 뷰 이름을 반환해준다 (/WEB-INF/views/registerForm.jsp)
•
Servlet-Context.xml의 InternalResourceViewResolver도 같은 역할을 한다
WebDataBinder
•
예외 발생하지 않고 컨트롤러에게 보낸다. (컨트롤러가 처리하게끔함)
•
@InitBinder 어노테이션이 붙은 메서드는 컨트롤러 내에서만 적용되는 타입변환 메서드다
◦
기본적으로 CustomDateEditor 클래스를 제공해준다
◦
ex) binder.registerCustomEditor(Date.class, new CustomDateEditor(df, false));
•
@DateTimeFormat(pattern=”yyyy-MM-dd”) 어노테이션은 Date 타입 변수 하나에만 붙일 수 있다
•
PropertyEditor는 양방향 타입변환으로 특정 타입이나 이름의 필드에 적용 가능하다
◦
ex) binder.registerCustomEditor(String[].class, “hobby”, new StringArrayPropertyEditor(”#”));
◦
디폴트 PropertyEditor는 스프링이 기본적으로 제공한다
◦
커스텀 PropertyEditor는 사용자가 직접 구현하고 PropertyEditorSupport를 상속하면 편리하다
◦
모든 컨트롤러 내에서 변환하려면 WebBindingInitializer를 구현 후 등록한다
◦
특정 컨트롤러 내에서 변환하려면 컨트롤러에 @InitBinder가 붙은 메서드를 작성한다
•
Converter는 단방향 타입 변환으로 PropertyEditor의 단점을 개선했다 (stateful → stateless)
◦
ConversionService : 타입 변환 서비스를 제공하며 여러 컨버터를 등록 가능하다
◦
모든 컨트롤러 내에서 변환하려면 ConfigurableWebBindingInitializer를 설정해야 사용한다
◦
특정 컨트롤러 내에서 변환하려면 컨트롤러에 @InitBinder가 붙은 메서드를 작성한다
•
Formatter는 양방향 타입 변환으로 바인딩할 필드에 적용한다 (@NumberFormat, @DateTimeFormat)
Validator
•
객체를 검증하기 위한 인터페이스
•
@Valid를 사용하려면 Maven Repo에서 API를 받아서 pom.xml에 추가한 후 사용해야함
•
조건문을 통해 필드에 대한 에러를 적을 땐 rejecValue를 사용한다
•
수동 검증 : Validator 객체를 생성해서 직접 검증
•
자동 검증 : @InitBinder 안에 setValidator 해준 뒤 검증할 필드 앞에 @Valid만 붙여주면 된다
•
글로벌 Validator : servlet-context.xml에 빈 등록하고 @Valid 사용
•
사용 예시 : Validator 인터페이스를 구현하고 supports, validate 추상 메서드를 구현한다
——————————————————————————————————————————————————
Context의 getBean을 통해 빈을 호출할 때 객체의 scope에 대한 설정에 따른다
•
scope=”prototype” : 빈을 호출할 때마다 새로운 객체 생성
•
scope=”singleton” : 빈을 호출할 때마다 기존 객체 호출
분리!!!
•
관심사의 분리
•
변하는 것과 변하지 않는 것 분리
•
중복 코드 제거
Bean이란?
•
JavaBeans : 재사용 가능한 컴포넌트, 상태(iv), 게터&세터, 기본생성자
◦
Servlet&JSP Bean : MVC의 Model, EL, scope에 사용하고 JSP Container가 관리
◦
EJB : 복잡한 규칙을 갖고 EJB Container가 관리
◦
Spring Bean : POJO 객체로 단순하고 독립적이고 Spring Container가 관리
Bean의 초기화
•
<property>
◦
setter가 있어야 쓸 수 있지만 setter를 호출하지 않아도 된다는 장점
•
setter
•
<constructor-arg>
◦
생성자를 이용한다
•
<list>, <set>, <map>
Spring Container란?
•
Bean 저장소로 저장, 관리(생성, 소멸, 연결)를 담당
◦
BeanFactory : Bean의 생성, 연결 등의 기본 기능을 정의한 인터페이스
◦
ApplicationContext : BeanFactory를 확장해 여러 기능을 추가 정의한 인터페이스
AC의 종류 중 XML(<bean>)과 Java Config(@Bean) 중 자바 코드 선호
ApplicationContext란?
•
처음 RootAC를 생성한 후 DispatcherServlet을 등록할 때 또 만들어진 AC들은 부모-자식 관계를 가진다
•
RootAC에는 주로 공통된 non-Web의 Bean을 넣고 자식AC는 개별적으로 Bean을 넣어두고 찾는다
ApplicationContext의 관계도
•
ApplicationContext : Application 전체에서 접근할 수 있는 저장소 영역
•
attributes : AC가 갖는 Map 저장소
•
RootAC(XmlWebApplicationContext) : attributes가 맨 처음 만든 AC
•
children : 각 서블릿들에 대한 정보(이름, 경로)를 갖는 저장소
•
DispatcherServlet : children에 의해 생성된 모든 요청의 전처리를 맡는 서블릿
•
ServletWebAC : DS의 contextClass 변수에 생성된 AC로 자신의 parent 변수에 RootAC를 갖는다
ApplicationContext의 실제 구조
•
제일 위에 있는 servletContext는 진짜 ApplicationContext + Cache 인 대문 역할
•
하위 ApplicationContext는 attributes + 서블릿들을 모은 context + 조상인 Service 등을 감싼 역할
•
그 하위에 context가 진짜 context이고 children + DS 등을 갖고 있다
ApplicationContext의 주요 메서드들
제어의 역전 IoC (Inversion of Control)
•
제어의 흐름을 전통적인 방식과 다르게 뒤바꾸는 것
◦
기존 : 호출 → 실행 + 처리 → 결과값 반환
◦
IoC : 호출 + 제공 → 실행 + 처리
•
의존성 주입 DI : IoC에서 제공의 역할로 사용할 객체를 외부에서 주입 해주는 것
◦
수동 주입 : new SuperEngine()과 같이 직접 지정해주는 것
◦
자동 주입 : @Autowired 와 같이 자동으로 빈을 찾아서 연결하는 것
@Autowired ← byType
•
인스턴스 변수, setter, 참조형 매개변수를 가진 생성자, 메서드에 적용 할 수 있다
•
어노테이션을 붙이면 빈으로 등록된 객체 중 맞는걸 자동으로 연결해준다
•
객체의 Type으로 먼저 검색하고 여러개면 똑같은 이름으로 검색한다
◦
@Qualifier()는 같은 이름을 못 찾았을 때 대안을 정의해준다
•
하나하나 붙이는 것보다 생성자에 매개변수로 다 선언한 후 한 번 붙이는걸 선호한다
◦
생성자가 하나일 땐 생략 가능, 여러 개면 꼭 붙여준다
•
@Value()는 변수에 붙으며 해당 변수에 값을 지정해준다
@Resource ← byName
•
Spring Container에서 이름으로 빈을 검색해서 참조 변수에 자동 주입하고 일치하는 이름이 없으면 예외
•
Autowired랑 같은 기능을 하며 객체의 이름(key)을 통해 찾는다.
•
@Resource(name=””) 에서 name을 생략하면 객체의 첫글자를 소문자로 바꾼 이름을 찾는다
@Component
•
<context:component-scan base-package=””/> 에서 지정해준 패키지의 서브 패키지까지 검색
•
@Component 어노테이션이 붙은 클래스를 다 찾아서 빈으로 등록해준다
•
빈으로 등록하는걸 제외하고 싶다면 <context:exclude-filter type=”” expression=””/> 사용
•
google의 guava library를 통해 사용할 수 있다
•
객체를 등록하기 위해 클래스 앞에 붙이는 어노테이션
1.
ClassLoader, ClassPath를 통해 패키지 내의 모든 클래스를 읽어서 Set에 저장
2.
for문을 통해 @Component가 붙은 클래스를 찾는다
3.
null이 아니라면 객체를 생성해 클래스이름을 key로 Map에 저장한다
@Value와 @PropertySource
•
변수에 값을 대입하는 것 말고도 SystemProperties나 SystsemEnvironment를 불러와서 대입할 수 있다
•
또한 .properties 파일에서 =를 구분자로 값을 찾아와 대입할 수 있다 (PropertySource 사용해야함)
스프링 어노테이션과 표준 어노테이션
DB를 이용한 TDD
•
JUnit 테스트에서 test 클래스의 DataSource 인스턴스 변수는 클래스 내에서는 인스턴스 메서드와 공유되지만
@Test가 붙은 테스트 메서드는 개별 객체로 실행되기 때문에 test 클래스의 DataSource 인스턴스 변수를 공유받지 못한다
DAO(Data Access Object)
•
데이터에 접근하기 위한 객체
•
DataBase에 저장된 데이터에 대한 CRUD를 수행
•
DB 테이블당 하나의 DAO를 작성한다
•
Controller에 의해서 사용되며 DB의 데이터를 사용하기 위함
계층(layer)의 분리
•
DAO가 없다면 로그인과 회원가입 Controller에서처럼 selectUser와 같은 중복이 발생한다
•
그렇기 때문에 데이터에 접근하는 계층인 영속계층(Persistence Layer, Data Access Layer)과
데이터를 보여주는 계층인 Presentation Layer를 분리한다
•
이걸 통해 관심사의 분리와 중복코드 제거를 할 수 있다
•
DB툴 변경에 유리하다 (MySQL과 Oracle 등)
•
추가로 Presentation Layer에서 하나 더 나눠서 Business Layer로 나눈다
Transaction
•
더 이상 나눌 수 없는 작업의 단위 (insert, update, select 등등)
◦
계좌이체의 경우는 출금과 입금이 하나의 tx(transaction)으로 묶여야함
◦
묶은 경우 둘 다 성공하든가 하나만 실패해도 취소 (all or nothing)
•
TDD 코드를 짜기 위해선 connection을 받아온 후 setAutoCommit(false)로 해줘야한다
또한 try-catch를 이용해 예외발생 시 rollback을 사용할 수 있다
Transaction의 속성 (ACID)
•
원자성(Atomicity) : 나눌 수 없는 하나의 작업으로 다뤄져야 한다
•
일관성(Consistency) : Tx 수행 전과 후가 일관된 상태를 유지해야 한다
•
고립성(Isolation) : 각 Tx는 독립적으로 수행되어야 한다
•
영속성(Durability) : 성공한 Tx의 결과는 유지되어야 한다
Commit 과 Rollback
•
커밋 : 작업 내용을 DB에 영구적으로 저장
•
롤백 : 최근 변경사항을 취소 (마지막 커밋으로 복귀)
•
자동 커밋 : 명령 실행 후 자동으로 커밋이 수행 (rollback 불가)
•
수동 커밋 : 명령 실행 후 명시적으로 commit 또는 rollback을 입력 (SET autocommit = 0;)
Isolation level (1 → 4)
1.
READ UNCOMMITED (Dirty READ) : 커밋되지 않은 데이터로도 읽기 가능
2.
READ COMMITED (Phantom READ) : 커밋된 데이터만 읽기 가능
3.
REPEATABLE READ : Tx이 시작된 이후 변경은 무시됨 (Default)
4.
SERIALIZABLE : 한 번에 하나의 Tx만 독립적으로 수행
AOP (Aspect Oriented Programming)
•
관점지향 프로그래밍으로 횡단 관심사 또는 Cross-cutting concerns이라고도 한다
•
부가적인 코드(advice)를 동적으로 추가해주는 기술이다
•
메서드의 ‘시작’ 또는 ‘끝’에 자동으로 코드를 추가한다
•
관련 용어
◦
target : advice가 추가될 객체로 핵심 기능을 하는 객체
◦
advice : target에 동적으로 추가될 부가적인 코드
◦
join point : advice가 추가될 대상(메서드)
◦
pointcut : join point들을 정의한 패턴 execution(.com.fastcampus..*(..))
◦
proxy : target에 advice가 동적으로 추가되어 생성된 객체
◦
weaving : target에 advice를 추가해서 proxy를 생성하는 동작
•
Advice의 종류 (설정은 XML이나 어노테이션으로 가능)
try-catch 사용 안할 시
◦
around advice (@Around) : 메서드의 시작과 끝 부분에 추가되는 부가 기능 (before+after)
◦
before advice (@Before) : 메서드의 시작 부분에 추가되는 부가 기능
◦
after advice (@After) : 메서드의 끝 부분에 추가되는 부가 기능
try-catch 사용 시
◦
after returning (@AfterReturning) : 예외가 발생하지 않았을 때 실행되는 부가 기능 (try 블럭)
◦
after throwing (@AfterThrowing) : 예외가 발생했을 때 실행되는 부가 기능 (catch 블럭)
•
advice가 추가될 메서드를 지정하기 위한 패턴
◦
execution(반환타입 패키지명.클래스명.메서드명(매개변수 목록))
◦
@Around(”execution(* com.fastcampus.ch3.aop.*.*(..))”)
•
advice의 반환타입은 Object나 void를 사용하여야 한다.
◦
Object를 사용할 때는 return result;로 결과를 반환해야한다
•
advice 클래스에 @Order(1) 과 같이 advice가 실행될 순서를 지정해줄 수 있다.
•
advice 클래스의 매개변수로 ProceedingJoinPoint를 사용하면 메서드의 정보를 이용할 수 있다
◦
pjp.getSignature() 뒤에 getName, getArgs 등과 result
서비스 계층의 분리
•
Presentation Layer에서 비즈니스 로직을 분리해내서 Business Layer를 만든다
Biz Layer는 DAO들을 DI 받고 로직을 처리하는 메서드를 가지며 Controller는 호출하기만 하면 된다
•
registerUser() 에는 UserDao의 insertUser() + UserHistoryDao의 insertUserHistory()의 Tx가 있다
이 Tx는 하나라도 실패하면 안되고 RegisterController에서 처리하기엔 너무 복잡하므로
UserService로 비즈니스 로직을 따로 분리해 처리하는게 적합하다
•
각 계층에 붙은 레이어 @Controller, @Service, @Repository는 @Component를 포함한다
그렇기에 <component-scan>으로 스캔이 가능하다.
TransactionManager란
•
DAO의 각 메서드는 개별 Connection을 사용한다 (각 Transaction은 1개의 Connection을 사용한다)
→ 커밋과 롤백에 대한 문제를 해결하기 위해 TxManager를 사용해 Connection을 묶어줘야 한다
•
사용하기 위해선 DAO에서 Connection을 얻거나 반환할 때 DataSourceUtils를 사용해야 한다
•
TxManager를 사용해 DAO의 insert 메서드에 transaction을 적용한 예시이다
•
PlatformTransactionManager와 TransactionStatus를 자동으로 생성하기 위한 코드
•
TxManager를 빈으로 등록한 후 annotation-driven을 통해 @Transactional을 사용할 수 있게 해준다
@Transactional
•
AOP를 이용한 핵심 기능과 부가 기능의 분리
◦
insert()라는 핵심 기능 위 아래로 붙은 부가 기능을 AOP를 통해 떼어낸 후 @Transactional을 사용
◦
메서드뿐만 아니라 클래스나 인터페이스에도 붙을 수 있으며 붙은 곳의 모든 메서드에 적용된다
•
@Transaction 은 RuntimeException, Error만 롤백
•
@Transaction(rollbackFor = Exception.class) 은 Exception을 롤백
@Transactional의 속성
•
propagation : Tx의 경계를 설정하는 방법을 지정
◦
같은 클래스의 메서드를 호출하는 경우 Tx를 나눌 수 없으므로 클래스를 분리한 후 경계를 지정해야함
◦
REQUIRED : Tx이 진행 중이면 참여하고 없으면 새로운 Tx 시작 (Default)
◦
REQUIRES_NEW : Tx이 진행중이건 아니건 새로운 Tx 시작
◦
NESTED : Tx이 진행 중이면 Tx의 내부 Tx로 실행 (서브 Tx) ← git의 브랜치 느낌 savepoint
◦
MANDATORY : 반드시 진행 중인 Tx 내에서만 실행 가능 벗어나면 예외 발생
◦
SUPPORTS : Tx이 진행 중이건 아니건 상관없이 실행
◦
NOT_SUPPORTED : Tx 없이 처리 (Tx이 진행 중이면 잠시 중단)
◦
NEVER : Tx 없이 처리 (Tx이 진행 중이면 예외 발생)
•
isolation : Tx의 isolation level을 지정
•
readOnly : Tx이 데이터를 읽기만 하는 경우 true로 지정하면 성능이 향상
•
rollbackFor : 지정된 예외가 발생하면 Tx을 rollback한다 (RuntimeException, Error는 자동 롤백)
•
noRollbackFor : 지정된 예외가 발생해도 Tx을 롤백하지 않는다
•
timeout : 지정된 시간(초) 내에 Tx이 종료되지 않으면 강제 종료한다