Framework : 애플리케이션을 구현하고 관리하는 환경(틀, 약속)
Spring 3.XX ~ 5.XX
스프링 프레임워크는 자바 플랫폼을 위한 오픈 소스 애플리케이션 프레임워크이다.
전자 정부 표준 프레임워크로 채택 > 전자정부사이트
세팅된 개발 환경 제공 + 생산성 + 개발 편의성 + 유지/보수성 + 현업 > 대형 프로젝트
의존성 주입 지원(DI)
관점 지향 프로그래밍 지원(AOP)
Spring MVC 지원
레이아웃 지원(Tiles)
데이터베이스 연동 지원

src/main/java
src/main/resources
src/test/java
src/test/resources
JRE System Library
Maven Dependencies
src
7.1 webapp *
7.2 webapp > resources
7.3 webapp > WEB-INF ⭐
7.3.1 classes
- 컴파일된 클래스 파일 저장폴더
- 개발자 관여(X)
7.3.2 spring
- 이 폴더 이하에서 모든 스프링 설정을 한다. ⭐
7.3.2.1 appServlet
7.3.2.1.1 servlet-context.xml
- 웹 요청과 관련된 환경 설정
7.3.2.2 root-context.xml
- 스프링 전체 환경 설정
7.3.3 views
- 뷰 페이지 폴더
7.3.4 web.xml
- 아파치 톰캣 서버 설정
Dependency Injection
의존(성) 주입 : 어떤 객체가 사용하는 의존 객체를 직접 만들어 사용하는게 아니라, 주입 받아서 사용하는 방법
객체를 관리하는 방법
스프링에서 아주 중요한 개념 중 하나
스프링 전반에 걸쳐서 DI가 적용되어 있음
DI : 잘 알려진 디자인 패턴 중 하나 > 스프링에서 도입 + 구현 + 적용 > Spring DI
프로그래밍에서 구성 요소간의 의존 관계가 소스 내부가 아닌 외부 환경에 의해서 정의되게 하는 디자인 패턴
IoC ( Inversion of Control ) 디자인 패턴 : 역제어 > 객체를 생성하는 순서가 뒤집힌다.
🦾 DI/IoC 개념 실습1
- "com.test.spring.di01" 패키지 생성
- com.test.spring.di01 > "Ex01.java"
> "Pen.java"
> "Brush.java"
> "Hong.java"
> "Lee.java"
1) Ex01.java
Ex01과 Pen의 관계
- Ex01이 Pen을 의존(사용)한다.
- Pen을 Ex01의 의존객체라고 부른다. 즉, 주입되는 객체 (Pen : 의존객체)
- Ex01이 자신의 업무를 구현하기 위해서 Pen을 사용했다.
- Pen이 없으면 Ex01은 업무를 진행할 수 없다.
Ex01과 Brush의 관계
- Brush는 의존객체
Ex01과 Hong의 관계
- Ex01 > Hong > Pen
- Ex01 > (의존관계) > Hong 의존객체 > (의존관계) > Pen 의존객체
package com.test.spring.di01;
public class Ex01 {
public static void main(String[] args) {
Pen pen = new Pen();
pen.write(); //펜으로 글씨를 적습니다.
Brush brush = new Brush();
brush.draw(); //붓으로 그림을 그립니다.
Hong hong = new Hong();
hong.run(); //펜으로 글씨를 적습니다.
//****
Brush b = new Brush();
Lee lee = new Lee(b); //의존(객체) 주입(DI)
lee.run(); //붓으로 그림을 그립니다.
}
}
2) Pen.java
package com.test.spring.di01;
public class Pen {
public void write() {
System.out.println("펜으로 글씨를 적습니다.");
}
}
3) Brush.java
package com.test.spring.di01;
public class Brush {
public void draw() {
System.out.println("붓으로 그림을 그립니다.");
}
}
4) Hong.java
package com.test.spring.di01;
public class Hong {
public void run() {
Pen pen = new Pen();
pen.write();
}
}
5) Lee.java ⭐
package com.test.spring.di01;
public class Lee {
//의존객체
private Brush brush;
//DI 구현 ⭐
//1. 생성자
//2. Setter
//1.다른 사람(외부)이 만들어서 생성 > 생성자 > 의존 주입 도구
public Lee(Brush brush) {
this.brush = brush;
}
//2.Setter > 의존 주입 도구
public void setBrush(Brush brush) {
this.brush = brush;
}
public void run() {
//의존 객체 생성 X
//Brush brush = new Brush(); //직접 생성
brush.draw();
}
}
🦾 xml설정 실습2
- "com.test.spring.di02" 패키지 생성
- com.test.spring.di02 > "Ex02.java"
> Pen.java
> Brush.java
> "Hong.java"
> "Lee.java"
> "di02.xml"
1) Ex02.java
package com.test.spring.di02;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Ex02 {
public static void main(String[] args) {
//m1();
m2();
}
private static void m2() {
//Ex02 > Hong > Pen
//Ex02 > Lee > Brush
ApplicationContext context = new ClassPathXmlApplicationContext("com/test/spring/di02/di02.xml");
Hong hong = (Hong)context.getBean("hong");
hong.run(); //펜으로 글씨를 적습니다.
Lee lee = (Lee)context.getBean("lee");
lee.run(); //붓으로 그림을 그립니다.
}
private static void m1() {
//스프링 환경 > 객체 생성 + 소멸 > 스프링 관리
// > 관리 XML 필요
//Pen 객체 생성하기
Pen p1 = new Pen();
p1.write();
//XML 설정 읽기 > 인식 > 호출
ApplicationContext context = new ClassPathXmlApplicationContext("com/test/spring/di02/di02.xml");
//getBean() > 식별자를 검색 (id) > 해당 클래스의 인스턴스 생성(new Pen()) + 반환
Pen p2 = (Pen) context.getBean("pen"); //id
p2.write();
Brush b1 = (Brush) context.getBean("brush"); //id
b1.draw();
Pen p3 = (Pen) context.getBean("p1"); //name
p3.write();
Pen p4 = (Pen) context.getBean("p2"); //name
p4.write();
Pen p5 = (Pen) context.getBean("p3"); //name
p5.write();
}
}
2) dio2.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- m1() -->
<!-- 하나의 객체가 어디에서 쓰이느냐? > 역할 달라질 수 있다 > 식별자 여러개 제공 > 가독성 -->
<bean id="pen" name="p1 p2 p3" class="com.test.spring.di02.Pen"></bean>
<bean id="brush" class="com.test.spring.di02.Brush"></bean>
<!-- <bean id="pen" name="필기도구 메모도구 연필 볼펜" class="com.test.spring.di02.Pen"></bean> -->
<!-- m2() -->
<bean id="hong" class="com.test.spring.di02.Hong">
<!-- Spring DI -->
<!-- 생성자 인자 -->
<constructor-arg ref="pen"></constructor-arg>
</bean>
<bean id="lee" class="com.test.spring.di02.Lee">
<!-- Spring DI -->
<!-- Setter -->
<property name ="brush" ref="brush" ></property>
</bean>
</beans>
3) Hong.java
package com.test.spring.di02;
public class Hong {
//의존객체
private Pen pen;
public Hong(Pen pen) {
this.pen = pen;
}
public void run() {
pen.write();
}
}
4) Lee.java
package com.test.spring.di02;
public class Lee {
//의존객체
private Brush brush;
public void setBrush(Brush brush) {
this.brush = brush;
}
public void run() {
brush.draw();
}
}
어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화하겠다는 것주업무와 보조업무를 분리시켜 각각의 업무를 관리하는 기술
Core Concern
Cross-cutting Concern
Advice
< 주업무가 실행되는 어느 시점에 보조 업무를 실행할지? >
🦾 AOP 실습
- "com.test.spring.aop" 패키지 생성
- > "Main.java" //메인 클래스
> "Memo.java" 인터페이스 //메인 업무
> "MemoImpl.java" //메인업무
> "Logger.java" //보조업무
> "memo.xml" //스프링 설정
1) Main.java
package com.test.spring.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
//메모 쓰기
ApplicationContext context = new ClassPathXmlApplicationContext("com/test/spring/aop/memo.xml");
Memo memo = (Memo)context.getBean("memo");
memo.add("스프링 AOP 작업을 하고 있습니다.");
//메모 읽기
try {
String txt = memo.read(5);
txt = memo.read(15);
} catch (Exception e) {
//e.printStackTrace();
}
//메모 수정하기
boolean result = memo.edit(5, "수정한 내용입니다.");
//메모 삭제하기
result = memo.del(5);
}
}
2) Memo.java
package com.test.spring.aop;
public interface Memo {
//메모 쓰기
void add(String memo);
//메모 읽기
String read(int seq) throws Exception ;
//메모 수정하기
boolean edit(int seq, String memo);
//메모 삭제하기
boolean del(int seq);
}
3) MemoImpl.java
package com.test.spring.aop;
public class MemoImpl implements Memo {
@Override
public void add(String memo) {
System.out.println("메모 쓰기: " + memo);
}
@Override
public String read(int seq) throws Exception{
if (seq >= 1 && seq <= 10) {
System.out.println("메모 읽기: " + seq);
} else {
throw new Exception("존재하지 않는 메모");
}
return "메모입니다.";
}
@Override
public boolean edit(int seq, String memo) {
System.out.println("메모 수정하기: " + memo);
return false;
}
@Override
public boolean del(int seq) {
System.out.println("메모 삭제하기: " + seq);
return false;
}
}
4) Logger.java
package com.test.spring.aop;
import java.util.Calendar;
import org.aspectj.lang.ProceedingJoinPoint;
public class Logger {
public void log() {
Calendar now = Calendar.getInstance();
System.out.printf("[LOG][%tF %tT] 로그를 기록합니다.\n", now, now);
}
public void time(ProceedingJoinPoint jp) {
long begin = System.nanoTime();
System.out.println("[LOG] 기록을 시작합니다.");
//주 업무
try {
jp.proceed(); //지금 실행하는 주업무의 대리자
} catch (Throwable e) {
e.printStackTrace();
}
long end = System.nanoTime();
System.out.println("[LOG] 기록을 종료합니다.");
System.out.printf("[LOG] 소요 시간 %,dns\n", end - begin);
}
public void histroy(Object memo) {
System.out.println("[LOG] 읽기 기록: " + memo);
}
public void check(Exception e) {
System.out.println("[LOG] 예외 발생 " + e.getMessage());
}
}
5) memo.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
<!-- 주 업무 객체 -->
<bean id="memo" class="com.test.spring.aop.MemoImpl" />
<!-- 보조 업무 객체 -->
<bean id="logger" class="com.test.spring.aop.Logger" />
<!-- 주 업무 객체 + 보조 업무 객체 -->
<!-- Spring AOP 설정 -->
<aop:config>
<!-- 보조 업무를 담당할 객체를 지정 : 역할(Role) -->
<aop:aspect id="loggerAdvice" ref="logger">
<!-- 주 업무 객체를 지정 > 메소드 찾기 > 포인트컷 지정하기 -->
<!-- Point Cut : 주 업무 객체의 특정 메소드 -->
<!-- Point Cut 지정 방법 > Aspectj 표현식을 사용한다. -->
<!-- 포인트컷 지정(= 주 업무 지정) -->
<!--
Aspectj 표현식
- execution([접근지정자] 반환형 [클래스].메소드(인자)
- execution(public void com.test.spring.aop.MemoImpl.add(String))
- 접근 지정자 > 생략 가능(public만 가능)
- 반환형 > 생략 X
- 클래스 > 반드시 전체 경로 표현. 생략 가능
- 메소드 > 메소드명. 생략 X
- 인자 > 인자리스트. 생략 X
- 와일드 카드
1. * : 반환형, 패키지, 클래스, 메소드명
2. .. : 인자 리스트
tip_ 공통점을 찾아라!
-->
<!-- <aop:pointcut expression="execution(public void com.test.spring.aop.MemoImpl.add(String))" id="p1"/> -->
<aop:pointcut expression="execution(public String com.test.spring.aop.MemoImpl.read(int))" id="p1"/>
<aop:pointcut expression="execution(public boolean com.test.spring.aop.MemoImpl.edit(int,String))" id="p2"/>
<aop:pointcut expression="execution(public * com.test.spring.aop.MemoImpl.*(..))" id="p3"/>
<aop:pointcut expression="execution(public * com.test.spring.aop.MemoImpl.*(int))" id="p4"/>
<aop:pointcut expression="execution(public boolean com.test.spring.aop.MemoImpl.*(..))" id="p5"/>
<!-- 생략 가능... but 권장 X -->
<aop:pointcut expression="execution(* *(..))" id="p6"/>
<!-- 첫번째는 무조건 int, 2번째는 알아서해라 -->
<aop:pointcut expression="execution(public * com.test.spring.aop.MemoImpl.*(int,..))" id="p7"/>
<!-- 메소드가 add단어로 시작하는거 -->
<aop:pointcut expression="execution(public * com.test.spring.aop.MemoImpl.add*(..))" id="p8"/>
<aop:pointcut expression="execution(public * com.test.spring.aop.MemoImpl.*a*(..))" id="p9"/>
<aop:pointcut expression="execution(public * com.test.spring.aop.MemoImpl.add(..))" id="p10"/>
<aop:pointcut expression="execution(public String com.test.spring.aop.MemoImpl.read(int))" id="p11"/>
<!-- 위빙(Weaving) > 포인트컷과 보조업무 결합 x 5가지 방법 -->
<!--
After Advice
- 주 업무 실행 > 보조 업무 실행
-->
<!--
<aop:after method="log" pointcut-ref="p1"/>
<aop:after method="log" pointcut-ref="p2"/>
-->
<!-- <aop:after method="log" pointcut-ref="p9"/> -->
<!-- <aop:before method="log" pointcut-ref="p9"/> -->
<!-- <aop:around method="time" pointcut-ref="p3"/> -->
<!-- 주 업무를 실행한 후에 `주 업무의 반환값을 사용`하는 보조업무를 실행 -->
<!-- <aop:after-returning method="histroy" pointcut-ref="p11" returning="memo"/> -->
<!-- 주 업무를 실행 중 `예외가 발생`했을 때 보조업무를 실행 -->
<aop:after-throwing method="check" pointcut-ref="p11" throwing="e"/>
</aop:aspect>
</aop:config>
</beans>