Spring
9. 값 형식 DI
지시할때 꼭 레퍼런스가 아닌 값으로 넣을 수가 있다.
property name="exam" value ="exam" 이부분 세터로 값을 injection해보자.
자바코드로 값을 세팅하고자 하면?
exam.setKor(20);
exam.setEng(50);
이런식으로 자바코드가 되어있을때 값을 준다고하면?
<property name="kor" value ="20"/>
이렇게 값을 줄 수 있다.
이렇게 단일태그 형식으로 할 수도 있지만
<property name="kor">
<value>20</value>/<ref></ref>
</property>
이렇게 중첩된 태그로 표현할 수도 있다.
현제 지시서 에 값이 없어서 0으로 나온다.
<bean id="exam" class="spring.di.entity.NewlecExam">
<property name="kor" value="20" />
</bean>
-> kor 값이추가되어 total is 20, avg is 5.000000이 나온다.
<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" />
</bean>
total is 40, avg is 10.000000
나머지도 추가해보앗다.
10. 생성자 DI
setter로 했는데 이번에는 생성자로 값을 설정해보자
<constructor-arg value="30">
constructor-arg을 사용해서 생성자 순서대로 값을 넣으면된다.
Exam exam = new NewlecExam(10,10,10,10); 이렇게 지시서에 값을 주고 싶을 경우이다.
<constructor-arg value="10" />
<constructor-arg value="20" />
<constructor-arg value="30" />
<constructor-arg value="40" />
그런데 이렇게 하면 어떤게 어떤과목이엇는지 기억이안나서 버그를 유발할 수 잇다.
<constructor-arg index="0" value="10" />
<constructor-arg index="1" value="20" />
<constructor-arg index="2" value="30" />
<constructor-arg index="3" value="40" />
인덱스를 추가함으로써 어떤건지 알 수 있다.
NewlecExam [kor=10, eng=20, math=30, com=40] (Exam의 값을 보기위해 toString을 추가해놧다)
total is 100, avg is 25.000000
인덱스를 쓰지않고 name = "kor" 이런식으로도 표현할 수 있다.
생성자 호출의 모호한 매개변수 순서는 같은데 생성자의 자료형이 다를 경우?
타입을 지정할 수도 있다. type="float" 타입까지 거론해야만 생성자를 정확하게 지정할 수 있다ㅣ.
그런데 이렇게 하나하나 지정하는 태그가 너무 번잡하다.?
설정 파일 처리기 추가 하면된다.
xmlns:p="http://www.springframework.org/schema/p"
->namespace에 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>
-> 객체가 만들어져
NewlecExam [kor=1, eng=1, math=1, com=1]
잘나온다.
그런데 이것을 set으로 바꾸려면? 어케해야할까 add라서 setter가 아니다.
ArrayList를 생성할때 기능에는 new ArrayList(Collection c) 생성자에 컬렉션 객체를 넣을 수도있다.
컬렉션의 생성자에 대입하는 방식으로 객체를 만들 수 있다.
<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>
1은 직접 생성한거고 2는 위에있는 객체를 만들어놧으니 그것을 참조하는 것이다.
NewlecExam [kor=10, eng=10, math=0, com=0]
NewlecExam [kor=10, eng=10, math=0, com=0]
초기화된 컬렉션이 필요하면 정적으로 초기화된 내용으 넣어서 갔다쓸수있는것이다.
생성자에 목록으로 컬렉션을 만들어 ArrayList를 넣은거니 그냥 컬렉션을 뽑을 수없느나?
있는데 별도의 네임스페이스로 추가흘 해야한다.
namespace -> util
<util:list id="exams" list-class="java.util.ArrayList">
<bean class="spring.di.entity.NewlecExam" p:kor="10" p:eng="10"/>
<ref bean = "exam"/>
</util:list>
위에서 한거랑 뭐가다른가?
위에서한거는 목록을 세팅하기 위해 만드는것으로 객체를 만드는 역할을 하지 못한다.
후자는 실제로 객체를 만드는 것이다.
컬렉션의 종류가 많으니 어떤 것으로 받을 것인지 추가적으로 구현할 수 잇는 클래스가 뭔지 구체적으로 적어줘야한다.
12. 어노테이션을 이용할 때의 장점과 @Autowired를 이용한 DI 해보기
어플리케이션의 초기화 할때 xml으로 외부파일에 설정정보를 두기 어노테이션으로 코드 파일을 심기
코드파일에 설정정보를 어노테이션이라고하는데 설정을 바꾸고자 한다.
1.어노테이션이 뭔지(자바때 배움)
2.어노테이션의 장점

