다양한 벤더(Oracle, MS SQL, MySQL 등)의 데이터베이스와 연동할 수 있다.
etc..
Java 기반의 애플리케이션에서 사용하는 데이터 액세스 기술의 기본이 되는 저수준(low level) API 이다.
Spring에서는 JDBC API를 직접 사용하기 보다는 Spring Data JDBC, Spring Data JPA 같은 기술이 편리해서 더 많이 사용한다. (Spring Data JDBC나 Spring Data JPA가 더 편리해서 현업에서는 JDBC를 많이 쓰지 않지만 예전 기술로 만든 애플리케이션을 그대로 사용할 경우 Spring JDBC를 사용할 수도 있다.)
Spring Data JDBC나 Spring Data JPA 같은 기술도 내부적으로 JDBC를 이용하기 때문에 JDBC의 흐름정도는 알면 데이터 액세스 기술을 구현하는데 도움이 된다.
➤ JDBC의 동작 흐름
▶️ JDBC 드라이버(JDBC Driver)
JDBC 드라이버는 Database와의 통신을 담당하는 인터페이스이다.
Oracle, MS SQL, MySQL 등에서 해당 벤더에 맞는 JDBC 드라이버를 구현해서 제공을 한다.
JDBC 드라이버 구현체를 이용해서 특정 벤더의 Database에 액세스 할 수 있다.
➤ JDBC API 사용 흐름
1. JDBC 드라이버 로딩
사용하고자 하는 JDBC 드라이버를 로딩한다.
JDBC 드라이버는 DriverManager라는 클래스를 통해서 로딩된다.
2. Connection 객체 생성
JDBC 드라이버가 정상적으로 로딩되면 DriverManager를 통해 데이터베이스와 연결되는 세션(Session)인 Connection 객체를 생성한다.
3. Statement 객체 생성
Statement 객체는 작성된 SQL 쿼리문을 실행하기 위한 객체이다.
객체 생성 후에 정적인 SQL 쿼리 문자열을 입력으로 가진다.
4. Query 실행
생성된 Statement 객체를 이용하여 입력한 SQL 쿼리를 실행한다.
5. ResultSet 객체로부터 데이터 조회
실행된 SQL 쿼리문에 대한 결과 데이터 Set으로 부터 데이터를 조회한다.
6. ResultSet 객체 Close, Statement 객체 Close, Connection 객체 Close
JDBC API 를 통해 사용된 객체들은 사용이 끝나면 실행한 역순으로 차례대로 Close 해준다.
➤ Connection Pool 이란?
데이터베이스와의 연결을 위한 Connection 객체를 생성하는 작업은 비용이 많이 드는 작업이다.
// 모든 공개 매서드 실행
execution(public * *(..))
// set 다음 이름으로 시작하는 모든 메서드 실행
execution(* set*(..))
// AccountService 인터페이스에 의해 정의된 모든 메서드의 실행
execution(* com.xyz.service.AccountService.*(..))
// service 패키지에 정의된 메서드 실행
execution(* com.xyz.servic.*.*(..))
// service 패키지 또는 해당 하위 패키지 중 하나에 정의된 메서드 실행
execution(* com.xyz.service..*.*(..))
// Spring AOP에서만 메서드 실행하는 표현식들
// service 패키지 내의 모든 조인 포인트
within(com.xyz.service.*)
// service 패키지 또는 하위 패키지 중 하나 내의 모든 조인 포인트
within(com.xyz.service..*)
// AcountService 프록시가 인터페이스를 구현하는 모든 조인 포인트
this(com.xyz.service.AccountService)
// AcountService 대상 객체가 인터페이스를 구현하는 모든 조인 포인트
target(com.xyz.service.AccountService)
// 단일 매개변수를 사용하고 런타임에 전달된 인수가 Serializable 과 같은 모든 조인포인트
args(java.io.Serializable)
// 대상 객체가 @Transactional 애너테이션이 있는 조인 포인트
@target(org.springframework.transaction.annotation.Transactional)
// 실행 메서드에 @Transactional 애너테이션이 있는 조인 포인트
@annotation(org.springframework.transaction.annotation.Transactional)
// 단일 매개 변수를 사용하고 전달된 인수의 런타임 유형이 @Classified 애너테이션을 갖는 조인 포인트
@args(com.xyz.security.Classified)
// tradeService 라는 이름을 가진 스프링 빈의 모든 조인 포인트
bean(tradeService)
// 와일드 표현식 *Service 라는 이름을 가진 스프링 빈의 모든 조인 포인트
bean(*Service)
❑ JoinPoint
➤ AOP 적용 위치
AOP는 메서드 실행 위치 뿐만 아니라 다양한 위치에 적용할 수 있다.
적용 가능 지점 : 생성자, 필드 값 접근, static 메서드 접근, 메서드 실행
AOP 를 수행하는 메서드는 이 JoinPoint 인스턴스를 인자로 받게 된다.
JointPoint 인스턴스에서 조인 포인트의 정보를 얻어내야 한다.
➤ JoinPoint
AOP를 적용할 수 있는 지점을 의미하는 추상적 개념
프로그램 실행 중 지점을 나타낸다.
AspectJ를 사용해서 컴파일 시점과 클래스 로딩 시점에 적용하는 AOP는 바이트코드를 실제 조작하기 때문에 해당 기능을 모든 지점에 적용할 수 있다.
프록시 방식을 사용하는 Spring AOP는 메서드 실행 지점에만 AOP를 적용할 수 있다.
프록시는 메서드 오버라이딩 개념으로 동작한다.
생성자나 static 메서드, 필드값 접근에는 프록시 개념이 적용될 수 없다. (프록시는 스프링 AOP에서 사용하기 때문)
프록시를 사용하는 스프링 AOP의 조인 포인트는 메서드 실행으로 제한된다.
프록시 방식을 사용하는 스프링 AOP는 스프링 컨테이너가 관리하는 스프링 빈에만 AOP를 적용할 수 있다.
JoinPoint 메서드는 advice의 종류에 따라 사용방법이 다르지만 기본적으로 advice 메서드에 매개변수로 선언만 하면 된다.
(Default) Spring IoC 컨테이너당 단일 인스턴스로 bean definition scpoe를 지정
prototype
스프링컨테이너가프로토타입빈의생성과의존관계주입까지만관여하고더는관리하지않는매우짧은범위의스코프
request
웹요청이들어오고나갈때까지유지되는스코프
session
웹세션이생성되고종료될때까지유지되는스코프
application
ServletContext 라는클래스타입의객체로생성되어서버가종료될때까지유지
websocket
단일 bean definition 범위를 WebSocket의라이프사이클까지확장함 Spring ApplicationContext의컨텍스트에서만유효함
➤ 스프링 없는 컨테이너만 사용한 객체 생성방법
같은 클래스의 여러 인스턴스를 만들면 서로 다른 주소값을 가진다.
수많은 객체를 생성하게 되면 이 방식은 메모리 낭비가 생기고 효율성이 떨어지게 됨
-> 해결 방법으로 싱글톤 패턴을 사용할 수 있음
❑ Singleton Scope
싱글톤 패턴 : 클래스의 인스턴스를 단 1개만 생성하도록 하는 디자인 패턴
스프링 컨테이너의 시작과 함께 인스턴스가 생성되어 스프링 컨테이너가 종료될 때까지 유지됨
싱글톤 빈의 하나의 공유 인스턴스만 관리함
생성자를 private로만 만들어서 외부에서 임의의 new로 인스턴스를 만드는 것을 방지함
스프링 컨테이너 종료시 소멸 메서드도 자동으로 실행됨
➤ 싱글톤?
싱글톤은 해당 빈의 인스턴스를 오직 하나만 생성해서 사용하는 것을 의미함
단일 인스턴스는 싱글톤 빈의 캐시에 저장됨
이름이 정해진 빈에 대한 모든 요청과 참조는 캐시된 개체를 반환함
싱글톤 스코프의 스프링 빈은 여러번 호출해도 모두 같은 인스턴스의 참조 주소값을 가짐
▶️ 싱글톤 디자인 패턴은 어떤 때 사용할까?
예를 들어 CodeStates라는 회사 클래스와 크루, 수강생 클래스가 있다.
크루와 수강생은 여러 명일 수 있으므로 인스턴스를 여러 개 생성하면 되지만
CodeStates는 어떤 크루나 수강생 객체에서 참조해도 동일한 회사이므로 하나의 인스턴스만 생성해도 된다.
싱글톤 패턴 적용 코드
public class CodeStates {
// static 영역에 단일 객체 생성
private static final CodeStates instance = new CodeStates();
// getInstance 메서드로만 인스턴스 호출
public static CodeStates getInstance() {
return instance;
}
// 생성자를 private 으로 선언하여 외부에서 new 키워드로 인스턴스 생성하는 것을 방지
private CodeStates() {
}
public void getInfo() {
System.out.println("CodeStates 입니다.");
}
}
▶️ 코드 특징
static 영역에 객체 인스턴스를 미리 1개 생성함
객체 인스턴스가 필요한 경우 getInstance() 메서드를 통해서만 조회
항상 같은 인스턴스가 호출됨
외부에서 생성자를 new로 새로 만드는 것을 방지하기 위해 private으로 생성자를 생성함
스프링 컨테이너의 기본값은 싱글톤이기 때문에 위 코드처럼 직접 static 인스턴스를 만들지 않아도 됨
➤ 싱글톤 패턴의 문제점
싱글톤 패턴을 구현하는 코드 자체는 많음
의존 관계상 클라이언트가 구체 클래스에 의존함
지정해서 가져오기 때문에 테스트하기 어려움
private 생성자를 사용하여 자식 클래스를 만들기 때문에 유연성이 떨어짐
속성 공유
Application 초기 구동 시 인스턴스 생성
➤ 싱글톤 패턴 문제 해결
싱글톤 패턴의 문제는 스프링 컨테이너로 해결할 수 있다.
스프링 컨테이너는 싱글톤 컨테이너 역할을 한다.
싱글톤 객체로 생성하고 관리하는 기능을 싱글톤 레지스트리라고 함
스프링 컨테이너의 위 기능 덕분에 싱글톤 패턴의 모든 단점을 해결하고 객체를 싱글톤으로 유지할 수 있음
싱글톤 컨테이너로 관리하려는 객체의 클래스에 @Configuration(클래스 위에 작성)와 @Bean(메서드 위에 작성)을 작성해준다.
@Bean을 통해 스프링 컨테이너에 등록됨
➤ 싱글톤 방식 주의점
여러 클라이언트가 하나의 객체 인스턴스를 공유하기 때문에 몇가지 조심할 부분이 있다.
상태를 유지하게(stateful) 설계하면 안된다. 무상태(stateless)로 설계해야 한다.
특정 클라이언트가 값을 변경할 수 있으면 안됨
특정 클라이언트에서 의존적인 필드가 존재하면 안됨
클라이언트가 수정할 수 있으면 안됨, 읽기만 가능해야함
스프링 빈의 공유 값을 설정하면 장애가 발생할 수 밖에 없음 -> 공유되지 않는 지역변수, 매개변수 사용