newlec Spring

9. 값 형식 DI

지시할때 꼭 레퍼런스가 아닌 값으로 넣을 수도 있다.
property name="exam" value ="exam" 이부분 세터로 값을 injection해보자.

exam.setKor(20);
exam.setEng(50);
이런식으로 자바코드가 되어있을때 값을 준다고 가정해보자.

property를 이용해서 각 값을 넣어주면된다.
중첩된 태그로 표현할 수도 있다.

<bean id="exam" class="spring.di.entity.NewlecExam">
    <property name="kor" value ="10"/>
    <property name="eng" value ="10"/>
    <property name="math" value ="10"/>
    <property name="com">
        <value>10</value>
    </property>
</bean>

10. 생성자 DI

setter로 값을 주입DI햇는데 생성자로 DI할수도 있다.
Exam exam = new NewlecExam(10,10,10,10);하듯이 태그를 사용하면된다.
constructor-arg태그를 사용하면 순서대로 값이 들어간다.
그런데 어떤것이 무엇인지 모르면 버그를 유발할 수 있다.
그래서 index attribute로 순서를 지정할 수 있다.

<!-- Exam exam = new NewlecExam(10,10,10,10);  -->
<bean id="exam" class="spring.di.entity.NewlecExam">
    <constructor-arg index="0" value="10"/>
    <constructor-arg index="1" value="20"/>
    <constructor-arg index="3" value="30"/>
    <constructor-arg index="2" value="40"/>
</bean>

index로 넣으면 헷갈릴 수 있기때문에 name attribute로 직접 지정할 수도 있다.

<bean id="exam" class="spring.di.entity.NewlecExam">
    <constructor-arg name="kor" value="10"/>
    <constructor-arg name="eng" value="20"/>
    <constructor-arg name="com" value="30"/>
    <constructor-arg name="math" value="40"/>
</bean>

억지를 부려서 개수는 같은데 자료형이 다를 경우가 있다.
그래서 어떤 생성자를 호출할지 모호한 경우가 있어 타입을 지정할 수도 있다.
타입을 지정할 수도 있다. type="float" 타입까지 지정해야 정확한 생성자가 호출될 수 있다.

그런데 이렇게 하나하나 지정하는 태그가 너무 번잡하다고 생각될때 지시자로 짧게 바꿀수 있다.
xmlns:p="http://www.springframework.org/schema/p"를 지시자에 추가해주면
p라는 이름으로 값을 넣어줄 수 있다.

namespace란 모듈을 가지고 있고 이름을 식별을 하기 위해서 이것이 namespace이다.
확장된 이름을 가지고 구별하기 위해 하나는 객체를 생성하고 다른의미의 bean이있다고 할때
홍길동:bean 김길동:bean 이렇게하면 각자의 처리 만 하게 된다.

namespace는
1.특정처리에 의해서 실행될수 있도록 그녀석을 처리할수 있도록 짓는다.
2.태그의 이름을 식별하기 위해서 사용한다.

그냥 bean에 kor을 넣으면 속성으로 보지만 p:kor을하면 처리를 할것으로 인식을 하는 것이다.

<bean id="exam" class="spring.di.entity.NewlecExam" p:kor="10" p:eng="10">

11. 콜렉션 생성과 목록 DI

컬렉션 arraylist생성하고 사용해보자.

List<Exam> exams = new ArrayList<>();
exams.add(new NewlecExam(1, 1, 1, 1));

for(Exam e : exams) {
    System.out.println(e);
}

xml에서 이것을 생성해서 xml에서 초기화할수있는지를 알아보자.

List<Exam> exams = (List<Exam>) context.getBean("exams");

다른 것들도 클래스이름(구현객체)를 넣어서 만들었듯이 ArrayList클래스를 넣어주면된다.

<bean id="exams" class="java.util.ArrayList"></bean>

값을 setter로 넣어야하는데add라서 setter아니다.
그래서 property를 넣지는 못한다.
그런데 new ArrayList(Collection c) 컬렉션을 넣는 생성자가 있다.
그래서 컬렉션의 생성자에 대입하는 방식으로 객체를 만들 수 있다.
list태그에 초기화하고자하는 값을 넣을 수 있다.

exam객체를 만들었던 것처럼 list에 exam객체를 만들어서 넣으면된다.
직접 생성하거나 console에 넣었던 것처럼 참조를 넣을 수도 있다.

<bean id="exams" class="java.util.ArrayList">
    <constructor-arg>
        <list>
            <bean class="spring.di.entity.NewlecExam" p:kor="10" p:eng="10"/>
            <ref bean="exam"/>
        </list>
    </constructor-arg>
</bean>

전자는 직접 만들어서 추가하는 것이고 후자는 위에 만들어놓은 exam을 넣는 것이다.
초기화된 컬렉션이 필요하면 정적으로 초기화된 내용을 넣어서 사용할 수 있는 것이다.

생성자에 목록으로 컬렉션을 만들어 ArrayList를 넣은거니 그냥 컬렉션을 뽑을 수 없는지 알고 싶다.
네임스페이스를 이용해서 추가해야한다.
namespace -> util
xmlns:util="http://www.springframework.org/schema/util"

<util:list>
    <bean class="spring.di.entity.NewlecExam" p:kor="1" p:eng="1"/>
    <ref bean="exam"/>
</util:list>

위에서 한거랑 뭐가다른가?
위에서한거는 목록을 세팅하기 위해 만드는것으로 객체를 만드는 역할을 하지 못한다.
후자는 실제로 객체를 만드는 것이다.
컬렉션의 종류가 많으니 어떤 것으로 받을 것인지 추가적으로 구현할 수 잇는 클래스가 뭔지 구체적으로 적어줘야한다.

12. 어노테이션을 이용할 때의 장점과 @Autowired를 이용한 DI 해보기

어플리케이션의 초기화 할때 두가지 방법이 있다.
xml으로 외부파일에 설정정보를 두기와
어노테이션으로 코드 파일을 심기가 있다.

코드파일에 설정정보를 어노테이션이라고하는데 설정을 바꾸고자 한다.
1.어노테이션이 뭔지(자바때 배움)
2.어노테이션의 장점
이 두가지를 알아야한다.

DI부분을 어노테이션으로 변경해보고자 한다.

많은데 다 사용하다보면 알게 된다.
B - b2 결합할때 xml로 뺀 이유가 결합상태를 변경할 수 있으니 이것을 변경하기 쉽도록 뺀것이다.
업데이트를 하기 위해 b3를 만들었다. b2가 필요없어서 이를 대체 하고자 하면 생성할 객체가 b3가 되어야한다.

xml에서 설정을 b2->b3로 바꿔야한다.
그런데 이것조차 불편하다. 객체를 바꾸면 설정도 알아서 바뀌었으면 한다.

아예 코드에 메타데이터를 설정하는 것으로 바꿔버렸다.
@Component 이게나오면 객체화해버린다.
객체를 바꿔버리면 설정도 바뀐다.

XML에서 설정을 분리하는 방식에서 어노테이션으로 바꾸는 수업으로 해보고자 한다.

<bean id="exam" class="spring.di.entity.NewlecExam" p:kor="10" p:eng="10">
<bean id="console" class="spring.di.ui.InlineExamConsole">
    <property name="exam" ref="exam"/>
</bean>

현재 우리는 InlineExamConsole를 만들고 exam객체를 di하고있다.

InlineExamConsole객체에 exam객체를 injection해주는 것을 먼저 알아보자.
여기서 사용되는게 @Autowired 어노테이션이다.
setter에 @Autowired을 붙이면 injection된다.

어노테이션을 추가하기전에 참조할 객체를 지워버리면 NullPointerException이 발생한다.

@Autowired
@Override
public void setExam(Exam exam) {
    this.exam = exam;
}

xml에서 객체를 읽고 객체가 만들어지면 안에서 @Autowired가 있는 setter가 있는지 확인을 한다.
@Autowired는 자동으로 객체를 연결해달라는 것이다.
스프링이 객체를 만들면서 알아서 찾아서 세팅을 해준다.

근데 또 에러가 발생한다. 특별히 지시를 하지 않으면 클래스가서 확인을 하지 않기때문이다.
굳이 생성하지 않는 클래스에가서 객체를 만들지 않는다. 지시서에 키워드를 넣어줘야한다.

namespaces 의 context를 추가해주어야 한다.
xmlns:context="http://www.springframework.org/schema/context"

<context:annotation-config/> 태그를 추가해줘야한다.
config중에서 일부분을 annotation햇으니 객체들의 어노테이션을 찾아보라고 지시하는 것이다.

13. @Autowired의 동작방식 이해와 @Qualifier 사용하기

@Autowired로 바인딩이 되는 것은 알겠는데 뭘 근거로 바인딩 되는지
xml설정할땐 ref로 같은 이름의 exam을 바인딩했는데 같은 exam이 있다뭔 뭘 바인딩해야할까
만약 exam2가있다면 뭐를 바인딩 할것인가를 생각해보아야한다.

자동으로 하면 잘못된 방향의 di가 될 수 있으니 설정을 해줘야한다.

IOC컨테이너는 스프링이 가진것을 조립해준다.
지시서를 읽고 exam객체만들고 console객체를 만들고 di를 해주는 상태이다.

exam:Exam <- console:InlineExamConsole 이렇게 참조하는 상태가 되는 것이다.

어떤기준으로 Exam객체를 찾아서 넣는가? 자료형 vs 변수명

1.변수명을 바꿔보자
<bean id="exam1" class="spring.di.entity.NewlecExam" p:kor="10" p:eng="10"/>
아무런 영향없이 잘된다. 심지어 이름을 없애버려도 잘된다.
결국 변수이름이 아닌 타입으로 결정된다는 것이다.

NewlecExam이라는 것을 읽어서 한 것이다. 이것과 부합될 것을 자동으로 찾아서 바인딩 해주는 것이다.

만약 같은 이름을 가진 객체가 두개가 있다면?
<bean id="exam" class="spring.di.entity.NewlecExam" p:kor="10" p:eng="10"/>
<bean id="exam" class="spring.di.entity.NewlecExam" p:kor="10" p:eng="10"/>
에러가 발생한다.

에러메시지를 보면 dependency를 찾고자 햇으나 사용할 수 있는게 두가지가 발견되었다고 에러가 발생한다.
NewlecExam#0, NewlecExam#1두가지자동으로 번호가 붙여진다.

spring.di.entity.NewlecExam#0,spring.di.entity.NewlecExam#1

이름을 한쪽에만 붙여주면 일단 같은 이름이 있는 것을 우선으로 해준다.

<bean id="exam1" class="spring.di.entity.NewlecExam" p:kor="10" p:eng="10"/>
<bean id="exam2" class="spring.di.entity.NewlecExam" p:kor="20" p:eng="20"/>
각자 이름을 붙여주고 구분을 하려고 하면 @Qualifier어노테이션을 사용해야한다.

@Autowired
@Qualifier("exam1")
@Override
public void setExam(Exam exam) {
    this.exam = exam;
}

메소드에 @Qualifier를 붙여주고 이름을 지정해줘야한다.

결론적으로 @Autowired가 뭘기준으로 하는가
1.자료형식 기준
2.애매할경우 변수이름을 사용한다.
3.id를 가지고 메소드의 변수명을 교체하는 것은 문제가있으니 @Qualifier 사용

14. @Autowired의 위치와 required 옵션

@Autowired는 설정할 수 있는 위치가 3군데가 있다.
옵션이 있는데 이 옵션에 대해 알아보고자 한다.
1.setter 2.필드 3.생성자에 사용가능하다.

인젝션하는 방법이 setter와 생성자에 하니 이것은 당연하다.

1.필드

필드에 이름을 붙이면 기본생성자가 출력이 되면서 실행되는 것이다.
필드에 붙이면 기본생성자가 호출되면서 붙게된다.
기본생성자를 지우고 필드에 붙이면 에러가 발생한다.
기본생성자가 없을거면 오버로드 생성자도 없어야한다.
자동으로 컴파일러가 기본생성자를 만들어주기때문이다.

2.생성자

오버로드된 생성자에 붙여보자. 그런데 @Qualifier("exam2")에 빨간줄이 생긴다.
왜냐면 setter와 필드는 exam이 하나만 들어가는데 생성자의 경우 객체가 두개 들어가는 경우가 발생할 수도 있다.
public InlineExamConsole(Exam exam1, Exam exam2) <-이런식
그래서 @Qualifier를 생성자의 파라미터에 넣어줘야한다.

@Autowired
public InlineExamConsole(@Qualifier("exam1") Exam exam) {
    this.exam = exam;
}

3.setter 메소드

setter는 xml에서 주입될때 setter가 실행될때 사용된다.

@Autowired
@Qualifier("exam1")
@Override
public void setExam(Exam exam) {
    this.exam = exam;
}

어떤 함수가 실행되면서 인젝션되게 하고싶은가에 따라서 적절히 작성해야한다.
강사님은 필드에 @Autowired하는 것을 선호한다고 한다.
주입되서 실제로 사용되는 메소드가 print()인데 주입되는 객체가 없다면 에러가 발생한다.
가끔은 주입되는 객체가 없으면 없는대로 실행되도록 하고 싶다.
기본값을 if문으로 처리해줘야한다.

@Override
public void print() {
    if(exam == null) {
    System.out.printf("total is %d, avg is %f\n", 0, 0);
    } else {
    System.out.printf("total is %d, avg is %f\n", exam.total(), exam.avg());
    }
}

값을 나중에 꽃아넣을 수 잇게 하고싶다. 그런데 @Autowired는 무조건 객체를 불러들어와야한다.
@Autowired(required = false)을 해주면 없어도 일단 ok로 넘어가게된다.

@Autowired(required = false)
@Qualifier("exam1")
private Exam exam;

15. 어노테이션을 이용한 객체생성

xml에서 객체를 만드는 것을 빼버리고 싶다.
클래스위에 @Component를 붙여줘야한다. 그런데 붙이기만 하면 에러가 발생한다.

@Autowired를 읽기위해서 context:annotation-config/으로 어노테이션으로 설정한게 있다고 했을때 이때 객체를 돌아보게 된다.
돌아가서 확인하게 된것이다.

@Component 그런데 이것은 처음부터 스프링에서 생성을 하지 않으니 클래스를 볼일이없다. 아예 읽혀지를 않는다.
<context:component-scan base-package="spring.di.ui"/>을 작성해줘야한다.
일단 spring.di.ui에 가서 패키지를 뒤져보고 컴포넌트를 읽어보고 그것을 객체화 해달라고 지시해주는 것이다.

그런데 이걸로 실행을 하면 에러가 발생한다. 이름을 'console'로 둔적이 없기때문이다.
ExamConsole console = context.getBean(ExamConsole.class);
console.print();
형식으로 찾으면 에러가 발생하지 않는다.

객체를 생성하면서 알아서 읽어주기 때문에 context:annotation-config/도 필요없다.

이름으로 찾고 싶다면 @Component에 이름을 적어주면된다.

@Component("console")
public class InlineExamConsole implements ExamConsole

exam도 넣고자 한다면 base-package에 두개 다 넣어주고 @Component태그를 달아줘야한다.

<context:component-scan base-package="spring.di.ui, spring.di.entity"/>

@Component
public class NewlecExam implements Exam {}

<context:component-scan base-package="spring.di.ui, spring.di.entity"/>

total is 0, avg is 0.000000 똑같이 이렇게 나오지만
null값이라 0이나오는게아니라 생성자에 값이 없어서 나오는 것이다.

이 객체에 값을 초기화하는 방법은 다음 장에 알아보자.

16. 특화된 @Component 어노테이션 (@Controller/@Service/@Repository)

어노테이션으로 객체를 생성하면 값을 어떻게 넣는가? @Value어노테이션으로 값을 설정할 수 있다.
@Value("20")
private int kor; 각 속성에다가 값을 줄수잇다.

근데 사실 모든 것에 @Compnent를 붙이는게 맞지 않다.
@Compnent MVC방식으로 만들때 업무형 로직을 가지고 있는 것들을 다음과 같이 붙인다.
좀더 의미론적으로 값을 하는 어노테이션이 있다.
@Controller / @Service / @Repository 세가지가 있다.

좀 더 역할로서의 의미를 부여하는 것이다.
객체화하고자하는 클래스가 어떤 역할을 하는지를 알 수 있게 되는 것이다.
사용자 입출력하는 controller
요구서비스하는 service
데이터주고받는 repository(Dao)
모두 다 @Component로 붙일 수 있지만 용도가 각자 다르기때문에 다르게 나눠주는게 좋다.

NewlecExam은 entity(Model)이기때문에 @Component를 붙이지 않는다.

직접만드는 클래스 남이만드는 클래스 소스코드를 가지고 있지 않기 때문에 어노테이션을 붙이는 것도 불가능하다.
여타 클래스를 생성하는 방법도 필요한데 스프링을 xml로 해야하느냐 어노테이션을 병행해야하느냐?
고민이생긴다.
다음시간에는 모든 것들을 xml에서 제외해버리고
어노테이션으로 구현할 수 있게끔 config자체를 자바로 바꿔보자.
xml을 지워버릴수도잇다.

17. XML Configuration을 Java Configuration으로 변경하기

설정을 xml을 통해 다양한 태그로 dependecy생성해달라 injection해달라 했엇다.
지금은 들어있는게 이제 거의 없다.

<context:component-scan base-package="spring.di.ui, spring.di.entity"/>
<bean id="exam" class="spring.di.entity.NewlecExam" p:kor="10" p:eng="10"/>

이제 설정은 두가지 밖에없다. 이것조차 다 없애서 순수하게 어노테이션으로 하자.

스프링을 설계할때 설정을 xml로만하거나 어노테이션만으로 하도록 나눠야한다.

setting.xml파일을 지우면 이것을 자바클래스로 만들어서 설정을 해줘야한다.
이것이 java config이다.
xml자체가 자바 파일로 바뀐다고 이해하면된다.

가장먼저 해야할것은 @Configuration이다.
일반적인 자바클래스가아닌 설정을 위한 자바클래스임을 나타내주는 것이다.

<context:component-scan base-package="spring.di.ui"/>
@ComponentScan("spring.di.ui")으로 바꿔서 넣어주면된다.

두개이상 을 넣고자 한다면 자바문법을 따져서 배열형태로 넣으면된다.
@ComponentScan({"spring.di.ui","spirng.di.entity"})

그런데 entity는 compoent가 아니니 @ComponentScan("spring.di.ui")로 설정할 것이다.
빈이라는 태그로 NewlectExam()을 만들었다면 @Bean어노테이션을 사용하면된다.

<bean id="exam" class="spring.di.entity.NewlecExam" p:kor="10" p:eng="10"/>

@Bean
public Exam exam(){
    return new NewlecExam();
}

객체를 직접 생성하는 작업을 통해서 어노테이션을 한다.
이거는 내가 만들고 있는 것이니 뭔가 지시서 느낌이 안든다.
@Bean이라는 것은 생성한 것을 IoC에 담아달라고 지시하는 것이다.
이 이름을 가지고 담아달라고 지시하는 것이다.

함수라면 행위 형태를 가지고 잇는데 얘는 그냥 명사형이다.
id="exam"가 exam()이 되었기때문이다.
함수명이라고 하지말고 컨테이너에서의 이름이라고 생각하면된다.

우리가 ApplicationContext를 생성할때 ClassPathXmlApplicationContext을사용했다.(8장참조하기)

setting.xml로 한것은 위의 세가지 방식을 사용한 것이다.
이번에는 AnnotationConfigApplicationContext로 구현하면된다.

ApplicationContext context = new AnnotationConfigApplicationContext(NewlecDIConfig.class);
ExamConsole console = (ExamConsole) context.getBean("console");
console.print();

config 파일을 하나로만 설정했지만 register라는 함수로 여러개의 설정을 추가할 수도 있다.
한번에할수도 있고 하나씩 할 수도 있다.
refresh();만 해주면된다.

2023.04.14

namespace는 config파일 자체를 못열어서 구버전에서 밖에 못본다.


context 파일 우클릭 > open with > spring 검색 > spring config editor 클릭 > ok

'기초단계 > SPRING' 카테고리의 다른 글

2023.04.18 Spring  (0) 2023.04.18
2023.04.16 Spring  (0) 2023.04.17
2023.04.13 Spring  (0) 2023.04.13
2023.01.25-1 Spring  (0) 2023.01.25
2023.01.24-1 Spring  (0) 2023.01.24

+ Recent posts