각 알파벳을 한번씩만 이용해서 만든 단어나 문구인지 판단하라.
public class Isogram {
public boolean isIsogram(String str) { // 각 알파벳을 한번씩만 이용해서 만든 단어나 문구인지 판단하는 메서드
String str2 = str.toUpperCase(); //입력된 전체 문자열을 모두 소문자로 변환
for(int i=0; i<str2.length()-1; i++){ // 뒤에 있는 인덱스값들과 하나씩 비교
for(int j=i+1; j<str2.length(); j++){
if(str2.charAt(i)==str2.charAt(j)) return false;
}
}
return true;
}
}
public class Isogram {
public boolean isIsogram2(String str) { // 두번째 방법
if(str.length() == 0) return true; //입력된 문자열이 공백일 경우 true를 리턴
HashMap<Character, String> cache = new HashMap<Character, String>(); //사용된 알파벳을 저장할 HashMap을 선언
str = str.toLowerCase(); //입력된 전체 문자열을 모두 소문자로 변환
for(int i = 0; i < str.length(); i++) {
if(cache.containsKey(str.charAt(i))) { //HashMap에 이미 해당 알파벳이 저장되어 있다면 false 출력
return false;
}
cache.put(str.charAt(i), "Exists"); // //HashMap에 해당 문자열이 저장되어 있지 않다면, 저장
}
return true;
}
}
Math.sqrt 안쓰고 제곱근을 구하라.
귀납법 이용한 풀이
public class Root {
public String computeSquareRoot(int num) { // Math.sqrt 안쓰고 제곱근 구하기
double num2 = 1.00;
double beforeNum2 = 0.00;
boolean bo = true;
while(bo){
num2 = (num2+num/num2)/2;
if(num2==beforeNum2) bo=false;
beforeNum2 = num2;
}
String result2 = String.format("%.2f", num2);
return result2;
}
}
하나씩 값을 제곱시켜봐서 일치하는지 비교하는 방식의
노가다 풀이
public String computeSquareRoot(int num) {
double[] diffs = new double[]{1, 0.1, 0.01, 0.001}; //소수점 3자리부터 반올림하기 때문에, 배열에 소수점 자리만큼 double값을 선언, 할당
double base = 1;
for(int i = 0; i < diffs.length; i++) {
while (base * base < num) {
base = base + diffs[i];
}
if(base * base == num) {
return String.format("%.2f", base);
}else {
base = base - diffs[i]; //while문에서 오버된 값 한번 빼줘야 함
}
}
return String.format("%.2f", base);
}
하나씩 더해가면서 비교한 방식
일반적으로는 생성자를 통한 의존관계 주입을 사용
ApplicationContext
를 스프링 컨테이너라고 하고 인터페이스로 구현되어 있다. (다형성 적용)
스프링 컨테이너는 XML, 애너테이션 기반의 자바 설정 클래스로 만들 수 있다.
(cf. 예전에는 개발자가 xml을 통해 모두 설정해줬지만, 이러한 복잡한 부분들을 Spring Boot를 사용하면서 거의 사용하지 않게 되었다.)
빈의 인스턴스화, 구성, 전체 생명 주기 및 제거까지 처리한다.
스프링 컨테이너를 통해 원하는 만큼 많은 객체를 가질 수 있다.
의존성 주입을 통해 애플리케이션의 컴포넌트를 관리한다.
// Spring Container 생성 방법
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(DependencyConfig.class);
new AnnotationConfigApplicationContext(구성정보.class)
로 스프링에 있는 @Bean의 메서드를 등록빈 등록
을 함// Annotation
ApplicationContext context = new AnnotationConfigApplicationContext(DependencyConfig.class);
// XML
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
다양한 외부 리소스로부터 구성 메타데이터를 로드할 수 있도록 하는 리소스 문자열이다.
- 스프링 컨테이너가 관리하는 객체
(cf. java Bean = getter와 setter만 가지고 있는 클래스
spring Bean이랑은 다른거임)
인스턴스화된 객체를 의미
스프링 컨테이너에 등록된 객체를 스프링 빈이라고 한다.
@Bean이 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록
빈은 클래스의 등록정보, 게터/세터 메서드를 포함
빈은 컨테이너에 사용되는 설정 메타데이터로 생성
설정 메타데이터
XML
또는 자바 애너테이션
, 자바 코드
로 표현// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// retrieve configured instance
PetStoreService service = context.getBean("memberRepository", memberRepository.class);
// use configured instance
List<String> userList = service.getUsernameList();
추상화 시키는 기능
속성에 따라 컨테이너가 Bean을 어떻게 생성하고 관리할지 결정
@Bean
or <bean>
당 각 1개씩 메타 정보가 생성
Spring이 설정 메타정보를 BeanDefinition 인터페이스를 통해 관리하기 때문에 컨테이너 설정을 XML, Java로 할 수 있는 것 이다.
(cf. 스프링 컨테이너는 설정 형식이 XML인지 Java 코드인지 모르고 BeanDefinition만 알면 된다.)
Bean은 BeanDefinition(빈 설정 메타정보)으로 정의되고 BeanDefinition에 따라서 활용하는 방법이 달라진다.
빈이 존재할 수 있는 범위
리마인드
- 스프링은 스프링 컨테이너를 통해 객체를 관리한다.
- 스프링 컨테이너에서 관리되는 객체를 빈(Bean)이라고 한다.
특정 bean 정의에서 생성된 개체에 연결할 다양한 의존성 및 구성 값뿐만 아니라 특정 bean 정의에서 생성된 개체의 범위도 제어할 수 있다.
Spring Framework는 6개의 범위를 지원하며, 그 중 4개는 ApplicationContext를 사용하는 경우에만 사용할 수 있다.
bean은 여러 범위 중 하나에 배치되도록 정의할 수 있다.
구성을 통해 생성하는 개체의 범위를 선택할 수 있기 때문에 강력하고 유연하다.
사용자 정의 범위를 생성할 수도 있다.
Scope | Description |
---|---|
singleton | (Default) 각 Spring 컨테이너에 대한 단일 객체 인스턴스에 대한 단일 bean definition의 범위를 지정합니다. |
prototype | 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프이다. |
request | 웹 요청이 들어오고 나갈때 까지 유지되는 스코프이다. |
session | 웹 세션이 생성되고 종료될 때 까지 유지되는 스코프이다. |
application | 웹의 서블릿 컨텍스와 같은 범위로 유지되는 스코프이다. |
websocket | 단일 bean definition 범위를 WebSocket의 라이프사이클까지 확장합니다. Spring ApplicationContext의 컨텍스트에서만 유효합니다. |
클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는디자인 패턴
스프링 컨테이너의 시작과 함께 생성되어서 스프링 컨테이너가 종료될 때 까지 유지
(싱글톤 빈의) 하나의 공유 인스턴스만 관리
해당 bean definition와 일치하는 ID 또는 ID를 가진 빈에 대한 모든 요청은 스프링 컨테이너에서 해당 특정 빈 인스턴스를 반환한다.
스프링 컨테이너 종료시 소멸 메서드도 자동으로 실행
단일 인스턴스는 싱글톤 빈의 캐시에 저장
이름이 정해진 빈에 대한 모든 요청과 참조는 캐시된 개체를 반환한다.
Bean 하나에 하나씩 메타 정보가 생성되고, 스프링 컨테이너는 이런 메타 정보를 기반으로 스프링 빈을 생성한다.
public class SingletonTest {
// static DependencyConfig dependencyConfig = new DependencyConfig(); // <<이런식으로 (DI 컨테이너만 사용) 하면
// static MemberService memberService1 = dependencyConfig.memberService(); // memberService1,2가 각각 다른 주소값을 갖음
// static MemberService memberService2 = dependencyConfig.memberService(); // 수 많은 객체를 생성하게 되니 메모리 낭비가 심함
static SingletonService singletonService1 = SingletonService.getInstance(); // 이렇게 하면 같은 객체가 됨. (주소값 출력해보면 같음)
static SingletonService singletonService2 = SingletonService.getInstance();
public static void main(String[] args) {
System.out.println("memberService1 = " + singletonService1);
System.out.println("memberService2 = " + singletonService2);
}
}
public class SingletonService {
private static final SingletonService instance = new SingletonService(); // 인스턴스 한개를 미리 생성
public static SingletonService getInstance() { // getInstance() 메서드를 통해서만 객체 인스터스를 조회할 수 있도록 제한
return instance;
} // 이 메서드를 호출하면 항상 같은 static instance를 호출
private SingletonService() {} // 외부에서 new 키워드로 객체를 생성할 수 없도록 제한
}
위 코드처럼 직접 static 인스턴스를 직접 만들지 않아도 됨.
객체 인스턴스를 싱글톤으로 관리하는 컨테이너
(클래스의 인스턴스가 Java JVM 내의 단 하나만 존재한다는 뜻)
import com.codestates.section2week4.coffee.CoffeeRepository;
import com.codestates.section2week4.coffee.CoffeeService;
import com.codestates.section2week4.member.MemberRepository;
import com.codestates.section2week4.member.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DependencyConfig {
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemberRepository();
}
@Bean
public CoffeeService coffeeService() {
return new CoffeeService(coffeeRepository());
}
@Bean
public CoffeeRepository coffeeRepository() {
return new CoffeeRepository();
}
}
import com.codestates.section2week4.member.MemberService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SingletonTest {
static AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(DependencyConfig.class);
static MemberService memberService1 = ac.getBean("memberService", MemberService.class);
static MemberService memberService2 = ac.getBean("memberService", MemberService.class);
public static void main(String[] args) {
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
}
}
메서드가 Spring 컨테이너에서 관리할 새 객체를 인스턴스화, 구성 및 초기화한다는 것을 나타내는 데 사용
@Configuration
public class DependencyConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
ApplicationContext 구현은 아래와 같은 애너테이션이 달린 클래스로 파라미터를 전달 받고 있다.
@Configuration 클래스가 입력으로 제공되면 @Configuration 클래스 자체가 Bean 정의로 등록되고 클래스 내에서 선언된 모든 @Bean 메서드도 Bean 정의로 등록
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(DependencyConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
// @Component 또는 JSR-330 주석이 달린 클래스는 다음과 같이 생성자에 입력하는 방식으로 사용
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
@Bean
은 메서드-레벨 애너테이션이다.
@Bean 애너테이션은 @Configuration-annoted 또는 @Component-annoted 클래스에서 사용할 수 있다.
<bean />
에서 제공하는 일부 속성을 지원한다.
@Configuration
public class DependencyConfig {
@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
public interface BaseConfig {
@Bean
default TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
@Configuration
public class DependencyConfig implements BaseConfig {
}
@Bean 애너테이션이 추가된(@Bean-annotated) 메서드는 빈을 구축하는데 필요한 의존성을 나타내는데 매개 변수를 사용할 수 있다.
@Configuration
public class DependencyConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration는 해당 객체가 bean definitions의 소스임을 나타내는 애너테이션이다.
@Configuration는 @Bean-annoted 메서드를 통해 bean을 선언한다.
@Configuration 클래스의 @Bean 메서드에 대한 호출을 사용하여 bean 사이의 의존성을 정의할 수도 있다.
설정 정보 없이 자동으로 스프링 빈을 등록하는 기능
(스프링이 제공하는 기능)
@Component
가 붙은 모든 클래스를 스프링 빈으로 등록해 준다.
의존관계를 자동으로 주입하는 @Autowired
기능도 제공한다.
컴포넌트 스캔을 사용하면 @Configuration이 붙은 설정 정보도 자동으로 등록
(@Configuration 코드에 @Component 애너테이션이 붙어있기 때문)
DependencyConfig 등 @Configuration 설정이 된 파일이 있을 시 아래 코드 추가 : @ComponentScan(excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class))
탐색할 패키지의 시작 위치를 지정하고, 해당 패키지부터 하위 패키지 모두 탐색
@ComponentScan()
의 매개변수로 basePackages = “”를 줄 수 있다.
지정하지 않으면, @ComponentScan
이 붙은 설정 정보 클래스의 패키지가 시작 위치가 된다.
스프링 부트를 사용하면 @SpringBootApplication
를 이 프로젝트 시작 루트 위치에 두는 것을 추천
(@SpringBootApplication
에 @ComponentScan
이 들어있다.)
@Component
: 컴포넌트 스캔에서 사용됩니다.
@Controller
& @RestController
: 스프링 MVC 및 REST 전용 컨트롤러에서 사용된다.
@RestController
는 소스 코드에 @Component를 포함@Service
: 스프링 비즈니스 로직에서 사용
@Repository
: 스프링 데이터 접근 계층에서 사용된다.
@Configuration
: 스프링 설정 정보에서 사용된다.
includeFilters : 컴포넌트 스캔 대상을 추가로 지정
excludeFilters : 컴포넌트 스캔에서 제외할 대상을 지정
FilterType 옵션
기본값
, 애너테이션으로 인식해서 동작TypeFilter
라는 인터페이스를 구현해서 처리생성자를 통해서 의존 관계를 주입 받는 방법
- 생성자에 @Autowired를 하면 스프링 컨테이너에 @Component로 등록된 빈에서 생성자에 필요한 빈들을 주입한다.
생성자 호출 시점에 딱 1번만 호출되는 것이 보장된다.
불변과 필수 의존 관계에 사용된다.
생성자가 1개만 존재하는 경우에는 @Autowired를 생략해도 자동 주입 된다.
NullPointerException 을 방지할 수 있다.
주입받을 필드를 final 로 선언 가능하다.
@Component
public class CoffeeService {
private final MemberRepository memberRepository;
private final CoffeeRepository coffeeRepository;
@Autowired
public CoffeeService(MemberRepository memberRepository, CoffeeRepository coffeeRepository) {
this.memberRepository = memberRepository;
this.coffeeRepository = coffeeRepository;
}
}
setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존 관계를 주입하는 방법
@Component
public class CoffeeService {
private MemberRepository memberRepository;
private CoffeeRepository coffeeRepository;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setCoffeeRepository(CoffeeRepository coffeeRepository) {
this.coffeeRepository = coffeeRepository;
}
}
생성자 주입과 차이점은 생성자 대신 set필드명 메서드를 생성하여 의존 관계를 주입
@Autowired
를 입력하지 않으면 실행이 되지 않는다.
@Component
가 실행하는 클래스를 스프링 빈으로 등록@Autowired
있는 것들을 자동으로 의존 관계를 주입필드에 @Autowired 붙여서 바로 주입하는 방법
코드가 간결해서 예전에 많이 사용했다.
but 외부에서 변경이 불가능하여 테스트하기 힘들다는 단점이 있다.
DI 프레임워크가 없으면 아무것도 할 수 없다.
실제 코드와 상관 없는 특정 테스트를 하고 싶을 때 사용할 수 있다.
@Component
public class CoffeeService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private CoffeeRepository coffeeRepository;
}
일반 메서드를 사용해 주입하는 방법
주입할 스프링 빈이 없을 때 동작해야하는 경우가 있다.
자동 주입 대상 옵션 처리 방법
@Autowired(required=false)
org.springframework.lang.@Nullable
Optional<>
Optional.empty
가 입력된다.의존 관계 주입은 처음 애플리케이션이 실행될 때 대부분 정해지고 종료 전까지 변경되지 않고 변경돼서는 안된다.
생성자 주입은 객체를 생성할 때 최초로 한번만 호출되고 그 이후에는 다시는 호출되는 일이 없기 때문에 불변하게 설계할 수 있다.
생성자 주입
을 사용하면 주입 데이터 누락 시 컴파일 오류가 발생한다.필드 주입
과 수정자 주입
은 빈이 생성된 후에 참조를 하기 때문에 애플리케이션이 어떠한 오류와 경고 없이 구동 됨.java: variable (데이터 이름) might not have been initialized
의존관계 설정이 되지 않으면 객체생성이 불가능
의존성 주입이 필요한 필드를 final 로 선언 가능
(스프링에서) 순환참조 감지가 가능
테스트 코드 작성 용이
수정자 주입이 필요한 경우에만 수정자 주입 사용하고 가능한한 생성자 주입 방식을 사용하자.