많은데 다 사용하다보면 알게 된다.
B - b2 결합하고 있다. xml로 뺀 이유가 결합상태를 변경할 수 있으니 이것을 변경하기 쉽도록 뺀것이다.
업데이트를 하기 위해 b3를 만들었다. b2가 필요없어서 이를 대체 하고자 하면 생성할 객체가 b3가 되어야한다.
xml에서 설정을 b2->b3로 바꿔야한다. 그런데? 이것조차 불편해짐 객체를 바꾸면 설정도 알아서 바뀌었으면 한다.
아예코드에 설정을 심는 것으로 바뀌어버렷다.
@Component 이게나오면 객체화해버린다.
객체를 바꿔버리면 설정도 바뀌어 버린다는 것이다. 매우 편해진다는 것이다.
<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하고있다.
<property name="exam" ref="exam"/>객체와 객체를 di해주는 방법을 먼저 알아보자.
여기서 사용되는게 @Autowired 어노테이션이다.
어노테이션을 추가하기전에 참조할 객체를 지워버리면 NullPointerException이 발생한다.
할당될 게 없기 때문이다.
@Autowired
@Override
public void setExam(Exam exam) {
this.exam = exam;
}
xml에서 객체를 읽고 객체가 만들어지면 안에서 @Autowired가 있는 setter가 있는지 확인을 한다. @Autowired는 자동으로 객체를 연결해달라는 것이다.
스프링이 객체를 만들며 찾아서 세팅을 해주는 것이다.
근데 또 에러가 발생한다.
특별히 지시를 하지 않으면 클래스가서 확인을 하지 않기때문이다. 지시서에 키워드를 넣어줘야한다.
namespaces 의 context를 넣는다.
context:annotation-config/
config중에서 일부분을 annotation햇으니 객체들의 어노테이션을 찾아보라고 지시하는 것이다.
NewlecExam [kor=10, eng=10, math=0, com=0]
total is 20, avg is 5.000000
값이 나오게 된다
13. @Autowired의 동작방식 이해와 @Qualifier 사용하기
그런데 문제가있다. 기본적으로 바인딩하는것은 알게됬는데 무엇을 근거로 세팅을 해주는건가?
만약 exam2가있다면 뭐를 바인딩 할것인가?
자동으로 하면 잘못된 방향의 di가 될 수 있으니 설정을 해줘야한다.
IOC컨테이너는 스프링이 가진것을 조립해준다.
지시서를 읽고 exam객체만들고 console객체를 만들고 di를 해주는 상태이다.
exam:Exam <- console:InlineExamConsole 이렇게 참조하는 상태가 되는 것이다.
뭘기준으로 Exam객체를 찾아서 넣는가? 자료형으로 찾는다 변수명으로 넣는다.
1.변수명을 바꿔보자
<bean id="exam1" class="spring.di.entity.NewlecExam" p:kor="10" p:eng="10"/>
아무런 영향없이 잘된다. 심지어 이름을 없애버려도 잘된다.
결국 변수이름이 아닌 2.타입으로 결정된다는 것이다.
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"/>
-> 에러발생
에러메시지를 보면 dependencty를 찾고자 햇으나 사용할 수 있는게 두가지가 발견되었다고 에러가 발생한다. 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"/>
각자 이름을 붙여주고 구분을 하려고 하면 어떻게 해야할까?
@Autowired
@Qualifier("exam1")
@Override
public void setExam(Exam exam) {
this.exam = exam;
}
메소드에 @Qualifier를 붙여주고 이름을 지정해주자.
@Autowired가 뭘기준으로 한가? 1.자료형식을 기준 2.애매할경우 변수이름을 사용 3.id를 가지고 변수명을 교체하는 것은 문제가있으니 @Qualifier를 사용하자.
14. @Autowired의 위치와 Required 옵션
설정할 수 있는 위치가 3군데가 있다. 옵션이 있는데 이 옵션에 대해 알아보고자 한다.
1.setter 2.필드 3.생성자에 사용가능
인젝션하는 방법이 setter와 생성자에 하니 이것은 당연하다.
필드에 이름을 붙이면? private인데 어떻게 되나? ->기본생성자가 출력이 되면서 실행되는 것이다.
constructor
total is 40, avg is 10.000000
기본생성자가 생성되면서 되길 원하면 필드에 붙이자.
그럼 기본생성자를 지우고 필드에 붙이면? 에러가 발생한다.!!
기본생성자가 없을거면 오버로드 생성자도 없어야한다. 자동으로 컴파일러가 기본생성자를 만들어주기때문임.
이번엔 오버로드된 생성자에 붙여보자. 그런데 @Qualifier("exam2")에 빨간줄이 생긴다.
왜냐면 setter와 필드는 exam이 하나만 들어가는데 생성자의 경우 두개가 들어가는 경우가 발생할 수도 있다.
public InlineExamConsole(Exam exam1, Exam exam2) <-이런식
@Autowired
public InlineExamConsole(@Qualifier("exam1")Exam exam) {
this.exam = exam;
}
그래서 파라미터에 직접 @Qualifier해줘야한다.
어떤 함수가 실행되면서 인젝션되게 하고싶은가에 따라서 적절히 작성해야한다.
주입되서 실제로 사용되는 메소드가 print()인데 주입되는 객체가 없다면 에러가 발생한다.
그런데 만약 주입되는 객체가 없으면 없는대로 실행되도록 하고 싶다
@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로 넘어가게된다.
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("console")
public class InlineExamConsole implements ExamConsole
total is 0, avg is 0.000000 근데 이부분은 exam이 그냥 없어서 null값이라 이렇게 나오는 것임.
exam도 넣고자 한다면 base-package에 두개다 넣어주자.
<context:component-scan base-package="spring.di.ui, spring.di.entity"/>
total is 0, avg is 0.000000 똑같이 이렇게 나오지만
null값이라 0이나오는게아니라 생성자에 값이 없어서 나오는 것이다.
그럼 이 객체에 값을 초기화하는 방법은 다음 장에 알아보자.
https://okky.kr/articles/285262
https://usang0810.tistory.com/26
->에러발생 maven에서 자동으로 만드는 target이랑 내가 원하는 경로랑 겹친다.
target 폴더는 프로젝트 컴파일 시 컴파일된 파일이 저장되는 target/classes가 생성되는 곳.
커밋할때 ingore하기
https://rimkongs.tistory.com/215
위문제가 아니었음.
의존성문제?
https://programming-workspace.tistory.com/48
https://stackoverflow.com/questions/22771826/beandefinitionstoreexception-failed-to-read-candidate-component-class
스프링 버전낮춰서 해결은했는데..! 말이안된다.
오류내용을 더 읽어보니 Unsupported class file major version 61이 있다.
https://annajin.tistory.com/m/100
jdk버전의 오류인것이다. ㅅㅂ..! jdk버전을 11로 낮췄다.
최신 시대에서 점점 버전을 낮춰간다는게 말이안되긴한다.
이딴걸 학원에서 가르친다는건가? 학원에선 새로운 버전으로 새로운일을 하는 것을 알려주길 바란다.
16. 특화된 @Component 어노테이션 (@Controller/@Service/@Repository)
어노테이션으로 객체를 생성하면 값을 어떻게 넣는가? @Value어노테이션으로 값을 설정할 수 있다.
@Value("20")
private int kor; 각 속성에다가 값을 줄수잇다.
total is 20, avg is 5.000000

근데사실 @Compnent를 붙이는게 맞지 않다.
@Compnent MVC방식으로 만들때 업무형 로직을 가지고 있는 것들을 이렇게 한다.
좀더 의미론적으로 값을 하는 어노테이션이 있다. @Controller / @Service / @Repository 가 있다.
좀 더역할로서의 의미를 부여하는 것이다. 객체화하고자하는 클래스가 어떤 역할을 하는지 을 알 수 있게 되는 것이다.
사용자 입출력하는 controller 요구서비스하는 service 데이터주고받는 repository(Dao)
다 @Compnent로 붙일 수 있지만 용도가 각자 다르기때문에 다르게 나눠주는게 좋다.

NewlecExam은 entity이기때문에 @Compnent를 붙이지 않는다.
Model도 붙이지않음. 직접만드는 클래스 남이만드는 클래스 소스코드를 가지고 있지 않기 때문에 어노테이션을 붙이는 것도 불가능하다.
여타 클래스를 생성하는 방법도 필요한데 스프링을 xml로 해야하느냐 어노테이션을 병행해야하느냐?
고민이생긴다. 다음시간에는 모든 것들을 xml에서 제외해버리고 어노테이션으로 구현할 수 있게끔 config자체를 자바로 바꿔보자.xml을 지워버릴수도잇다.
그래서 결국 NewlecExam에는 붙이지 않는다.
17. XML Configuration을 Java Configuration으로 변경하기
설정을 xml을 통해 다양한 태그로 dependecy생성해달라 injection해달라 했엇다. 지금은 들어있는게 이제 거의 없다.
<context:component-scan base-package="spring.di.ui"/>
<bean id="exam" class="spring.di.entity.NewlecExam" p:kor="10" p:eng="10"/>
이제 설정은 두가지 밖에없다. 이것조차 다없애서 순수하게 어노테이션으로 하자.
스프링을 설계할때 설정을 xml로만하거나 어노테이션만으로 하도록 나눠야한다.
setting.xml파일을 지우면 이것을 자바클래스로 만들어서 설정을 해줘야한다.
가장먼저 해야할것은 @Configuration이다.
<context:component-scan base-package="spring.di.ui"/>
다음은 위에서 남겨놓은 내용인 @ComponentScan("spring.di.ui") 다시 넣어준다.
순서상관x
두개이상 을 넣고자 한다면? 자바문법을 따져서 배열형태로 넣는다. @ComponentScan({"spring.di.ui","spirng.di.entity"})
그런데 compoent가 아니니 @ComponentScan("spring.di.ui")로 설정할 것이다.
<bean id="exam" class="spring.di.entity.NewlecExam" p:kor="10" p:eng="10"/>
이것을 설정하고자 한다면? 빈이라는 태그로 NewlectExam()을 만들었다면
@Bean
public Exam exam(){
return new NewlecExam();
}
객체를 직접 생성하는 작업을 통해서 어노테이션을 한다. 이거는 내가 만들고 있는 것이니 뭔가 지시서 느낌이 안든다.
@Bean이라는 것은 생성한 것을 IoC에 담아달라고 지시하는 것이다.이 이름을 가지고 담아달라는 것임.
함수라면 행위 형태를 가지고 잇는데 얘는 그냥 명사형임. id="exam"가 exam()이 되었기때문임. 함수명이라고 하지말고 컨테이너에서의 이름이라고 생각하면된다.
package spring.di;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import spring.aop.entity.Exam;
import spring.aop.entity.NewlecExam;
@ComponentScan("spring.di.ui")
@Configuration
public class NewlecDIConfig {
@Bean
public Exam exam(){
return new NewlecExam();
}
}
우리가 ApplicationContext를 생성할때 ClassPathXmlApplicationContext을사용했다.(8장참조하기)

setting.xml로 한것은 위의 세가지 방식을 사용한 것이다.
이번에는 AnnotationConfigApplicationContext로 구현하면된다.
ApplicationContext context = new AnnotationConfigApplicationContext(NewlecDIConfig.class);
config 파일을 하나로만 설정했지만 register라는 함수로 여러개의 설정을 추가할 수도 있다.

한번에할수도 있고 하나씩 할 수도 있다.
refresh();만 해주면된다.
'기초단계 > SPRING' 카테고리의 다른 글
| 2023.01.17 Spring (0) | 2023.01.17 |
|---|---|
| 2023.01.16-2 Spring (1) | 2023.01.16 |
| 2023.01.16-1 Spring (0) | 2023.01.16 |
| 2023.01.15-2 Spring (0) | 2023.01.15 |
| 2023.01.13 Spring (0) | 2023.01.13 |