스프링 핵심 요소

5 분 소요

스프링 핵심 요소(Ioc, DI, AOP)

Inversion of Controll(IoC)

  • 객체 생성 및 의존 관계를 클래스 호출 방식이 아닌 외부 설정으로 관리
  • 프로그램의 제어 흐름 구조가 뒤바뀌어 작업을 수행하는 쪽에서 객체를 생성

클래스 호출 방식

  • 클래스 내에 선언과 구현이 결합성이 높기 때문에 다양한 형태로 변화가 불가능
      public class Car{
          private Driver driver = new Driver1();
          //private Driver driver = new Driver2();
    
          public void moveCar(){
              driver.drive();
          }
      }
    

IoC 방식

  • 팩토리 패턴의 장점을 더하여 결합성이 낮아진다. 실행 시점에 클래스간의 관계가 형성이 된다.
      <bean id="car" class="com.sample.Car">
          <property name="driver" ref="driver1">
      </bean>
    
      public class Car{
          private Driver driver;
    
          public void setDriver(Driver driver){
              this.driver = driver;
          }
          public void moveCar(){
              driver.drive();
          }
      }
    

IoC의 장점

  • 인터페이스 기반 설계가 가능
  • 컴퍼넌트 재사용성 증가
  • 체계적이고 효율적인 Dependency 관리

IoC 구현

Dependency Injection(DI)

  • 클래스 사이에 필요한 의존관계를 빈 설정 정보를 바탕으로 컨테이너가 자동으로 연결해주는 것

DI 방법

  • 생성자 방식 Constructor Injection
    • 태그 사용
    • 의존 객체가 많아지면 사용하기 불편
  • 수정자 방식 Setter Injection
    • 태그 이용
    • 생성자 주입보다 유연하게 설정이 가능

IoC Container

  • POJO이 생성, 초기화, 서비스 소멸에 관한 모든 권한을 가지면서 POJO이 생명주기를 관리
  • 개발자가 직접 인스턴스를 관리하는 방식이 아닌 컨테이너로의 위임을 통해 제공하는 것이 IoC 패턴
  • IoC Container의 장점
    • Transaction 처리, 인스턴스 Pooling 기능, Security와 같은 복잡한 기능들을 컨테이너를 이용함으로서 해결
    • Test의 용이성(품질 향상), 개발 생산성 향상

Bean 기본 설정

  • Bean이란 IoC Container에 의해 관리되는 객체를 말함
  • 태그를 사용하여 Bean의 생성 및 의존 관계를 설정

Bean 주입

Annotation을 통한 Bean 정의 및 주입

  • 주입할 Bean 클래스 생성
     @Service(name = "employeeservice")
     public class EmployeeServiceImpl implements EmployeeService}
     //생략
     }
    
  • 주입 받을 Bean 클래스 생성
      @Controller
      public class EmployeeController{
          @Resource(name="employeeService")
          private EmployeeService employeeService;
          //생략
      }
    
  • Bean 설정(context-common.xml)
    • scan 대상 패키지와 유형을 정의하며, 개별 bean에 대한 설정은 필요없음
    • component-scan: Annocation을 기반으로 Bean을 Container에 등록하고 Bean간의 주입을 수행함
        <context:component-scan base-package="cevonframe.sample">
        <context:include-filter type="annotation expression="org.springframework.streotype.service">
        <context:include-filter type="annotaion" expression = "org.springframework.stereotype.Controller"/>
        </context:component-scan>
      

xml을 통한 Bean 설정과 Annotation을 통한 주입

  • 주입할 Bean 클래스를 생성
      public class EmployeeServiceImpl implements EmployeeService{
          //생략
      }
    
  • 주입 받을 Bean 클래스 생성
      public class EmployeeController{
          @Resource(name="employeeService")
          private EmployeeService employeeService;
          //생략
      }
    
  • Bean 설정(context-common.xml)
      <context:annotation-config/>
      <bean id="employeeService" class="springframework.sample.hr.employee.service.EmployeeServiceImpl"/>
      <bean id="employeeController" class="springframework.sample.hr.employee.controller.EmployeeController"/>
    

    xml을 통한 Bean 설정과 setter를 통한 주입

  • 주입할 Bean 클래스 생성
      public class EmployeeServiceImpl implements EmployeeService{
          //이하 생략
      }
    
  • 주입 받을 Bean 클래스 생성
      public class EmployeeController{
          private EmployeeService employeeService;
    
          public void setEmployeeService(EmployeeService employeeService){
              this.employeeService = employeeService;
          }
      }
    
  • Bean 설정(context-common.xml)
      <bean id="employeeService" class="springframe.sample.hr.employee.service.EmployeeserviceImpl"/>
      <bean id="employeeController" class="springframe.sample.hr.employee.controller.EmployeeController">
          <property name="employeeService" ref="employeeService"/>
      </bean>
    

