-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[4주차] 김지웅 서예원 장은채 윤지우 #3
Comments
Chapter 6: 빈 라이프사이클과 범위🤔빈 객체의 라이프 사이클을 알아보기 전에 스프링 컨테이너 라이프 사이클을 먼저 이해해보자💡스프링 컨테이너는 초기화와 종료라는 라이프 사이클을 갖는다.ex.
💡스프링 컨테이너의 라이프 사이클에 따라 빈 객체도 자연스럽게 생성과 소멸이라는 라이프 사이클을 갖는다🤔그렇다면 스프링 빈 객체의 라이프 사이클 단계는 어떻게 될까
💡프링 컨테이너를 초기화 할때 스프링 컨테이너는 가장 먼저 빈 객체를 생성하고 의존 자동 주입을 통한 의존 설정이 이 시점에서 수행 된다. 빈 객체의 초기화를 수행하기 위해 빈 객체의 지정된 메소드를 호출하고 스프링 컨테이너를 종료하면 이때도 지정된 메소드를 통해 빈 객체의 소멸을 처리한다.🤔 빈 객체의 초기화 와 소멸에 필요한 스프링 인터페이스엔 무엇이 있을까1.org.springframework.beans.factory.lnitializingBean →빈 객체를 생성한 뒤에 초기화 과정이 필요하면 InitializingBean 인터페이스를 상속하고 afterPropertiesSet()메서드를 알맞게 구현하면 된다. 2.org.springframework.beans.factory.DisposableBean 🤔모든 클래스가 InitializaingBean 인터페이스와 DisposableBean 인터페이스를 상속받아 구현할 수 있는 것은 아니다. 그렇다면 그땐 어떻게 해야할까상황) 1.직접 구현한 클래스가 아닌 외부에서 제공받은 클래스를 스프링 빈 객체로 설정했을 때 소스 코드를 받지 못했을때 2.두 인터페이스를 사용하고 싶지 않을 때 해결) 스프링 설정에서 직접 메소드 지정 🚨주의:초기화 메서드가 두 번 실행 되지 않도록 주의 🤔스프링은 다양한 빈 스코프를 제공하여 빈의 생명주기를 관리하는데 기본적으로 제공되는 스코프가 어떻게 되는가1.싱글톤 (singleton):
2.프로토타입(prototype):
🤔스코프 지정 방법//”prototype”을 갖는 @scope 에노테이션을 @bean 에노테이션과 함께 사용
//싱글톤의 범위를 명시적으로 지정하고 싶다면 @scope 에노테이션 값으로 “singleton”을 주면됨
|
스프링5 프로그래밍 입문Chapter 4 의존 자동 주입
1.예제 프로젝트 준비2. @Autowired 애노테이션을 이용한 의존 자동 주입
필드, 메서드 등에 주입할 수 있다. 필드 주입import org.springframework.beans.factory.annotation.Autowired;
public class ChangePasswordService {
@Autowired
private MemberDao memberDao;
public void changePassword(String userId, String newPassword) {
memberDao.updatePassword(userId, newPassword);
}
}
if
|
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
1.```java사용해서 코드처럼 적어주세욯 ㅎ |
1.@qualifier의 두 가지 사용 위치 설명 부분에서 마지막 설명 내용 코드 밖으로 빼주면 너무 좋을거같아용 |
This comment was marked as resolved.
This comment was marked as resolved.
1.빈 객체의 용도에 대해서 예시를 들어서 설명한 부분 너무 좋았는데 모두가 이해하기 쉬운 c++내용이 아니면 이해하기 어려울거같아용 |
|
Chapter 7 : AOP 프로그래밍 요약이 장에서 다룰 내용프록시와 AOP AOP(Aspect-Oriented Programming)란 AOP를 이용하면 공통 기능을 핵심 로직에서 분리하여 별도의 클래스(Aspect)로 작성하고, 1. 프로젝트 준비메이븐 프로젝트의 pom.xml 파일에 다음 의존성을 추가 <dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
</dependencies> spring-context 모듈을 의존 대상으로 추가하면 spring-aop 모듈도 함께 포함됨. Calculator 인터페이스 정의:package chap07;
public interface Calculator {
long factorial(long num){
long result = 1;
for(long i = 1)
}
} 양의 정수 n의 계승은 n!으로 표현하며, 이는 1부터 n까지 숫자의 곱. 예를 들어, 4!은 4 * 3 * 2 * 1 = 24 Calculator 인터페이스 구현 (ImpeCalculator):package chap07;
public class ImpeCalculator implements Calculator {
@Override
public long factorial(long num) {
long result = 1;
for (long i = 1; i <= num; i++) {
result *= i;
}
return result;
}
} Calculator 인터페이스 구현 (RecCalculator):package chap07;
public class RecCalculator implements Calculator {
@Override
public long factorial(long num) {
if (num == 0) return 1;
else return num * factorial(num - 1);
}
} 2. 프록시와 AOP실행 시간 측정 코드 삽입 방법1. ImpeCalculator:public class ImpeCalculator implements Calculator {
@Override
public long factorial(long num) {
long start = System.currentTimeMillis();
long result = 1;
for (int i = 1; i <= num; i++) {
result *= i;
}
long end = System.currentTimeMillis();
System.out.printf("ImpeCalculator.factorial(%d) 실행 시간 = %d\n", num, (end - start));
return result;
}
} 문제점 : 실행 시간 측정 코드가 비즈니스 로직과 섞여 있어 코드가 복잡해짐 2. RecCalculator:public class RecCalculator implements Calculator {
@Override
public long factorial(long num) {
long start = System.currentTimeMillis();
try {
if (num == 0) return 1;
return num * factorial(num - 1);
} finally {
long end = System.currentTimeMillis();
System.out.printf("RecCalculator.factorial(%d) 실행 시간 = %d\n", num, (end - start));
}
}
} 메서드 실행 전후에 시간을 측정하여 실행 시간을 출력한다. 문제점 : 재귀 호출로 인해 실행 시간이 여러 번 출력된다. 3. 실행 시간 측정을 위한 별도 코드 작성:ImpeCalculator impeCal = new ImpeCalculator();
long start1 = System.currentTimeMillis();
long fourFactorial1 = impeCal.factorial(4);
long end1 = System.currentTimeMillis();
System.out.printf("ImpeCalculator.factorial(4) 실행 시간 = %d\n", (end1 - start1));
RecCalculator recCal = new RecCalculator();
long start2 = System.currentTimeMillis();
long fourFactorial2 = recCal.factorial(4);
long end2 = System.currentTimeMillis();
System.out.printf("RecCalculator.factorial(4) 실행 시간 = %d\n", (end2 - start2)); 이것도 문제가 있음 문제점: 실행 시간을 나노초 단위로 측정하려면 모든 측정 코드를 수정해야 한다. 해결법 =======> 프록시 객체를 이용한 해결ExeTimeCalculator:package chap07;
public class ExeTimeCalculator implements Calculator {
private Calculator delegate;
public ExeTimeCalculator(Calculator delegate) {
this.delegate = delegate;
}
@Override
public long factorial(long num) {
long start = System.nanoTime();
long result = delegate.factorial(num);
long end = System.nanoTime();
System.out.printf("%s.factorial(%d) 실행 시간 = %d\n", delegate.getClass().getSimpleName(), num, (end - start));
return result;
}
} 장점 : 실행 시간 측정 코드가 비즈니스 로직과 분리되어 코드의 가독성이 향상된다. ExeTimeCalculator 사용 예시 ===>ImpeCalculator impeCal = new ImpeCalculator();
ExeTimeCalculator calculator = new ExeTimeCalculator(impeCal);
long result = calculator.factorial(4); ExeTimeCalculator 클래스는 Calculator 인터페이스를 구현하고 있다. => 코드 중복을 제거 프록시의 역할 :핵심 기능의 실행을 다른 객체에 위임하고 공통 기능을 제공하는 객체를 프록시라고 함.
프록시와 데코레이터의 차이프록시: 접근 제어에 초점을 맞추며, 핵심 기능을 구현하지 않고 다른 객체에 위임 데코레이터: 기능 추가와 확장에 초점을 맞추며, 기존 기능에 추가적인 기능을 제공 예시public class MainProxy {
public static void main(String[] args) {
ExeTimeCalculator ttCal1 = new ExeTimeCalculator(new ImpeCalculator());
System.out.println(ttCal1.factorial(20));
ExeTimeCalculator ttCal2 = new ExeTimeCalculator(new RecCalculator());
System.out.println(ttCal2.factorial(20));
}
} 실행 결과 : ImpeCalculator.factorial(20) 실행 시간 = 3123 프록시 적용의 이점:
결론 ===>
2.1 AOPAOP란?Aspect Oriented Programming 의 약자로 여러 객체에 공통으로 적용할 수 있는 기능을 분리해서 재사용성을 높여주는 프로그래밍 기법. => 쉽게 말해, 객체 지향 프로그래밍 (OOP)의 상위 호환 AOP의 기본 개념:핵심 기능에 공통 기능을 삽입하는 것 공통 기능 삽입 방법
스프링 AOP는 프록시를 이용한 세 번째 방법을 주로 사용 스프링 AOP는 프록시 객체를 자동으로 생성 => 공통 기능을 구현한 클래스만 알맞게 구현하면 됨 AOP 주요 용어
2.2 Advice의 종류
이유 ===> 대상 객체의 메서드를 실행하기 전/후, 예외 발생 시점 등 다양한 시점에 원하는 기능을 삽입할 수 있음 3. 스프링 AOP 구현스프링 AOP를 이용해 공통 기능을 구현하고 적용하는 방법
3.1 @aspect, @pointcut, @around를 이용한 AOP 구현개발자는 공통 기능을 제공하는 Aspect 구현 클래스를 만듦 Aspect 구현 예제:@Aspect
public class ExeTimeAspect {
@Pointcut("execution(public * chap07..*(..))")
private void publicTarget() {}
@Around("publicTarget()")
public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.nanoTime();
try {
Object result = joinPoint.proceed();
return result;
} finally {
long finish = System.nanoTime();
Signature sig = joinPoint.getSignature();
System.out.printf("%s.%s(%s) 실행 시간: %d ns\n",
joinPoint.getTarget().getClass().getSimpleName(),
sig.getName(), Arrays.toString(joinPoint.getArgs()), (finish - start));
}
}
}
3.2 ProceedingJoinPoint의 메서드Around Advice에서 사용할 공통 기능 메서드는 ProceedingJoinPoint 인터페이스의 주요 메서드:Signature getSignature(): 호출되는 메서드에 대한 정보를 구함 Signature 인터페이스의 주요 메서드:String getName(): 호출되는 메서드의 이름을 구함 |
4. 프록시 생성 방식예제: 프록시 생성 방식1. 기본 코드 수정
// 기존 코드
Calculator cal = ctx.getBean("calculator", Calculator.class); => 이를 RecCalculator 타입으로 변경 // 수정된 코드
RecCalculator cal = ctx.getBean("calculator", RecCalculator.class); 2. 빈 설정 코드
@Bean
public Calculator calculator() {
return new RecCalculator();
} 3. 문제(예외) 발생
Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException:
Bean named 'calculator' is expected to be of type
'chap07.RecCalculator' but was actually of type 'com.sun.proxy.$Proxy17'
4. 프록시 객체 생성 원리스프링은 AOP를 적용할 때, RecCalculator 클래스는 Calculator 인터페이스를 상속받기 때문에, 프록시 객체의 계층 구조
5. 해결 방법
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppCtx {
@Bean
public Calculator calculator() {
return new RecCalculator();
}
} 6. 변경된 코드 실행코드 복사
RecCalculator cal = ctx.getBean("calculator", RecCalculator.class); 위 설정을 적용한 후, 다음과 같이 getBean() 메서드에 RecCalculator 클래스를 사용하면 빈 객체를 가져올 수 있음 4.1 Execution 명시자 표현식execution(수식어패턴? 리턴타입패턴 클래스이름패턴? 메서드이름패턴(파라미터패턴))
=> 각 패턴은 *를 이용하여 모든 값을 표현할 수 있으며, ..(점 두 개)를 사용하여 0개 이상을 의미
execution(public void set*(..))
execution(* chap07.*.*())
execution(* chap07..*.*(..))
execution(Long chap07.Calculator.factorial(..))
execution(* get*(*))
execution(* get*(*, *))
execution(* read*(Integer, ..)) 4.2 Advice 적용 순서예제@Aspect
public class CacheAspect {
private Map<Long, Object> cache = new HashMap<>();
@Pointcut("execution(public * chap07..*(long))")
public void cacheTarget() {}
@Around("cacheTarget()")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
Long num = (Long) joinPoint.getArgs()[0];
if (cache.containsKey(num)) {
System.out.printf("CacheAspect: Cache에서 구함 [%d]\n", num);
return cache.get(num);
}
Object result = joinPoint.proceed();
cache.put(num, result);
System.out.printf("CacheAspect: Cache에 추가 [%d]\n", num);
return result;
}
}
@Configuration
@EnableAspectJAutoProxy
public class AppCtxWithCache {
@Bean
public CacheAspect cacheAspect() {
return new CacheAspect();
}
@Bean
public ExeTimeAspect exeTimeAspect() {
return new ExeTimeAspect();
}
@Bean
public Calculator calculator() {
return new RecCalculator();
}
} public class MainAspectWithCache {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppCtxWithCache.class);
Calculator cal = ctx.getBean("calculator", Calculator.class);
System.out.println(cal.factorial(7));
System.out.println(cal.factorial(7));
System.out.println(cal.factorial(5));
System.out.println(cal.factorial(5));
ctx.close();
}
} 실행 결과 분석첫 번째 factorial(7) 실행: ExeTimeAspect와 CacheAspect가 모두 적용 RecCalculator.factorial(7) 실행 시간: 26775 ns
CacheAspect: Cache에 추가 [7]
CacheAspect: Cache에서 구함 [7]
RecCalculator.factorial(5) 실행 시간: 6247 ns
CacheAspect: Cache에 추가 [5]
CacheAspect: Cache에서 구함 [5] Advice 적용 순서CacheAspect
|
V
ExeTimeAspect
|
V
RecCalculator @order 애노테이션을 이용한 적용 순서 지정@Aspect
@Order(1)
public class ExeTimeAspect {}
@Aspect
@Order(2)
public class CacheAspect {} 예를 들어 위 코드처럼 두 Aspect 클래스에 @order 애노테이션을 적용했다고 가정
ExeTimeAspect
|
V
CacheAspect
|
V
RecCalculator 변경 후 실행 결과
CacheAspect: Cache에 추가 [7]
RecCalculator.factorial(7) 실행 시간: 326207 ns
CacheAspect: Cache에서 구함 [7]
RecCalculator.factorial(7) 실행 시간: 96836 ns 4.3 @around의 Pointcut 설정과 @pointcut 재사용@around 애노테이션에 직접 execution 명시자 지정 예시@Aspect
public class CacheAspect {
@Around("execution(public * chap07..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
// Advice logic
Long num = (Long) joinPoint.getArgs()[0];
if (cache.containsKey(num)) {
System.out.printf("CacheAspect: Cache에서 구함 [%d]\n", num);
return cache.get(num);
}
Object result = joinPoint.proceed();
cache.put(num, result);
System.out.printf("CacheAspect: Cache에 추가 [%d]\n", num);
return result;
}
private Map<Long, Object> cache = new HashMap<>();
} 공통 Pointcut 재사용
@Aspect
public class ExeTimeAspect {
@Pointcut("execution(public * chap07..*(..))")
private void publicTarget() {}
@Around("publicTarget()")
public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.nanoTime();
Object result = joinPoint.proceed();
long finish = System.nanoTime();
System.out.printf("%s.%s(%s) 실행 시간: %d ns\n",
joinPoint.getTarget().getClass().getSimpleName(),
joinPoint.getSignature().getName(),
Arrays.toString(joinPoint.getArgs()),
(finish - start));
return result;
}
} 다른 클래스에서 공통 Pointcut 사용하기
@Aspect
public class ExeTimeAspect {
@Pointcut("execution(public * chap07..*(..))")
public void publicTarget() {}
} @Aspect
public class CacheAspect {
@Around("aspect.ExeTimeAspect.publicTarget()")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
// Advice logic
Long num = (Long) joinPoint.getArgs()[0];
if (cache.containsKey(num)) {
System.out.printf("CacheAspect: Cache에서 구함 [%d]\n", num);
return cache.get(num);
}
Object result = joinPoint.proceed();
cache.put(num, result);
System.out.printf("CacheAspect: Cache에 추가 [%d]\n", num);
return result;
}
private Map<Long, Object> cache = new HashMap<>();
} 별도 클래스에서 공통 Pointcut 정의 및 재사용
public class CommonPointcut {
@Pointcut("execution(public * chap07..*(..))")
public void commonTarget() {}
} @Aspect
public class CacheAspect {
private Map<Long, Object> cache = new HashMap<>();
@Around("CommonPointcut.commonTarget()")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
Long num = (Long) joinPoint.getArgs()[0];
if (cache.containsKey(num)) {
System.out.printf("CacheAspect: Cache에서 구함 [%d]\n", num);
return cache.get(num);
}
Object result = joinPoint.proceed();
cache.put(num, result);
System.out.printf("CacheAspect: Cache에 추가 [%d]\n", num);
return result;
}
} @Aspect
public class ExeTimeAspect {
@Around("CommonPointcut.commonTarget()")
public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.nanoTime();
Object result = joinPoint.proceed();
long finish = System.nanoTime();
System.out.printf("%s.%s(%s) 실행 시간: %d ns\n",
joinPoint.getTarget().getClass().getSimpleName(),
joinPoint.getSignature().getName(),
Arrays.toString(joinPoint.getArgs()),
(finish - start));
return result;
}
} CommonPointcut 클래스는 공통으로 사용할 Pointcut을 정의 => Pointcut 관리 용이 |
Chapter 6: 빈 라이프사이클과 범위🤔빈 객체의 라이프 사이클을 알아보기 전에 스프링 컨테이너 라이프 사이클을 먼저 이해해보자💡스프링 컨테이너는 초기화와 종료라는 라이프 사이클을 갖는다.ex. //컨테이너 초기화: 스프링 컨테이너는 설정 클래스를 읽어 빈 객체를 생성하고, 각 빈을 연결(의존 주입)함
AnnotationConfigApplicationContext ctx= new AnnotaionConfigApplicationContext(AppContext.class);
//컨테이너에서 빈 객체를 구해서 사용 : 컨테이너에 보관된 빈 객체를 구함
Greeter g= ctx.getBean(”greater”,Greeter.class);
String msg=g.greet(”스프링”);
System.out.println(msg);
//컨테이너 종료: 빈 객체의 소멸
ctx.close(); 💡스프링 컨테이너는 설정 클래스(AppContext)에서 정보를 읽어와 빈 객체를 생성하고 의존성을 주입한 뒤, 빈 객체를 사용하게 한다. 컨테이너가 종료되면 빈 객체도 소멸된다.🤔그렇다면 스프링 빈 객체의 라이프 사이클 단계는 어떻게 될까
💡스프링 컨테이너는 먼저 빈 객체를 생성하고, 의존 자동 주입을 통해 의존성을 설정한다. 그런 다음 빈 객체의 초기화를 수행하기 위해 지정된 초기화 메서드를 호출한다. 컨테이너 종료 시에도 지정된 소멸 메서드를 호출해 빈 객체를 소멸시킨다🤔 빈 객체의 초기화 와 소멸에 필요한 스프링 인터페이스엔 무엇이 있을까1.org.springframework.beans.factory.lnitializingBean :초기화 작업이 필요한 빈 객체는 InitializingBean 인터페이스를 구현하고 afterPropertiesSet() 메서드를 오버라이드하여 초기화 작업을 수행한다. public class MyBean implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
// 초기화 작업
}
} 2.org.springframework.beans.factory.DisposableBean public class MyBean implements DisposableBean {
@Override
public void destroy() throws Exception {
// 소멸 작업
}
} 🤔 인터페이스를 사용하지 않을경우 (커스텀 메서드)모든 클래스가 InitializingBean과 DisposableBean 인터페이스를 구현할 수 있는 것은 아니다. 이러한 상황에는 다른 방법을 사용해야 한다:1.직접 구현한 클래스가 아닌 외부에서 제공받은 클래스를 스프링 빈 객체로 설정했을 때 소스 코드를 수정할 수 없는 경우 2.스프링 설정에서 직접 메소드 지정 해결 방법: 스프링 설정에서 메서드 지정 @Configuration
public class AppContext {
@Bean(initMethod = "init", destroyMethod = "cleanup")
public MyBean myBean() {
return new MyBean();
}
} 'initMethod' 속성으로 초기화 메서드를, destroyMethod 속성으로 소멸 메서드를 지정할 수 있다. 🚨 주의: 초기화 메서드가 두 번 실행되지 않도록 주의해야 한다. 이는 InitializingBean 인터페이스와 initMethod 속성을 함께 사용할 때 발생할 수 있다. 🤔다양한 빈 스코스프링은 다양한 빈 스코프를 제공하여 빈의 생명주기를 관리한다. 기본적으로 제공되는 스코프는 다음과 같다: 1.싱글톤 (singleton):-기본 스코프. Client client1 =ctx. getBean("client", Client.class);
CLient client2 = ctx.getBean("client",Client.class); 2.프로토타입(prototype):-요청 시마다 새로운 빈 인스턴스를 생성한다. 🤔스코프 지정 방법프로토타입 스코프"prototype" 스코프를 갖는 빈을 정의하려면 @scope 애너테이션을 @bean 애너테이션과 함께 사용한다. @Bean
@Scope(”prototype”)
public MyBean myBean(){
return new MyBean();
} 싱글톤 스코프싱글톤 스코프를 명시적으로 지정하려면 @scope 애너테이션 값으로 "singleton"을 지정한다. @@Bean(initMethod = “connect”, destroyMethod=”close”)
@Scope(”singleton”) |
Chapter3 스프링DI의존이란?:의존은 변경에 의해 영향을 받는 관계를 의미한다. (아래의 예시를 들어 설명) import java.time.LocalDateTime;
public class MemberRegisterService{
private MemberDao memberDao = new MemberDao();
public void regist(RegisterRequest req){
//이메일로 화원 데이터(Member)조회
Member member=memberDao.selectByEmail(req.getEmail());
if(member !=nul){
//같은 이메일을 가진 회원이 이미 존재하면 익셉션 발생
throw new DuplicateMemberException("dup email "+ req.getEmail());
}
//같은 이메일을 가진 회원이 존재하지 않으면 DB에 삽입
Member newMember= new Member( req.getEmail(), req.getPassWord(),req.getName(),LocalDateTime.now());
memberDao.insert(newMember);
}
} ->MemberDao의 insert()메서드의 이름을 insertMember()로 변경하면 이 메서드를 사용하는 MemberRegisterService 클래스의 소스코드도 함께 변경된다. 이렇게 변경에 따른 영향이 전파되는 관계를 '의존' 이라고 표현한다. DI를 통한 의존 처리:DI의 주요 형태는 의존하는 객체를 직접 생성하는 대신 의존 객체를 전달받는 방식 ...
public class MemberRegisterService{
private MemberDao memberDao;
public MemberRegisterService(MemberDao memberdao){
this.memberDao=memberDao;
}
...
} ->생성자를 통해 MemberResiterService 가 의존하고 있는 MemberDao객체를 주입받은 것이다. 🚨DI를 적용한 결과 MemberRegisterService 클래스를 사용하는 코드는 다음과 같이 MemberRegisterService객체를 생성할때 생성자에 MemberDao객체를 전달해야한다. MemberDao dao=new MemberDao(); //의존 객체를 생성자를 통해 주입
MemberRegisterService svc=new MemberRegisterService(dao); 🤔 객체를 생성하는 부분의 코드가 길어지고 복잡해졌다. 근데 직접 의존 객체를 생성하면 되는데 굳이 생성자를 통해서 의존하는 객체를 주입하는걸까? DI와 의존 객체 변경의 유연함DI를 사용하면 의존 객체를 변경하는 작업이 매우 유연해진다..코드 수정없아 의존객체 교체
-예를 들어, 데이터 저장소를 메모리 기반에서 데이터베이스로 변경하거나, 로깅 구현체를 콘솔 로깅에서 파일 로깅으로 변경할 수 있다. 객체 조립기:main메서드에서 의존 대상 객체를 생성하고 주입하는 방법 대신 객체를 생성하고 의존 객체를 주입한다는것 다시 말해 개별적으로 존재하는 객체들이 서로 협력할 수 있도록 관계를 설정 객체 조립기의 역할:객체 조립기는 애플리케이션의 여러 구성 요소를 생성하고, 이들간의 의존성을 설정합니다. 이를 통해 각각의 독립적인 객체들이 유기적으로 연결되어 동작 할 수 있도록 하며 객체 간의 결합도를 낮추고 재사용성을 높이는것이 중요 객체 조립기 사용 예제//MemberRegisterService객체와 ChangePasswordService 객체에 대한 의존 주입
//다음의 코드는 Assemble 클래스를 사용하는 코드
private staic Assemble assemble =new Assemble();
//Assemble객체를 생성
...
MemberRegisterService regSvc = assemble.getMemberRegisterService();
//get 메서드를 이용해서 필요한 객체를 구하고 그 객체 사용
RegisterRequest req=new RegisterRequest();
req.setEmail(arg[1]);
...
private static void processChangeCommand(String [] arg){
if(arg.length !=4){
printHelp();
return;
}
ChangePasswordService changePwSvc= assemble.getChangePassWordService();
... <위 사진은 조립기가 생성한 객체의 연결 관계> -Assemble 객체를 생성하는 시점에서 사용할 객체가 모두 생성되고 Assemble은 자신이 생성하고 조립한 객체를 리턴하는 메서드를 제공한다. Assemble가 제공하는 메서드를 이용해서 필요한 객체를 구하고 그 객체를 사요하는것은 전형적인 Assemble 사용법이다 |
Chapter 4 의존 자동 주입
1.예제 프로젝트 준비2. @Autowired 애노테이션을 이용한 의존 자동 주입
필드, 메서드,생성자,세터 등에 주입할 수 있다. 필드 주입import org.springframework.beans.factory.annotation.Autowired;
public class ChangePasswordService {
@Autowired
private MemberDao memberDao;
public void changePassword(String userId, String newPassword) {
memberDao.updatePassword(userId, newPassword);
}
}
if
|
The text was updated successfully, but these errors were encountered: