2025.03.11 ~ 03.13
Spring Projects는 여러가지가 있는데 먼저 Spring Framework에 대해 공부한다.
그 뒤 Spring Boot에 대해 배운다.

Java 기반의 Application을 위한 Open Source 프레임워크

Spring Framework에서 핵심적으로 봐야하는 것은 Core technologies 부분이다.
주요 설정
Spring IoC Container 에서 관리되는 객체
계층간 데이터 전달을 위한 객체, 데이터를 단순히 담는 역할
데이터베이스와 직접 상호작용하는 객체, 데이터 저장, 조회 역할
Spring이 애플리케이션의 환경을 관리하는 중심 역할을 하는 객체
일반적으로 "환경" 또는 "설정"을 의미하며, Spring에서는 애플리케이션 환경을 관리하는 객체
= context.getBean("bean id", 클래스명.class);
// spring 컨테이너(context)에서 "bean id"을 찾음
// 찾은 Bean을 맞는 타입으로 변환하여 반환
Spring의 대표적인 IoC 컨테이너(Bean 관리 객체)
Configuration Metadata(설정 메타데이터)를 사용하여 Bean을 등록하는 방식에 따라 Context 객체의 구현체가 달라진다.
ApplicationContext 구현체
.xml에서 Bean을 직접 설정
<bean id="스프링 컨테이너에서 관리할 빈의 이름" class="빈 클래스 주소">
<constructor-arg index="빈객체의인덱스" value="값"/>
<constructor-arg name="필드변수명" value="값"/>
<constructor-arg index="빈객체의인덱스" <value>값</value></constructor-arg>
<constructor-arg name="필드변수명"<value>값</value></constructor-arg>
이제 위와 같은 정보로 Spring이 자동으로 객체를 생성하고 해당 빈을 bean id 로 관리하게 된다.
여러가지 설정하는 방법을 적어놓은 것이다. 편한 방법으로 값을 다 넣어주면 된다.
bean을 실행하는 방법
ApplicationContext context = new GenericXmlApplicationContext("XML Configuration Metadata 파일 경로"); // 주로 bean 객체 명과 혼동되지 않게 ApplicationContext 객체 명은 일반적으로 context로 작성
// new를 사용하는 이유는 ioc container를 만들기 위해서 생성
DTO명 변수명 = (DTO명) context.getBean("bean id"); // getBean은 Object로 받기 때문에 형변환을 해준다.
// 잘못된 클래스로 캐스팅할 경우 오류 발생 가능
DTO명 변수명 = context.getBean(DTO명메타정보.class);
// 단, 같은 타입의 Bean이 여러 개 있으면 오류 발생 가능
DTO명 변수명 = context.getBean("bean id",DTO명메타정보.class)
// 가장 명확하고 안전한 방식
// .class : 클래스의 정보를 담고있는 Class 타입 객체
@Configuration 클래스에서 @Bean을 사용
@Configuration
public class 클래스명{
@Bean(name="bean id")
public DTO명 메서드명(){
return new DTO명(값들,값들...);
ApplicationContext context
= new AnnotationConfigApplicationContext(빈클래스명.class); // @Configuration 어노테이션이 달린 설정 클래스의 메타 정보를 전달
DTO명 변수명 = context.getBean("bean id",DTO명메타정보.class);
Spring에서 Annotation을 사용하면 자동으로 Bean을 등록할 수 있다.
@ComponentScan : base package 로 설정 된 하위 경로에 특정 어노테이션(@Component, @Service, @Repository 등이 붙은)을 가지고 있는 클래스를 Bean 으로 등록하는 기능, 상위 패키지를 설정하는 것을 추천

@Configuration // Spring이 이 클래스를 기반으로 IoC 컨테이너를 구성할 수 있도록 도와주는 역할
@ComponentScan(basePackages="패키지경로")
public class bean클래스명
ApplicationContext context
= new AnnotationConfigApplicationContext(빈클래스명.class);
String[] 변수명 = context.getBeanDefinitionNames(); // getBeanDefinitionNames : 스프링 컨테이너에서 생성 된 bean들의 이름을 배열로 반환, 어떤 bean들이 있는지 출력해보고 아래 bean클래스명을 이용하면 된다.
bean클래스명 변수명 = context.getBean("bean id",bean클래스명메타정보.class);
String[] beanNames = context.getBeanDefinitionNames();
for (String beanName : beanNames) {
System.out.println("beanNames = " + beanName);
}
객체와 객체 간의 의존 관계를 Bean 의 설정 정보를 바탕으로 Container 가 자동으로 연결해주는 것

<bean id="bean id" class="구현 클래스 타입 주소">
<constructor-arg index="index 번호" value="값"/>
<bean id="bean id" class="DTO클래스 주소">
<constructor-arg name="필드변수명" value="값"/>
<constructor-arg name="필드변수명" ref="연결할 bean id"/>
<bean id="bean id" class="DTO클래스 주소">
<property name="필드변수명" value="값"/>
<property name="필드변수명" ref="연결할 bean id"/>
생성 된 다른 bean을 의존성 주입할 경우에는 value 속성이 아닌 ref 속성을 사용하여 bean id를 전달한다.
@Configuration
public class 빈클래스이름 {
// 독립적인 객체 생성(Bean 등록)
@Bean
public 객체명1 객체1생성() {
return new 객체명9("값");
}
@Bean
public 객체명2 객체2생성() {
return new 객체명2("값",객체1생성());
}
@Bean
public 객체명2 객체2생성() {
객체명2 변수명 = new 객체명2();
변수명.set필드1("값");
변수명.set필드2(객체1생성());
return 변수명;
}
}
@Autowired : Type을 이용한 DI를 할 때 사용, Spring Container가 알아서 해당 타입의 Bean을 찾아서 주입
spring이 @Autowired 를 보고 해당 타입의 Bean을 컨테이너에서 찾아서 자동으로 주입
@Primary : 하나의 Interface 를 상속 받는 여러 개의 Class 중 우선 순위가 높은 Bean 을 지정
@Qualifier : 하나의 Interface 를 상속 받는 여러 개의 Bean 객체 중 특정 Bean 을 이름으로 지정, Primary보다 우선 순위 -> @Qualifier가 지정되면 @Primary는 무시됨
Collection : List 타입 , Map 타입으로 여러 개의 Bean 을 동시에 주입 받을 수 있다
@Autowired를 사용하여 List<>, Map<> 타입으로 자동 주입
필드 주입
@Service("bean id")
public class 빈클래스 이름{
@Autowired
private DAO객체 필드명;
// 서비스 로직
public List<DTO객체> 메서드1() {
return 필드명.메서드();
}
// 서비스 로직
public DTO객체 메서드2(자료형 매개변수) {
return 필드명.메서드(매개변수);
}
}
ApplicationContext context = new AnnotationConfigApplicationContext("패키지명"); //컨퍼넌트스캔 기능 활성화
빈클래스명 변수명 = context.getBean("bean id", 빈클래스명.class);
@Service("bean id")
public class 빈클래스 이름{
private DAO객체 필드명;
@Autowired
public 빈클래스 이름(DAO객체 필드명) {
this.필드명 = 필드명;
}
// 서비스 로직
public List<DTO객체> 메서드1() {
return 필드명.메서드();
}
// 서비스 로직
public DTO객체 메서드2(자료형 매개변수) {
return 필드명.메서드(매개변수);
}
}
ApplicationContext context = new AnnotationConfigApplicationContext("패키지명");
빈클래스명 변수명 = context.getBean("bean id", 빈클래스명.class);
변수명.빈메서드().forEach(System.out::printIn)
@Service("bean id")
public class 빈클래스 이름{
private DAO객체 필드명;
@Autowired
public void set필드명 이름(DAO객체 필드명) {
this.필드명 = 필드명;
}
// 서비스 로직
public List<DTO객체> 메서드1() {
return 필드명.메서드();
}
// 서비스 로직
public DTO객체 메서드2(자료형 매개변수) {
return 필드명.메서드(매개변수);
ApplicationContext context = new AnnotationConfigApplicationContext("패키지명");
빈클래스명 변수명 = context.getBean("bean id", 빈클래스명.class);
변수명.빈메서드().forEach(System.out::printIn)
@Configuration 클래스 내부 @Bean 메서드 위
@Service, @Repository, @Component 클래스 위
//필드주입
@Autowired
@Qualifier("빈 id")
private 필드자료형 필드명;
//생성자 주입
@Autowired
public 빈클래스명(@Qualifier("빈 id") 필드자료형 필드명){
this.필드명 = 필드명;
}
// setter 주입
@Autowired
public set필드명(@Qualifier("빈 id") 필드자료형 필드명){
this.필드명 = 필드명;
}
//list
@Autowired
private List<객체명> 변수명;
//Map
@Autowired
private Map<자료형, 객체명> 변수명;
스프링 빈이 생성될 때 생성되는 인스턴스의 범위
@Bean
@Scope("prototype")
public ~
Bean 은 초기화(init), 소멸화(destroy) 의 생명 주기를 가지고 있다.
@Bean(initMethod = "시작할 때 실행되길 원하는 메서드명", destroyMethod = "종료될 때 실행되길 원하는 메서드명")
ApplicationContext는 jvm이 종료가 되더라도 자동으로 종료를 시키지 않는다. -> close()메서드를 제공하지 않음.
실제 close() 메서드를 제공하는 건 AnnotationConfigApplicationContext 클래스
-> 따라서 context를 AnnotationConfigApplicationContext로 다운캐스팅한 후 close() 호출해야 함
((AnnotationConfigApplicationContext) context).close();
컨테이너 강제로 종료시킨 것이다.
implementation("javax.annotation:javax.annotation-api:1.3.2") - gradle에 추가
@Component
public class 클래스명{
@PostConstruct
public void 시작시실행메서드() {
// Bean 생성 후 실행할 코드
}
@PreDestroy
public void 종료시실행메서드() {
// Bean 소멸 전에 실행할 코드
}
}
((AnnotationConfigApplicationContext) context).close();
// AnnotationConfigApplicationContext으로 context를 선언했으면 context.close()만 작성해도 된다.
Key - Value 쌍으로 이루어진 간단한 파일
Spring에서는 @Value, @ConfigurationProperties를 사용하여 Properties 파일의 값을 Spring Bean에 주입할 수 있다
resources 하위에 디렉토리 형식으로 properties설정파일 만든다.
key-value 형식으로 데이터들 작성
@Configuration
@PropertySource("classpath:properties파일경로 ")
public class ContextConfiguration {
// 필드 주입 방식
@Value("${properties안 key값}")
private 자료형 필드명;
@Value("${properties안 key값 잘못적을 시:대체값}")
private 자료형 필드명2;
@Bean
public bean객체명 메서드1(){
return new 메서드2(필드명1, 필드명2, 필드명3);
}
/// 매개변수 주입 방식
@Bean
public bean객체명 메서드3(@Value("${properties안 key값}") 자료형 필드명,@Value("${properties안 key값 잘못적을 시:대체값}")자료형 필드명2){
return new 메서드4(필드명4, 필드명5, 필드명6);
}
}
관점 지향 프로그래밍(Aspect Oriented Programming)

Aspect : 핵심 비즈니스 로직과는 별도로 수행되는 횡단 관심사
Advice : Aspect 의 기능 자체 의미
Join Point : Advice 가 적용될 수 있는 위치를 의미
Pointcut : Join Point 중에서 Advice 가 적용 될 가능성이 있는 부분 선별 의미
Weaving : Advice 를 핵심 비즈니스 로직에 적용하는 것을 의미
@EnableAspectJAutoProxy(proxyTargetClass = true) : Spring AOP를 활성화하고, 프록시(proxy)를 사용하여 핵심 로직을 감싸겠다
@Aspect를 사용하려면 반드시 @EnableAspectJAutoProxy가 필요!
proxyTargetClass = true : CGLIB 기반 프록시를 사용
proxyTargetClass = false : JDK 동적 프록시 (기본값) 사용
핵심 비즈니스 로직과는 별도로 수행되는 횡단 관심사
Aspect가 수행하는 기능 자체
@Before("포인트컷")
public void 메서드1(JoinPoint 매개변수) {}
@After("포인트컷")
public void 메서드2(JoinPoint 매개변수) {}
@AfterReturning(pointcut = "포인트컷", returning = "매개변수")
public void 메서드3(JoinPoint 매개변수, Object result) {}
@AfterThrowing(pointcut = "포인트컷", throwing = "매개변수")
public void 메서드4(JoinPoint 매개변수, Exception exception) {}
Object로 반환 받음
@Around("포인트컷")
public Object 메서드(ProceedingJoinPoint 매개변수) throws Throwable {
Object 변수 = 매개변수.proceed();
return 변수;
}

Advice가 적용될 수 있는 위치
Join Point 중에서 특정 조건을 만족하는 부분만 선별
Advice를 핵심 비즈니스 로직에 적용하는 과정


Java에서 프록시는 대리자를 의미