Bean Lookup

Container를 통한 Bean 직접 접근

  • 주입할 Bean 클래스 생성
      public class EmployeeServiceImpl implements EmployeeSErvice{
          //생략
      }
    
  • Bean 설정(context-common.xml)
      <bean id="employeeService" class="springrame.sample.hr.employee.service.imployeeServiceImpl">
    

GetBean() 메소드를 통한 Bean 접근

  • //Container 생성
    ApplicationContext context = new ClassPathXmlApplicationContext("context-common.xml")
    //GetBean() 메소드를 통해 Bean 주입
    EmployeeService service = (EmployeeService)context.getBean("employeeService");
    

Bean의 초기화 및 소멸 처리

  • 인터페이스 기반 방식 - Spring에서 제공하는 InitializingBean 및 DisposalBean interface를 구현한다.
      public class sampleBean implements InitializingBean, DisposalBean{
          public void afterPropertiesSEt() throws Exception{
              //bean 설정 setting 후 수행할 초기화 작업 기술
          }
          public void destroy() throws Exception{
              //bean 소멸시 수행할 종료 작업 기술
          }
      }
    
  • 메소드 기반 방식 - Spring의 의존을 줄일 수 있으며, bean 설정 파일 내에 초기/소멸시 수행할 메소드를 기술한다.
      public class SampleBean{
          public void init() throws Exception{
              //bean설정 setting 후 수행할 초기화 작업 기술
          }
          public void cleanup() throws Exception{
              //bean 소멸시 수행할 종료 작업 기술
          }
      }
    

Aspect Oriented Programming(AOP)

  • 다수의 객체들에 분산되어 중복적으로 존재하는 공통 관심사를 분리하는 프로그래밍 기법
  • AOP는 로깅, 보안, 트랜잭션 등 공통 기능에 대해 비즈니스 로직에 영향 없이 모듈화 처리

AOP 장점

  • 중복 코드의 제거
    • 공통 관심이 여러 모듈에 반복적으로 기술되는 현상 방지
  • 비즈니스 로직의 가독성 향상
    • 핵심 기능 코드로부터 공통 관심 코드를 분리함으로서 비즈니스 로직의 가독성 향상
  • 생산성 향상
    • 비즈니스 로직의 독립으로 인한 개발의 집중력을 높임
  • 재사용성 향상
    • 공통 관심 코드는 여러 모듈에서 재사용될 수 있음
  • 변경 용이성 증대
    • 공통 관심 코드가 하나의 모듈로 관리되기 때문에 이에 대한 변경이 수월해짐

AOP 구성 요소

  • Advice: 각 JoinPoint에 삼입되어 동작할 수 있는 코드(동작시점: before, after, after returing, after, throwing, around 중에서 선택)
  • Join Point: 모듈이 삽이보디어 동작할 수 있는 특정 위치(메소드 호출, 메소드 실행 자체, 클래스 초기화, 객체 생성 시점 등)
  • Pointcut: 어느 JoinPoint를 사용할 것인지를 결정하는 선택 기능(Pattern Matching Examples, Pointcut Designators)
  • Aspect: 어디에서(Pointcut) 무엇을 할 것인지(Advice)를 합쳐놓은 수행할 작업의 집합
  • Weaving: Pointcut에 의해서 결정된 JoinPoint에 지정된 Advice를 삽입하는 과정 기존의 Core Concerns 모듈의 코드에 영향을 주지 않으면서 필요한 Crosscutting Concerns 기능을 추가하는 과정

AOP 적용 전/후 예

  • 적용 전: 비즈니스 코드와 Transaction 관리 코드가 섞여 있음
      public void insertAccount(AccountVO accountVO){
          TransactionManager tm = TransactionManagerFactory.createTransactionManger("jdbc");
          try{
              tm.begin();
              commonDao.insert("Account.insertAccount", accountVo);
              tm.commit();
          }catch(Exception e){
              tm.rollback();
          }
      }
    
  • 적용 후: 비즈니스 코드와 Transaction 관리 코드 분리
      public void insertAccount(AccountVO accountVO){
          commonDao.insert("Account.insertAccount", acountVO);
      }
    
      <bean id="txManager" class="org.springframework.DataSourceTransactionManager">
          <property name="dataSource", ref="dataSource"/>
      </bean>
      <aop:config>
          <aop:pointcut id="requiredTx" expression="execution(* *..*Service.insert(..))"/>
          <aop:adviser advice-ref="txAdvice" pointcut-ref="requiredTx"/>
      <aop:config>
      <tx:advice id="txAdvice" transaction-manager="txManager">
          <tx:attributes>
              <tx:method name="retrieve*" read-only="true"/>
              <tx:method name="insert*" rollback-for="Exception"/>
          </tx:attributes>
      </tx:advice>
    

Advice 구현

  • Before: 조인포인트(메소드 호출) 전에 수행
  • After Returning: 조인포인트가 성공적으로 리턴 된 후에 동작
  • After Throwing: Exception이 발생하여 조인포인트가 빠져나갈 때 수행
  • After(Finally): 조인포인트를 빠져나가는 (정상적이거나 예외적인 반환) 방법에 상관없이 수행
  • Around: 조인포인트 전후에 수행

  • Advice 구현 예
      public class AdviceUsingXML{
          //Service method 실행 전에 필요한 처리 작성
          public void before TargetMethod(JoinPoint thisJoinPoint){}
          //Service method 실행 후에 필요한 처리 작성
          Public void afterTargetMethod(JoinPoint thisJoinPoint){}
          //Service method 실행 후 리턴된 결과값으로 필요한 처리 작성
          public void afterReturningTargetMethod(JoinPoint thisJoinPoint, Object recVal){}
          //Service method 실행 시 Exception이 발생한 경우 필요한 처리 작성
          public void afterThrowing TargetMethod(JoinPoint thisJoinPoint, Exception exception) throws Exception{}	
          //Service Method 실행 전/후에 필요한 처리 작성
          public Object aroundTargetMethod(ProceedingJoinPoint thisJoinPoint) throws Throwable{
              //메소드 실행 전의 처리 내용
              Object retVal = thisJoinPoint.proceed();
              //메소드 실행 후의 처리 내용
              return retVal;
          }
    

Spring 내부 동작

  • @Service("bookService")
    public class BookServiceImpl{
        public List retrieveBookList(String title){
            //pointcut execution(* com.sample.aop.. *Impl.*(..))
            return bookDao.retrieveBookList(title);
        }
    }
    
  • AOP 적용
      Public class AutoGeneratedBookServiceProxy{ //스프링은 내부적으로 아래와 같은 처리흐름을 가지는 프록시를 생성
          private AdviceUsingXML advice;  // 앞에서 설정된 Advice Bean이 주입됨
          private BookServiceImpl target;   // 위에서 구현된 Proxy 대상이 되는 업무 service Bean
    
          public List<?> retrieveBookList(String title) throws Exception { // 실제 Service와 동일한 signature임
              advice.beforeTargetMEthod(joinPoint);
              advice.aroundTargetMethod(joinPoint);
              try{
                  List<?> retVal = target.retrievBookList(title);
                  advice.afterRunning TargetMethod(joinPoint, retVal);
                  return retVal;
              }catch(Exception e){
                  advice.afterThrowing TargetMethod(joinPoint,e);
                  throw e;
              }finally{
                  advice.afterTargetMethod(joinPoint);
              }
              advice.aroundTargetMethod(joinPoint);
          }
    

Apache Log4J2

  • 구조: Logger와 Appender를 각각 등록하고 Logger 별로 필요한 Appender를 주입함으로서 하나의 시스템에서 다양한 방식의 로그를 기록함
    • Logger: 로그의 주체(로그 파일을 작성하는 클래스)
    • Appender: 로그 출력 대상 및 방법 지원
    • Layout: Appender의 출력 포맷
  • 로그 레벨
    • 설정된 로그 Level 이상의 메시지만 기록됨
    • TRACE > DEBUG > INFO > WARN > ERROR > FATAL

태그:

카테고리:

업데이트: