[Spring] 스프링 프레임워크(Spring Framework), 스프링 DI, 스프링 AOP

suyeon·2022년 7월 27일

Spring

목록 보기
1/6
post-thumbnail

⚡ 스프링 프레임워크( Spring Framework)

  • 스프링공식사이트

  • Framework : 애플리케이션을 구현하고 관리하는 환경(틀, 약속)

  • Spring 3.XX ~ 5.XX

  • 스프링 프레임워크는 자바 플랫폼을 위한 오픈 소스 애플리케이션 프레임워크이다.

  • 전자 정부 표준 프레임워크로 채택 > 전자정부사이트

  • 세팅된 개발 환경 제공 + 생산성 + 개발 편의성 + 유지/보수성 + 현업 > 대형 프로젝트

[ 구성 요소 ]

  1. 의존성 주입 지원(DI)

  2. 관점 지향 프로그래밍 지원(AOP)

  3. Spring MVC 지원

  4. 레이아웃 지원(Tiles)

  5. 데이터베이스 연동 지원

    • JDBC
    • Spring JDBC
    • ORM(JPA)
    • MyBatis

⚡ 스프링 프로젝트 구조

  1. src/main/java

    • 여태까지 사용하는 Java 소스 폴더
  2. src/main/resources

    • Java 소스 이외의 자원 폴더
    • 설정
  3. src/test/java

    • 테스트용 폴더
  4. src/test/resources

    • 테스트용 폴더
  5. JRE System Library

    • JRE jar
  6. Maven Dependencies

    • 메이븐(빌드 도구 + 라이브러리 관리 도구 등)
    • 프로젝트 생성 & 유지 관리 & 배포를 도와주는 프로그램
    • 수업용 > jar 파일들 관리 용도로 사용
    • Spring = Maven or Gradle
  7. src

    • 1~4 까지의 물리적 표현
    • 소스의 루트 폴더

    7.1 webapp *

    • 웹 루트 폴더

    7.2 webapp > resources

    • CSS, JavaScript, Images 등을 저장하는 폴더

    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
    	- 아파치 톰캣 서버 설정

⚡ 스프링 DI (Spring DI) ⭐

  • 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 의존객체


  • Ex01과 Lee의 관계
    - 의존 주입
    - Ex01 > Lee > Brush
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"
  • Pen, Brush 파일 실습1 동일함

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)
  • 자바 객체 생성 + 설정 > 자바 객체 = Bean
<?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

  • Ex02 > Hong > Pen
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

  • Ex02 > Lee > Brush
package com.test.spring.di02;

public class Lee {
	
	//의존객체
	private Brush brush;
	
	public void setBrush(Brush brush) {
		this.brush = brush;
	}
	
	public void run() {
		brush.draw();
	}

}

⚡ 스프링 AOP (Spring Aspect Oriented Programming) ⭐

  • 관점 지향 프로그래밍
  • 관점 지향 : 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화하겠다는 것
  • 여기서 모듈화란 어떤 공통된 로직이나 기능을 하나의 단위로 묶는 것 
  • 주업무와 보조업무를 분리시켜 각각의 업무를 관리하는 기술
  • 생산성 + 유지 보수성 향상
  • [주요 관심사(Core Concern)]로부터 [횡단 관심사(Cross-cutting Concern)]의 [분리]를 허용함으로써 모듈성을 증가시키는 것이 목적

[ Spring AOP 구분 ]

  1. Core Concern

    • 주업무(핵심 기능)
    • ex) 예금, 대출, 송금, 신용카드 등
  2. Cross-cutting Concern

    • 보조업무(부가기능)
    • ex) 보안, 로깅, 트랜잭션 등
  3. Advice
    < 주업무가 실행되는 어느 시점에 보조 업무를 실행할지? >

    • Before Advice : 실행 전
    • After Advice : 실행 후
    • Around Advice : 실행 전&후

    • After-returning Advice : 주 업무가 성공적으로 리턴되면 동작(단, 메소드 내부에 리턴값이 존재하는 경우)
    • After-throwing Advice : 주 업무 실행 중, 예외가 발생하면 동작

🦾 AOP 실습

- "com.test.spring.aop" 패키지 생성
- 					   > "Main.java" 			//메인 클래스
					   > "Memo.java" 인터페이스   //메인 업무
					   > "MemoImpl.java"        //메인업무			
					   
					   > "Logger.java"			//보조업무
					   > "memo.xml"				//스프링 설정

1) Main.java

  • 메인 클래스
  • 컨트롤러 (주업무 통제하는 역할)
  • 기존의 주 업무 + 보조 업무 -> "AOP"를 통해서 통제한다. > 반드시 Memo객체를 스프링을 통해서 생성 해야 한다.
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 ⭐

  • Spring AOP 설정
<?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>

0개의 댓글