[spring] DI & IoC & AOP

sang·2024년 4월 8일

DI & IoC

DI

의존성 주입
개발자가 아닌 컨테이너가 컴포넌트(클래스) 연관 관계 규정
약한 결합(loosely coupled), 의존 관계 최소화
다른 클래스의 변경 사항에 영향을 받지 않음 -> 자유로운 클래스 변경 가능

코드 단순화
쉬운 유지 관리

IoC

제어 역행(역전)
개발자가 아닌 스프링에서 직접 객체를 제어
스프링에서는 DI로 IoC를 구현



적용 전

class Controller, class Service, class DAO

BoardService.java

public class BoardService {
  BoardDAO boardDAO;
  public BoardService() {
    boardDAO = new BoardDAO(); //직접 생성
  }
...

적용 후

interface Controller, interface Service, interface DAO,
class ControllerImpl implements Controller, 
class ServiceImpl implements Service, 
class DAOImpl implements DAO

기능 변경 시 인터페이스를 구현한 새로운 클래스를 작성하여 사용

BoardServiceImpl.java

public class BoardServiceImpl implements BoardService{
   private BoardDAO boardDAO;
   
   /* 생성자 이용 방식 */
   public BoardServiceImpl(BoardDAO boardDAO){
      this.boardDAO = boardDAO; //주입된 객체 사용
   }
  
   /* setter 이용 방식 */
   public void setBoardDAO(BoardDAO boardDAO){
     this.boardDAO = boardDAO; //주입된 객체 사용
   }
  
  ...


<bean> 태그

XML 파일에서 DI 설정 시 사용하는 태그

속성

id: bean 객체 고유 이름, 접근 이름
name: 객체 별칭
class: 패키지.생성할 클래스
constructor-arg: 생성자 방식
property: setter 방식

생성자 방식

pro19/person.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
  <bean id="personService1" class="com.spring.ex02.PersonServiceImpl">
    <constructor-arg value="이순신" />
  </bean>
  <bean id="personService2" class="com.spring.ex02.PersonServiceImpl">
    <constructor-arg value="손흥민" />
    <constructor-arg value="23" />
  </bean>
</beans>

pro19/com.spring/ex02/PersonServiceImpl.java

package com.spring.ex02;

public class PersonServiceImpl implements PersonService {
  private String name;
  private int age;
  
  public PersonServiceImpl(String name) {
    this.name = name;
  }
  
  public PersonServiceImpl(String name, int age) {
    this.name = name;
    this.age = age;
  }
  
  @Override
  public void sayHello() {
    System.out.println("이름: " + name);
    System.out.println("나이: " + age + "살");
  }
}

pro19/com.spring/ex02/PersonTest.java

package com.spring.ex02;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;

public class PersonTest {
  public static void main(String[] args) {
    BeanFactory factory = new XmlBeanFactory(new FileSystemResource("person.xml"));
    PersonService person1 = (PersonService) factory.getBean("personService1");
    person1.sayHello();
    
    System.out.println();

    PersonService person2 = (PersonService) factory.getBean("personService2");
    person2.sayHello();
  }
}

setter 방식

pro19/person.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
  <bean id="personService" class="com.spring.ex01.PersonServiceImpl">
    <property name="name">
        <value>홍길동</value>
    </property>
  </bean>
</beans>

pro19/com.spring/ex01/PersonService.java

package com.spring.ex01;

public interface PersonService {
    public void sayHello();
}

pro19/com.spring/ex01/PersonServiceImpl.java

package com.spring.ex01;

public class PersonServiceImpl implements PersonService {
  private String name;
  private int age;
 

  public void setName(String name) {
    this.name = name;
  }
  
  @Override
  public void sayHello() {
    System.out.println("이름: " + name);
    System.out.println("나이: " + age);
  }
}

pro19/com.spring/ex01/PersonTest.java

package com.spring.ex01;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;

public class PersonTest {
  public static void main(String[] args) {
    BeanFactory factory = new XmlBeanFactory(new FileSystemResource("person.xml")); //xml을 통해 bean 생성
    PersonService person = (PersonService) factory.getBean("personService"); //bean 가져오기
    
    person.sayHello();
  }
}


회원 관리

pro19/member.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
  <bean id="memberService" class="com.spring.ex03.MemberServiceImpl">
    <property name="memberDAO" ref="memberDAO" />
  </bean>
  <bean id="memberDAO" class="com.spring.ex03.MemberDAOImpl" />
</beans>

pro19/com/spring/ex03/MemberServiceImpl.java

package com.spring.ex03;

public class MemberServiceImpl implements MemberService {
  private MemberDAO memberDAO;
  
  public void setMemberDAO(MemberDAO memberDAO) {
    this.memberDAO = memberDAO;
  }
 
  @Override
  public void listMembers() {
    memberDAO.listMembers();
  }
}

pro19/com/spring/ex03/MemberDAOImpl.java

package com.spring.ex03;

public class MemberDAOImpl implements MemberDAO {
  @Override
  public void listMembers() {
    System.out.println("listMembers 메서드 호출");
    System.out.println("회원정보를 조회합니다.");
  }
}

pro19/com/spring/ex03/MemberTest1.java

package com.spring.ex03;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;

public class MemberTest01 {
  public static void main(String[] args) {
    BeanFactory factory = new XmlBeanFactory(new FileSystemResource("member.xml"));
    MemberService service = (MemberService) factory.getBean("memberService");
    service.listMembers();
  }
}


AOP

관점 지향 프로그래밍 Aspect Oriented Programming
메소드 안의 보조기능들을 분리하여 선택적으로 사용

  • ex)
    주기능: 회원관리, 상품관리, 주문관리
    보조기능(공통): 로깅, 보안, 트랜잭션

aspect: 보조기능
advice: aspect 구현체 (클래스)
advisor: advice+pointcut 결합
joinpoint: advice 적용 지점
pointcut: advice 적용 대상 정규식 (패키지명/클래스명/메소드명)
target: advice 적용 클래스
weaving: advice를 주기능에 적용

구현 방법

  • 스프링 프레임워크 제공 API 이용
  • @Aspect 애너테이션 이용


API를 이용한 AOP 구현

  1. target 클래스, advice 클래스 지정
  2. pointcut 설정, advisor 설정
  3. target + advice
  4. getBean() 객체 사용

advice 인터페이스

AOP 수행 시점

  • MethodBeforeAdvice
    void before(Method method, Object[] args, Object target) throws Throwable
    메소드 실행 전
  • AfterReturningAdvice
    void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable
    메소드 실행 후
  • ThrowsAdvice
    void afterThrowing(Method method, Object[] args, Object target, Exception ex)
    예외 발생 시
  • MethodInterceptor
    Object invoke(MethodInvocation invocation) throws Throwable
    메소드 실행 전/후 및 예외발생 시

pro20/src/AOPTest.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
  <bean id="calcTarget" class="com.spring.ex01.Calculator" /> <!-- target -->
  <bean id="logAdvice" class="com.spring.ex01.LoggingAdvice" /> <!-- advice -->
  <bean id="proxyCal" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- target + advice -->
    <property name="target" ref="calcTarget"/>
    <property name="interceptorNames">
      <list>
        <value>logAdvice</value> <!-- 메소드 호출 시 실행 -->
      </list>
    </property>
  </bean>
</beans>

pro20/src/com.spring/ex01/Calculator.java

target 클래스

package com.spring.ex01;

public class Calculator {
  public void add(int x, int y) {
    int result = x + y;
    System.out.println("결과:" + result);
  }

  public void subtract(int x, int y) {
    int result = x - y;
    System.out.println("결과:" + result);
  }

  public void multiply(int x, int y) {
    int result = x * y;
    System.out.println("결과:" + result);
  }

  public void divide(int x, int y) {
    int result = x / y;
    System.out.println("결과:" + result);
  }
}

pro20/src/com.spring/ex01/LoggingAdvice.java

advice 클래스

package com.spring.ex01;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class LoggingAdvice implements MethodInterceptor {
  public Object invoke(MethodInvocation invocation) throws Throwable {
    System.out.println("[메서드 호출 전 : LogginAdvice");
    System.out.println(invocation.getMethod() + "메서드 호출 전");
    Object object = invocation.proceed();
 
   System.out.println("[메서드 호출 후 : loggingAdvice");
   System.out.println(invocation.getMethod() + "메서드 호출 후");
    return object;
  }
}

pro20/src/com.spring/ex01/CalcTest.java

package com.spring.ex01;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class CalcTest {
  public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("AOPTest.xml");
    
    Calculator cal = (Calculator) context.getBean("proxyCal");
    
    /* 메소드 호출 - 호출 전후로 advice bean 적용 */
    cal.add(100, 20); System.out.println();
    cal.subtract(100, 20); System.out.println();
    cal.multiply(100, 20); System.out.println();
    cal.divide(100, 20);
  }
}

출력 결과

advice 출력 내용 (메소드 호출 전)
target 출력 내용 (메소드)
advice 출력 내용 (메소드 호출 후)


*자바 웹을 다루는 기술

profile
CS 메모장

0개의 댓글