-
[Spring] Spring 공부 정리 1카테고리 없음 2022. 2. 13. 20:41
Spring
https://www.youtube.com/watch?v=XtXHIDnzS9c&list=PLq8wAnVUcTFUHYMzoV2RoFoY2HDTKru3T
기업형 응용 프로그램을 보조하기 위한 프레임워크
다음의 둘중 하나를 선택하여 기업형 응용 프로그램을 만들 수 있다
Java SE + Java EE
Java SE + Spring
MVC, 트랜잭션, 인증과 권한
MVC를 이해하기 위해 DI를 이해
트랜잭션을 이해하기 위해 AOP를 이해
인증과 권한을 이해하기 위해 Servlet Filter를 이해
느슨한 결합력과 인터페이스를 위해 설정 파일을 만들 것인데 왜 쓰는 것인지 알아본다
DI (Dependency Injection)
https://www.youtube.com/watch?v=KJ9Rus3QfUc&list=PLq8wAnVUcTFUHYMzoV2RoFoY2HDTKru3T&index=2
https://www.youtube.com/watch?v=WjsDN_aFfyw&list=PLq8wAnVUcTFUHYMzoV2RoFoY2HDTKru3T&index=3
B1의 기능이 바뀐 경우 -> B1의 코드를 수정한다
B1의 기능이 또 바뀐 경우 -> B1의 코드를 또 수정한다
매번 기능이 바뀔 때마다 코드를 바꿔줘야 한다
B1의 기능이 바뀐 경우 -> B1대신 B2를 만든다.
B1대신 B2를 넣기 위해 인터페이스 B를 사용하면 되지만 부품을 갈아끼워주기 위한 코드(new B1();를 new B2();로 교체)가 역시 필요하게 된다.
일체형 vs 조립형
일체형 - 부품이 무엇이 있는지 모른다. 부품을 갈아끼울 수 없게 된다.
조립형 - 부품을 쉽게 갈아 끼울 수 있다
부품을 꼽거나 다른것으로 갈아끼우는 것이 DI로 볼 수 있다
생성자의 파라메터, setter의 파라메터를 통해 부품을 조립할 수 있다
그러나 조립을 할 때 우리가 부품을 조립하면 불편하기 때문에 Spring이 부품 조립을 자동으로 해준다.
우리가 어떤 부품을 쓸것인지를 설정파일에 설정하면 Spring이 알아서 부품을 조립해준다.
우리는 부품을 넣어 조립할 필요없이 조립된 것만 가져가서 바로 사용하면 된다.
IoC와 IoC 컨테이너
https://www.youtube.com/watch?v=QrIp5zc6Bo4&list=PLq8wAnVUcTFUHYMzoV2RoFoY2HDTKru3T&index=4
우리가 부품을 어떻게 조립시켜 달라는 주문서를 Spring에게 제공해야 Spring이 부품을 조립할 수 있다.
그러한 것을 xml파일 또는annotation을 통해 한다.
Spring은 우리가 주문한 부품들을 IoC 컨테이너에 담아놓는다.
IoC 컨테이너 안에서 작은 부품-> 큰 부품 순서대로 조립하여 부품들을 제공한다.
역순으로 작은 것부터 만들어 낸다
직접 의존성을 주입 해보자
이클립스 일반 Java Proect로 생성하여 Spring 라이브러리를 추가하거나
이클립스 Maven Project로 생성하여 Dependency를 추가하는 방법이 있다.
일단 강의대로 Java Project로 생성하였다.
(밑에서 Maven Project로 변경된다)
- 시험 종류
package spring.di.entity; public interface Exam { int total(); //모든 과목 점수 계산 float avg(); //과목 평균 점수 계산 }
Newlec 시험
package spring.di.entity; public class NewlecExam implements Exam { private int kor; private int eng; private int math; private int com; @Override public int total() { return kor + eng + math + com; } @Override public float avg() { return total() / 4.0f; } }
- 콘솔 종류
package spring.di.ui; public interface ExamConsole { public void print(); }
Inline 형식의 콘솔
package spring.di.ui; import spring.di.entity.Exam; public class InlineConsole implements ExamConsole { private Exam exam; public InlineConsole(Exam exam) { this.exam = exam; } @Override public void print() { System.out.printf("total is %d, avg is %f", exam.total(), exam.avg()); } }
Grid 형식의 콘솔
package spring.di.ui; import spring.di.entity.Exam; public class GridConsole implements ExamConsole { private Exam exam; public GridConsole(Exam exam) { this.exam = exam; } @Override public void print() { System.out.println("┌─────────┬─────────┐"); System.out.println("│ total │ avg │"); System.out.println("├─────────┼─────────┤"); System.out.printf("│ %3d │ %3.2f │\n", exam.total(), exam.avg()); System.out.println("└─────────┴─────────┘"); } }
- 테스트 프로그램
package spring.di; import spring.di.entity.Exam; import spring.di.entity.NewlecExam; import spring.di.ui.ExamConsole; import spring.di.ui.GridConsole; public class Program { public static void main(String[] args) { Exam exam = new NewlecExam(); //ExamConsole console= new InlineConsole(exam); //다른 출력을 하기 위해 부품을 InlineConsole 에서 GridConsole로 교체 ExamConsole console= new GridConsole(exam); console.print(); } }
시험, 콘솔 조립 및 조립된 것을 가지고 출력
직접 주입했던 과정을 스프링 DI를 통해 주입시키도록 바꿔보자
DI를 위해 필요한 이클립스 플러그인 설치하기
https://www.youtube.com/watch?v=Jwoz4ORX60A&list=PLq8wAnVUcTFUHYMzoV2RoFoY2HDTKru3T&index=6
이클립스에서 sts 4 for boot 를 설치하는 것은 자바 config를 기반으로 하기 때문에 설정하거나 추가할 수 있는 기능이 없다 -> sts 3 플러그인 설치 -> IDE 재시작
특정 패키지에서 우클릭 -> other -> Spring -> Spring bean Configuration File 선택 -> 파일이름 setting.xml 입력 추가
-> 자동으로 bean태그의 xmlns 속성이 모두 자동으로 입력된다.
setting.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
이전까지는 Exam 을 받는 생성자를 통해 파라메터를 넘겨 부품이 일체형으로 조립되도록 하였었다.
조립형으로 Exam 부품을 갈아끼울 수 있도록 하기 위해 인터페이스에 setter추가 구현 클래스에 기본 생성자, setter구현
Console의 Exam 부품을 다른 것으로 교체할 수 있기 하기 위해서 Console 인터페이스에 setter 메서드 추가
인터페이스
public interface ExamConsole { public void print(); public void setExam(Exam exam); //Exam 부품 조립 }
구현 클래스 (GridConsole 또한 아래와 같은 식으로 추가)
public class InlineConsole implements ExamConsole { public InlineConsole() { //조립형 } private Exam exam; public InlineConsole(Exam exam) { this.exam = exam; } @Override public void print() { System.out.printf("total is %d, avg is %f", exam.total(), exam.avg()); } @Override public void setExam(Exam exam) { //조립형 this.exam = exam; } }
설정 파일에 들어갈 부분
public class Program { public static void main(String[] args) { //일체형이 아닌 조립형으로 변경 //스프링에게 주문하는 지시서로 변경해야 할 부분 /* Exam exam = new NewlecExam(); ExamConsole console=new GridConsole(); console.setExam(exam); */ ExamConsole console = 스프링에 의해 만들어진 콘솔 제(설정파일에 조립 주문서 작성) console.print(); } }
settting.xml 에 ExamConsole 주문서 작성
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework .org/schema/beans/spring-beans.xsd"> <!-- Exam exam = new NewlecExam(); --> <bean id="exam" class="spring.di.entity.NewlecExam"/> <!-- ExamConsole console=new GridConsole(); --> <bean id="console" class="spring.di.ui.GridConsole"> <!-- console.setExam(exam); --> <property name="exam" ref="exam"/> </bean> </beans>
property의 name은 주입할 필드명이다. 만약 필드가 참조타입이면 ref를 사용하고, primitive타입이면 value를 사용할 수 있다. (value 예제는 바로 밑에 있다)
name 필드에 ref= bean id를 통해 필드에 객체를 대입시킨다.
property를 사용할 때 필드에 해당하는 setter가 없으면 Spring이 주입해줄 수 있는 방법이 없어 에러가 난다.
작성된 주문서를 읽는놈 ApplicationContext 인터페이스 구현 클래스
여러 클래스중 ClassPathXmlApplicationContext 가 일반적
(Maven Project로 변경)
Java Project명이였던 Spring을 우클릭 -> Configure -> Convert to Maven Project
pom.xml 파일(Maven빌드 설정이 들어갈) 구성에 대한 창이 나옴
Maven 빌드 설정 = 어떤 것을 끌어올 것인지 등등
finish 선택
뭔가가 추가된 모습
끌어올 것을 입력하기 위해 아래의 탭 클릭
springframework 를 입력해도 ok가 안되는데 이클립스에 인덱싱이 안되있어서 그렇다고 한다.
Window -> Show View -> Other 선택
뜨는 창에서 Maven -> Maven repositories 선택
추가된 탭에서 찾아서 Rebuild Index를 진행하면 되는데~ 인덱싱이 처음하는 것이라서 한시간 정도 걸린다고 한다.
웹에서 직접 추가하러 들어가보자
강의에서 선택된 5.1.9 버전 클릭
나와있는 xml 구문 복사
빌드 설정인 pom.xml 의 디펜던시 추가하는 태그는 <dependencies>
끌어올 하나의 디펜던시 태그는 <dependency>
ctrl + shift + s 로 저장하면 자동으로 라이브러리를 다운받는다.
위에서 하나의 <dependency>를 추가하였지만 그 하나가 다른 것들을 필요로 하기 때문에 연관된 것들이 모두 딸려서 다운받아 진다.
이제 라이브러리를 사용할 수 있으므로 마저 강의를 들으면
setting.xml 의 주문서 대로 조립된 것이 IoC 컨테이너에 담겨있다.
IoC 컨테이너에 담겨있는 부품을 <bean> 태그에 설정되어 있던 id를 통해 사용한다.
컨텍스트의 getBean 메서드를 통해 컨테이너에 존재하는 것을 가져올 수 있는데
이름보다는 클래스명으로 가져오는 것이 가독성에 좋고 캐스팅이 필요없다.
package spring.di; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import spring.di.ui.ExamConsole; public class Program { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring/di/setting.xml"); // ExamConsole console= (ExamConsole)context.getBean("console"); // id를 통해 얻으면 Object형으로 리턴하므로 캐스팅 필요 // 비선호 ExamConsole console = context.getBean(ExamConsole.class); //클래스,인터페이스에 해당하는 놈을 조립해서 준다. //만약 모호하다면 setting.xml에 정확히 기술해야 하겠다. console.print(); } }
주문서의 내용인 setting.xml 의 요구사항을 GridConsole에서 InlineConsole로 바꾼뒤 프로그램을 실행하면
코드 수정없이 결과가 다르게 된다.
태그가 이해가 정확히 되지 않아 스스로 따라해본것들
C클래스의 필드에 A, B타입이 존재
package study; public class A { }
<bean id="A인스턴스" class="study.A"/>
package study; public class B { }
<bean id="B인스턴스" class="study.B" />
package study; public class C { private A a; private B b; public A getA() { return a; } public void setA(A a) { this.a = a; } public B getB() { return b; } public void setB(B b) { this.b = b; } }
<bean id="C인스턴스" class="study.C" />
여기까지만 하면 C인스턴스를 가져올 때 필드 a,b는 기본값인 null로 된다.
따라서 C의 a,b필드에 객체를 주입해야 한다.
<bean id="C인스턴스" class="study.C"> <!-- name="C클래스 내부의 의존성 주입할 변수명" ref="빈의 아이디" --> <property name="a" ref="A인스턴스" /> <property name="b" ref="B인스턴스" /> </bean>
property 는 setter 메서드로 필드에 의존성을 주입한다.
set필드명 메서드가 정의되어 있지 않으면 에러가 난다.
name은 C클래스의 필드변수명(의존성 주입할 필드변수명)을 입력한다.
ref는 빈을 참조할 수 있는 id(필드에 주입시킬 객체)를 입력한다.
테스트 코드
package spring.di; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import study.A; import study.C; public class Program { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("study/settingstudy.xml"); System.out.println("A 인스턴스: " + context.getBean(A.class)); System.out.println("C 인스턴스: " + context.getBean(C.class)); System.out.println("C 인스턴스의 필드 a: " + context.getBean(C.class).getA()); System.out.println("C 인스턴스의 필드 b: " + context.getBean(C.class).getB()); } }
값 형식 DI
https://www.youtube.com/watch?v=9iNvs7aeeDM&list=PLq8wAnVUcTFUHYMzoV2RoFoY2HDTKru3T&index=9
위에서 따로 연습했던 과정에서 C클래스 내부에 참조타입 필드 a,b 가 포함되어 있었다.
A클래스 내부에 x,y,z 는 int 형이므로 ref 대신 value를 통해 주입할 수 있다.
setter에 해당하는 메서드는 필수로 존재하지 않으면 에러가 난다.
package study; public class A { private int x; private int y; private int z; public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public int getZ() { return z; } public void setZ(int z) { this.z = z; } }
<bean id="A인스턴스" class="study.A"> <property name="x" value="100" /> <property name="y" value="200" /> <property name="z" value="300" /> </bean>
package spring.di; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import study.A; public class Program { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("study/settingstudy.xml"); System.out.println("A 인스턴스의 x: " + context.getBean(A.class).getX()); System.out.println("A 인스턴스의 y: " + context.getBean(A.class).getY()); System.out.println("A 인스턴스의 z: " + context.getBean(A.class).getZ()); } }
value태그를 property태그의 내부로 넣어도 동일하다.
<bean id="A인스턴스" class="study.A"> <property name="x"> <value>100</value> </property> <property name="y"> <value>200</value> </property> <property name="z"> <value>300</value> </property> </bean>
강의로 넘어가서 시험의 각 과목 필드의 값을 주입해보자
<bean id="exam" class="spring.di.entity.NewlecExam"> <property name="kor" value="10" /> <property name="eng" value="20" /> <property name="math" value="30" /> <property name="com" value="40" /> </bean>
또는(결과는 동일)
property를 일일이 기재할 필요없이 네임스페이스를 추가하고 p태그를 사용할 수 있다.
setter메서드를 주석처리한 결과 에러가 난것으로 보아 setter주입 같다??
생성자를 통한 DI
선언된 순서대로 값이 들어간다.
생성자의 어떤 파라메터가 어느 값이 들어갔는지 헷갈리기 쉬워 버그를 일으킬 가능성이 다소 존재
-> index를 사용할 수 있다. 생성자의 첫번째 파라메터는 index 0부터 시작
public class Program { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring/di/setting.xml"); Exam exam= context.getBean(NewlecExam.class); System.out.println(exam); } }
명시적으로 생성자의 파라메터명을 설정할 수도 있다.
생성자 오버로딩중 파라메터 개수가 같지만 각 파라메터의 타입이 다를 수 있는 경우 명시적으로 Spring에게 알려줘야 할 수도 있다. -> 타입도 지정할 수 있는 옵션이 있다.
강의에서 getBean(클래스.class)가 아닌 getBean(인터페이스.class)는 결과가 어떻게 될 까 궁금했었다.
조회된 빈이 여러개라 선택할 수 없다고 나온다.
getBean에 인터페이스로 가져올 때 등록된 객체중 어느것을 가져올지 모르는 경우인 것 같다.
강의를 더 들으면서 안나오면 찾아봐야겠다.