-
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주차] 이태균, 박해원, 박수지, 인수빈 #1
Comments
4단원 @Autowired 애노테이션을 사용한 의존 자동 주입
위의 코드는 의존 대상을 설정 코드에서 직접 주입하는 코드이다. 그렇다면 자동의존주입을 사용하는 방법은 무엇이냐
이 코드는 @Autowired를 써서 자동주입 기능을 사용한 코드이다.
위의 저 코드를 없애고도 ChangePasswordService 클래스에서는 memberDao클래스의 기능을 사용할 수 있게 되었다. @Autowired는 필드 뿐만 아니라 메서드에도 붙일 수 있다.
위 코드처럼 메서드 자동주입기능을 사용하게되면 스프링은 **메서드의 파라미터 타입**에 해당하는 빈 객체를 찾아 인자로 주입하게된다. 일치하는 빈이 없을 때@Autowired 애노테이션을 적용한 대상에 일치하는 빈이 없다면? 그렇다면 자동 주입 가능한 빈이 두개 이상일 때 자동 주입할 빈을 지정하는 방법은 없을까? 만약 @qualifier 애노테이션이 없다면? ->빈의 이름을 한정자로 지정한다. 위 그림에서 first1()메서드로 정의한 빈의 한정자는 빈 이름인 first1이 되는것이다. 상속관계에서의 자동 주입Child라는 클래스가 Parent라는 클래스를 상속받는다고 가정해보자.
이 상태로 main을 실행하면 동일한 타입의 빈을 두개 설정하고 @qualifier 애노테이션을 붙이지 않았을 때와 동일한 exception이 발생한다. ->위에 배웠던 것처럼 설정 클래스와 @Autowired 애노테이션을 붙인 곳에 동일한 @qualifier를 붙여서 주입할 빈을 한정하면 해결할 수 있다.
위 코드에서 print()메서드는 date가 null인 경우에도 null이라는 출력을 한다.
1번의 사용 방법은 아래 코드와 같다.
@Autowired의 애노테이션의 required 속성을 false로 지정하면 Date타입의 빈이 없어도 exception이 발생하지 않고 setDate() 메서드를 실행하지 않는다. 2번의 사용 방법은 아래와 같다.
자동 주입 대상 타입이 Optional인 경우, 일치하는 빈이 존재하지 않으면 값이 없는 Optional을 인자로 전달하고, 일치하는 빈이 존재하면 해당 빈을 값으로 갖는 Optional을 인자로 전달한다. 3번의 사용 방법은 아래와 같다.
@nullable 애노테이션을 의존 주입 대상 파라미터에 붙이면, 스프링 컨테이너는 세터 메서드를 호출할 때 자동주입할 빈이 존재하면 해당 빈을 인자로 전달하고, 존재하지 않으면 인자로 null을 전달한다.
**정리 if)설정 클래스에서 의존을 주입했는데 자동 주입 대상이면 어떻게 될까?
|
5단원
@component 애노테이션은 해당 클래스를 스캔 대상으로 표시한다. 이를 통해 스프링이 검색해서 빈으로 등록하는게 가능하다.
@component 애노테이션에 값을 주었는지에 따라 빈으로 등록할 때 사용할 이름이 결정된다. 값을 주지 않았다면 클래스 이름의 첫 글자를 소문자로 바꾼 이름을 빈 이름으로 사용한다.
이 방식을 사용하면 설정코드에서 FirstDao와 Second에 대한 코드가 없어지기에 설정 클래스 코드가 줄어든다.
스캔 대상에서 제외하기 excludeFilters 속성을 사용하면 특정 대상을 자동 등록 대상에서 제외할 수 있다.
이 코드는 @filter 애노테이션의 type 속성값으로 FilterType.REGEX를 주었다. 이는 정규표현식을 사용해서 제외 대상을 지정한다는 것을 의미한다.
이 코드는 정규표현식 대신 AspectJ 패턴을 사용해서 대상을 지정한다.
이 코드는 특정 애노테이션을 붙인 타입을 컴포넌트 타입에서 제외하는 코드이다. 코드에선 @NoProduct나 @ManualBean 애노테이션을 붙인 클래스를 스캔 대상에서 제외하고 있다. ++ 특정 타입이나 그 하위 타입을 컴포넌트 스캔 대상에서 제외하려면 ASSIGNABLE_TYPE을 FilterType으로 사용하면 된다.
컴포넌트 스캔에 따른 충돌 처리
이런 설정 파일이 있다. spring과 spring2에 Haewon이라는 클래스가 존재하고 두 클래스 모두 @component 애노테이션을 붙였다고 하자.
만약 여기서 설정파일의 haewon 이름을 haewon2로 변경하면 |
[Chapter 6]1. 컨테이너 초기화와 종료
2. 스프링 빈 객체의 라이프사이클
3. 빈 객체의 생성과 관리
|
빈 라이프사이클과 범위
//컨테이너 초기화
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicatoinContext(AppContext.class);
//컨테이너에서 빈 객체를 구해서 사용
Greeter g = ctx.getBean(”greeter”, Greeter.class);
String msg = g.greet(”스프링”);
System.out.prinln(msg);
//컨테이너 종료
ctx.close();
컨테이너가 관리하는 빈 객체의 라이프사이클
스프링 컨테이너는 빈 객체를 초기화하고 소멸하기 위해 빈 객체의 지정한 메서드를 시용
이 두 인터페이스에 각각 afterPropertiesSet() 메서드와 destroy() 메서드르 저장하고 있음 스프링 컨테이너는 초기화 과정에서 afterPropertiesSet() 메서드를 실행, 소멸 과정에서 빈 객체의 destroy() 메서드를 실행함 이때 빈 객체를 소멸할 때 사용중인 데이터베이스 연결을 끊어야함 ex)채팅 클라이언트 : 시작할 때 서버와 연결을 생성하고 종료할 때 연결을 끊음 코드를 보면 destroy()를 통해 컨테이너를 종료할 때 호출한다는 것을 알 수 있음 빈 객체의 초기화와 소멸 : 커스텀 메서드모든 클래스가 InitializingBean 인터페이스와 DisposalbleBean 인터페이스를 상속받아 구혈할 수 있는 것은 아님 → 직접 구현한게 아닌 외부에서 제공받은 클래스를 스프링 빈 객체로 설정하고 싶을 떄도 있음 →@bean 태그에서 initMethod 속성과 destroyMethod 속성을 사용해서 초기화, 소멸 메서드 이름을 지정하면 됌 ex) ‘’’
public class Client2{
private String host;
public void setHost(String host){
[this.host](http://this.host) = host;
}
public void connect(){
‘’
}
public void send(){
‘’
}
public void close(){
‘'
}
} 일 때, 초기화 과정에서 connect() 메서드를 실행하고 소멸과정에서 close() 메서드를 실행하고 싶다? @Bean(initMethod = “connect”, destroyMethod=”close”)
public Client2 client2(){
Client2 client = new Client2();
client.setHost(”host”);
return client;
} 이렇게 할 수 있음 근데 주의할 점은 설정 코드에서 초기화 메서드를 직접 실행할 때 초기화 메서드가 두번 불리면 안됌! 빈 객체의 생성과 관리 범위Client client1 = ctx.getBean(”client”, Client.class);
Client client2 = ctx.getBean(”client”, Client.class);
//client1 == client2 -> true 한 식별자에 대해 한 개의 객체만 존재하는 빈은 싱글톤 범위를 갖는다. (별도 설정 안하면 빈은 싱글톤임) 빈 객체를 구할 때마다 매번 새로운 객체를 생성하려면 프로토타입 빈으로 설정하면됌 @bean어노테이션 밑에 @ Scope(”prototype”)만 설정해주면 끝 그리고 만약 싱글톤 범위를 명시적으로 지정하고 싶다면 @scope(”singleton”) 하면됌 → 프로토타입으로 설정하면 아까 client1 ≠ client2 가 됌 스프링 컨테이너는 프로토타입의 빈 객체를 생성하고 프로퍼티 설정, 초기화는 하지만 생성한 프로토타입 빈 객체의 소멸 메서드는 실행x → 빈 객체의 소멸 처리를 코드에서 직접 해야한다. AOP 프로그래밍
일단 프록시, 대상 객체에 대해 알아보자
이렇게 핵심 기능의 실행은 다른 객체에 위임하고, 부가적인 기능을 제공하는 객체를 프록시이고, 실제 핵심 기능을 실행하는 객체는 대상 객체이다.
스프링의 AOP구현스프링도 프록시를 이용해서 AOP를 구현함 기본적으로 핵심 기능에 공통 기능을 삽입한다. 이때 공통 기능을 Aspect라고 함
스프링 AOP 구현
공통기능을 제공하는 Aspect 구현 메서드를 만들고, @aspect 애노테이션을 이용해서 Aspect를 구현하면 스프링 프레임워크가 알아서 프록시를 만들어줌.
build.gradle에 추가하고 AOP 의존성을 추가하고 빌드를 하였으면 AOP를 활성화하겠다는 어노테이션을 추가해야함 implementation 'org.springframework.boot:spring-boot-starter-aop'
@EnableAspectJAutoProxy
@SpringBootApplication
public class AopApplication {
public static void main(String[] args) {
SpringApplication.run(AopApplication.class, args);
}
} @EnableAspectJAutoProxy 애노테이션은 스프링 컨텍스트 내에서 AspectJ AOP 프레임워크를 사용할 수 있도록 함. 해당 애노테이션을 사용하면 AOP 프록시 빈을 자동으로 등록하고 AOP를 사용할 수 있게 됌 근데 스프링부트에서는 자동으로 AOP 프록시 빈을 등록하고 AOP를 사용할 수 있게 해줌
모든 API에 비즈니스 로직의 실행시간을 측정해야 한다고 가정 @Aspect
@Component
public class LogAspect {
Logger logger = LoggerFactory.getLogger(LogAspect.class);
//모든 패키지 내의 aop package에 존재하는 클래스
@Around("execution(**..aop.*.*(..))")
public Object logging(ProceedingJoinPoint pjp) throws Throwable {
//해당 클래스 처리 전의 시간
StopWatch sw = new StopWatch();
sw.start();
//해당하는 클래스의 메소드 핵심기능을 실행
Object result = pjp.proceed();
//해당 클래스 처리 후의 시간
sw.stop();
long executionTime = sw.getTotalTimeMillis();
String className = pjp.getTarget().getClass().getName();
String methodName = pjp.getSignature().getName();
String task = className + ". " + methodName;
log debug("[ExecutionTime] " + task + "-->" + executionTime + "(ms)");
return result;
}
} AOP 클래스로 설정하기 위해 @aspect 애노테이션을 추가해주고, Spring 빈으로 등록하기 위해 @component 애노테이션을 추가 ( AOP 사용시 빈 등록을 꼭 해야함) 우리가 하고자하는건 모든 API 실행 시간을 측정하는 것이므로, @around 애노테이션을 통해 aop 패키지에 존재하는 모든 클래스에 해당 AOP를 적용하겠다고 설정 → @around("execution(**..aop..(..))") 실행 시간 측정을 위해 StopWatch를 생성하여 측정을 시작하고, pjp (우리 책에선 joinPoint) 를 통해 실제 핵심 로직을 실행하여 Object 클래스로 결과를 받음 (Object로 결과를 받아야함) 이후에 StopWatch를 중단하여 실행 시간을 밀리세컨드로 계산해 로그를 출력하고 함수를 종료시킴. 만약 실행 실행시간 측정을 밀리세컨트가 아닌 세컨드로 변경한다고 했을때 AOP를 적용하지 않았다면 관련 로직의 모든 코드를 수정하는것이 아닌 AOP를 적용함으로써 핵심 로직에 대한 수정 없이 쉽게 처리 가능! 만약 사용자가 직접 Aspect를 적용하고 싶다면? // 이 어노테이션을 부여하면 해당 메소드의 성능을 로깅합니다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
} @Aspect
@Component
public class LogAspect {
Logger logger = LoggerFactory.getLogger(LogAspect.class);
@Around("@annotation(LogExecutionTime)")
public Object logging(ProceedingJoinPoint pjp) throws Throwable {
//해당 클래스 처리 전의 시간
StopWatch sw = new StopWatch();
sw.start();
//해당하는 클래스의 메소드 핵심기능을 실행
Object result = pjp.proceed();
//해당 클래스 처리 후의 시간
sw.stop();
long executionTime = sw.getTotalTimeMillis();
String className = pjp.getTarget().getClass().getName();
String methodName = pjp.getSignature().getName();
String task = className + ". " + methodName;
log debug("[ExecutionTime] " + task + "-->" + executionTime + "(ms)");
return result;
}
} Around를 execution에서 @annotation으로 변경하고 시간측정 AOP를 적용하고 싶은 클래스에 가서 @LogExecution 애노테이션을 붙이면됌 |
3장의존 관계란?한 객체에서 다른 객체를 생성하거나, 다른 객체의 메서드를 호출한다면, 이를 의존한다고 표현한다.
의존하는 객체를 구하는 방법
변경의 유연함이란?
의존성을 주입하는 방식
각 방식의 장단점은?생성자방식
setter 메서드 방식
어떤 방식이 더 권장될까? 최근, 스프링을 포함한 DI 프레임워크에서는 생성자를 이용한 주입 방식이 권장된다.
이러한 이유로 생성자 주입 방식이 권장된다. 객체 조립기객체의 조립
그렇다면 전달하는 객체를 생성하는 코드는 어디에 위치하면 될까?
관심사의 분리, 유지보수 관점에서 생각한다면 후자가 더 나은 방법이라고 판단된다. 객체 조립기
스프링을 이용한 객체 조립 및 사용스프링의 정보 설정
스프링의 정보 설정에 사용되는 애노테이션을 확인해보자.
설정 클래스를 통한 정보 설정이 끝났다면, 이 정보를 바탕으로 실제로 객체를 생성하고 의존 객체를 주입하는 기능을 할 곳이 필요하다. 바로 이 역할을 하는 것이 Container이다. 앞서 언급한 객체 조립기에서는 해당 기능을 전담하는 클래스를 만들어 사용한다고 했다. 스프링 Container 인터페이스 및 구현체 종류스프링 컨테이너는 BeanFactory와 ApplicationContext 라는 두 종류의 인터페이스로 구현되어 있다.
스프링 컨테이너의 구현체 종류는 다음과 같다.
스프링 Container의 기능 및 생성스프링 Container의 기능
스프링 Container의 생성
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppCtx.class); 스프링 Container를 사용하는 이유는?스프링 컨테이너를 사용하지 않고 객체를 생성한다면, new 생성자를 사용해야 한다. 객체 간의 참조가 많아진다 = 의존성이 높아진다 = 객체 지향의 개념에서 멀어진다. 스프링 컨테이너는 구현 클래스의 의존성을 제거하고 인터페이스에만 의존하는 설계를 가능하게 하므로, 객체 지향적 설계에 가까워진다. 스프링 Container의 getBean()컨테이너에서 제공하는 getBean() 메서드를 이용하면 등록된 Bean() 객체를 구할 수 있다. 메서드의 사용 방식은 다음과 같다. //Bean 이름 및 타입 전달
Bean타입 변수명 = 컨테이너.getBean("Bean이름", "Bean 타입");
//Bean 타입 전달
Bean타입 변수명 = 컨테이너.getBean("Bean 타입"); getBean() 메서드 사용 중 발생할 수 있는 Exception
@Autowired스프링 Bean에 의존하는 다른 Bean을 자동으로 주입하고 싶을 때 사용한다.
자세한 내용은 4장에서 다루므로 책에 나온 예제 case만 간단하게 설명했다. |
[Chapter 7]1. 프로젝트 준비
[리스트 7.1] factorial → [리스트 7.2], [리스트 7.3] @OverRide (각각 for문, 재귀를 이용해 인터페이스 구현) 2. 프록시와 AOP
3. 스프링 AOP 구현4. 프록시 생성 방식 |
The text was updated successfully, but these errors were encountered: