Spring Framework 전환- 1011

Yung·2022년 10월 11일
0

Java223bitcamp

목록 보기
18/26

DataSource의 역할 I : 같은 스레드 같은 커넥션

DataSource의 역할 II : 커넥션 재사용

  • Connection Pool
    커넥션을 생성 -> 사용 -> 반납 -> 재사용
    => 커넥션 생성 과정에서 소요되는 사용자 인증, 권한 검사 시간을 줄일 수 있다.
    => 커넥션 객체를 여러개 관리

DataSource의 커넥션 풀 구동 과정

  • 커넥션 요청
    1) 커넥션 객체가 없으면, 새로 만들어 준다.
    2) 커넥션 객체가 있으면, 기존객체를 준다.
    • 기존객체를 준다 -> 생성에 소요되는 시간 을 줄일 수 있다.
    • 생성에 소요되는 시간 -> 사용자 인증, 권한 검사 등
    • 가비지 생성을 줄일 수 있다 -> 메모리를 효율적으로 운영

Spring FrameWork와 라이브러리 파일

1) Spring-context : Spring IoC 컨테이너와 관련된 라이브러리
2) Spring-jdbc : Spring JDBC 관련 라이브러리
3)

Spring IoC 컨테이너 준비 과정

new AnnotationConfigApplicationContext(AppCOnfig.class)
1) AppConfig(자바 config)객체 생성
2) 클래스나 메서드, 필드에 붙은 애노테이션 처리

079. Spring IoC 컨테이너 도입하기 : 페이지 컨트롤러 생성

Spring IoC 컨테이너 또는 Bean 컨테이너라고도 불린다.
Client에서 요청이 들어오면 DispacherServlet가 객체를 달라고 SpringIoC컨테이너에게 요청 하면 IoC는 컨테이너는 컨트롤러,서비스,DAO등의 객체를 생성후 Controller에 객체 리턴


Bean = object 객체 = instance
IoC(Inversion of Control) : 역제어(제어의 역전)
IoC는 DI와 Listener를 포함하는 개념
IoC컨테이너는 DI컨테이너 또는 Bean컨테이너라고도 불린다.

1) 일반적인경우 : 객체가 필요하면 생성해서 쓴다.
역제어 : 필요한 객체를 외부에서 만들어 주입해 준다.
역제어의 방식으로 프로그래밍을 하면 객체를 교체하기 쉽다. <= DI(Dependency Injection) : 의존객체 주입

2) 일반적인경우 : 어떤 작업을 수행하기 위해 메서드를 호출한다.
역제어 : 특정 상태에 놓일때 등록된 메서드가 자동호출 된다.
리스너, 필터, 서블릿 등 : 특정 상태에 놓일때 등록된 메서드가 자동호출 된다, 전형적인 역제어 방식

1단계 - Spring IoC 컨테이너 프레임워크를 프로젝트에 추가한다.

  • search.maven.org 에서 spring-context 라이브러리 검색한다.
  • 빌드 스크립트 파일(build.gradle)에 의존 라이브러리 정보를 추가한다.
    • `implementation 'org.springframework:spring-context:5.3.23
  • gradle eclipse 실행한다.
  • 이클립스IDE에서 프로젝트를 갱신한다.

2단계 - ContextLoaderListener에서 스프링 IoC 컨테이너를 준비한다.

  • com.bitcamp.board.config.AppConfig 클래스 생성
    • 스프링 IoC 컨테이너의 설정을 수행하는 클래스
  • com.bitcamp.board.listener.ContextLoaderListener 클래스 변경
// 스프링 IoC 컨테이너의 설정을 수행하는 클래스
// 1) DB 커넥션 객체 관리자 준비 : DataSource
public class AppConfig {

  public AppConfig() {
    System.out.println("AppConfig() 생성자 호출됨!");
  }
}
  • com.bitcamp.board.listener.ContextLoaderListener 클래스 변경
@WebListener
public class ContextLoaderListener implements ServletContextListener {
  @Override
  public void contextInitialized(ServletContextEvent sce) {
    System.out.println("공유 자원을 준비 중!!");
    try {

      // Spring IoC 컨테이너 준비
      AnnotationConfigApplicationContext iocContainer =
          new AnnotationConfigApplicationContext(AppConfig.class);

3단계 - Spring IoC 컨테이너에서 DataSource 객체를 생성한다.

  • Spring JDBC 라이브러리 추가한다.
    • search.maven.org 에서 spring-jdbc 라이브러리 검색한다.
    • 빌드 스크립트 파일(build.gradle)에 의존 라이브러리 정보를 추가한다.
    • implementation 'org.springframework:spring-jdbc:5.3.23'
    • gradle eclipse 실행한다.
    • 이클립스IDE에서 프로젝트를 갱신한다.
  • com.bitcamp.board.config.AppConfig 클래스 변경
    • createDataSource(): DataSource 구현체를 준비한다.
  // DataSource를 생성하는 메서드를 호출하라고 표시한다.
  // 메서드가 리턴한 객체는 @Bean 애노테이션에 지정된 이름으로 컨테이너에 보관될 것이다.
  @Bean
  public DataSource createDataSource() {
    System.out.println("createDataSource() 호출됨!");

    DriverManagerDataSource ds = new DriverManagerDataSource();
    ds.setDriverClassName("org.mariadb.jdbc.Driver");
    ds.setUrl("jdbc:mariadb://localhost:3306/studydb");
    ds.setUsername("study");
    ds.setPassword("1111");
    return ds;
  }

4단계 - Spring IoC 컨테이너에서 트랜젝션 관리자를 생성한다.

  • com.bitcamp.board.config.AppConfig 클래스 정의
    • createTransactionManager() : PlatformTransactionManager 구현체를 준비한다.
  //객체가 리턴한 값을 transactionManager라는 이름으로 저장한다.
  @Bean("transactionManager")
  public PlatformTransactionManager createTransactionManager(DataSource ds) {
    // Spring IoC 컨테이너는 이 메서드를 호출하기 전에 
    // 이 메서드가 원하는 파라미터 값인 DataSource를 먼저 생성한다.
    // => createDataSource() 메서드를 먼저 호출한다.
    System.out.println("createTransactionManager() 호출됨!");
    return new DataSourceTransactionManager(ds);
  }

5단계 - 스프링에서 생성한 DataSource를 사용하도록 DAO를 변경한다.

  • com.bitcamp.sql.DataSource 클래스 삭제
  • com.bitcamp.board.dao.MariaDBBoardDao 클래스 변경
  • com.bitcamp.board.dao.MariaDBMemberDao 클래스 변경

6단계 - 스프링에서 DAO 객체를 생성한다.

  • com.bitcamp.board.listener.ContextLoaderListener 클래스 변경
    • DataSource 생성 코드 삭제
    • TransactionManager 생성 코드 삭제
    • DAO 생성 코드삭제
  • com.bitcamp.board.dao.MariaDBBoardDao 클래스 변경
    • Spring IoC 컨테이너가 관리하는 개체임을 표시한다.
  • com.bitcamp.board.dao.MariaDBMemberDao 클래스 변경
    • Spring IoC 컨테이너가 관리하는 개체임을 표시한다.
// Spring IoC 컨테이너가 관리하는 개체임을 표시한다.
@Component
// - 이 애노테이션을 붙이면 Spring IoC 컨테이너가 객체를 자동 생성한다.
// - 물론 생성자의 파라미터 값을 자동으로 주입한다.
// - 파라미터에 해당하는 객체가 없다면 생성 오류가 발생한다.
public class MariaDBBoardDao implements BoardDao {

  DataSource ds;

  public MariaDBBoardDao(DataSource ds) {
    System.out.println("4) MariaDBBoardDao() 호출됨!");
    this.ds = ds;
  }
  • com.bitcamp.board.config.AppConfig 클래스 변경
    • @Component가 붙은 클래스를 찾아 객체를 생성하도록 그 클래스가 소속된 패키지를 지정한다.
// 스프링 IoC 컨테이너의 설정을 수행하는 클래스
// 1) DB 커넥션 객체 관리자 준비 : DataSource
// 2) 트랜잭션 관리자 준비 : PlaqtformTransactionManager
// 3) 어떤패키지의 있는 객체를 자동으로 생성할 것인지 지정한다.
@ComponentScan(value = "com.bitcamp.board")
// - com.bitcamp.board 패키지 및 그 하위 패키지에 소속된 클래스 중에서
//   @Component, @Controller, @Service, @Repository 등의 애노테이션이 붙은 클래스를 찾아
//   객체를 생성한다.
public class AppConfig {
}

7단계 - 스프링에서 서비스 객체를 생성한다.

  • com.bitcamp.board.listener.ContextLoaderListener 클래스 변경
    • 서비스 객체 생성 코드 삭제
  • com.bitcamp.board.service.DefaultBoardService 클래스 변경
    • Spring IoC 컨테이너가 관리하는 개체임을 표시한다.
    • 트랜잭션 메니저를 Spring 에서 제공하는 객체로 교체한다.
@Component
// - 이 애노테이션을 붙이면 Spring IoC 컨테이너가 객체를 자동 생성한다.
// - 객체의 이름을 명시하지 않으면
//   클래스 이름(첫 알파벳은 소문자. 예: "mariaDBBoardDao")을 사용하여 저장한다.
// - 물론 생성자의 파라미터 값을 자동으로 주입한다.
// - 파라미터에 해당하는 객체가 없다면 생성 오류가 발생한다.
public class DefaultBoardService implements BoardService {

  PlatformTransactionManager txManager;
  BoardDao boardDao;

  public DefaultBoardService(BoardDao boardDao, PlatformTransactionManager txManager) {
    System.out.println("5) DefaultBoardService() 호출됨!");
    this.boardDao = boardDao;
    this.txManager = txManager;
  }

  @Override
  public void add(Board board) throws Exception {
    // 트랜잭션 동작 방법을 정의한다.
    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setName("tx1");
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

    TransactionStatus status = txManager.getTransaction(def);
  • com.bitcamp.board.service.DefaultMemberService 클래스 변경
    • Spring IoC 컨테이너가 관리하는 개체임을 표시한다.
@Component
// - 이 애노테이션을 붙이면 Spring IoC 컨테이너가 객체를 자동 생성한다.
// - 객체의 이름을 명시하지 않으면
//   클래스 이름(첫 알파벳은 소문자. 예: "mariaDBMemberDao")을 사용하여 저장한다.
// - 물론 생성자의 파라미터 값을 자동으로 주입한다.
// - 파라미터에 해당하는 객체가 없다면 생성 오류가 발생한다.
public class DefaultMemberService implements MemberService {
  MemberDao memberDao;

  public DefaultMemberService(MemberDao memberDao) {
    System.out.println("5) DefaultMemberService() 호출됨!");
    this.memberDao = memberDao;
  };

8단계 - 스프링에서 페이지 컨트롤러를 생성한다.

  • com.bitcamp.board.controller.XxxController 클래스 변경
    • Spring IoC 컨테이너가 관리하는 개체임을 표시한다.
@Component("/board/xxx")
// - 애노테이션을 붙일 때 객체 이름을 명시하면 그 이름으로 저장한다.
// - 프론트 컨트롤러는 페이지 컨트로러를 찾을 때 이 이름으로 찾을 것이다.
  • com.bitcamp.board.listener.ContextLoaderListener 클래스
    • 페이지 컨트롤러 생성 코드를 삭제한다.

9단계 - Spring IoC 컨테이너를 프론트 컨트롤러에 주입한다.

  • com.bitcamp.board.servlet.DispatcherServlet 클래스 변경
    • Spring IoC 컨테이너를 주입받는 생성자로 변경한다.
public class DispatcherServlet extends HttpServlet {

  ApplicationContext iocContainer;

  public DispatcherServlet(ApplicationContext iocContainer) {
    // Spring IoC 컨테이너를 주입 받는다.
    this.iocContainer = iocContainer;
  }
  • com.bitcamp.board.listener.ContextLoaderListener 클래스
    • DispatcherServlet 객체를 생성할 때 생성자 파라미터로 Spring IoC 컨테이너를 주입한다.
@MultipartConfig(maxFileSize = 1024 * 1024 * 10)
@WebListener
public class ContextLoaderListener implements ServletContextListener {
  @Override
  public void contextInitialized(ServletContextEvent sce) {
    System.out.println("공유 자원을 준비 중!!");
    try {

      // Spring IoC 컨테이너 준비
      AnnotationConfigApplicationContext iocContainer =
          new AnnotationConfigApplicationContext(AppConfig.class);

      ServletContext ctx = sce.getServletContext();

      // 자바 코드로 서블릿 객체를 직접 생성하여 서버에 등록하기
      DispatcherServlet servlet = new DispatcherServlet(iocContainer);
      Dynamic config = ctx.addServlet("DispatcherServlet", servlet);
      config.addMapping("/service/*");
      config.setMultipartConfig(
          new MultipartConfigElement(this.getClass().getAnnotation(MultipartConfig.class)));

    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

10단계 - Spring IoC 컨테이너에서 페이지 컨트롤러를 꺼내 실행한다.

public class DispatcherServlet extends HttpServlet {

  ApplicationContext iocContainer;

  public DispatcherServlet(ApplicationContext iocContainer) {
    // Spring IoC 컨테이너를 주입 받는다.
    this.iocContainer = iocContainer;
  }

  private static final long serialVersionUID = 1L;

  ///java.io.Seriallizable를 구현한 애들은 seriallizable 설정해야한다 아니면 랜덤한 값으로 설정된다.
  // seriallizable 객체를 seriallizable 바이트배열로 만들어서 내보낼때 사용한다.
  // 프로그램을 내보낼때는 항상 버전을 정해서 보내야한다.
  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    try {
      // 프론트 컨트롤럴르 경유해서 실행할 페이지 컨트롤러의 경로를 알아낸다.
      //  - "/service" 다음에 오는 경로, 즉 * 에 해당하는 경로를 리턴한다.
      // System.out.println(pathInfo); -> /, /haha/nan/hul
      String pathInfo = req.getPathInfo();
      System.out.println(pathInfo);

      // 페이지 컨트롤러를 찾는다.
      // - Spring IoC 컨테이너는 객체를 찾지 못할 때 예외를 발생시킨다.
      Controller controller = (Controller) iocContainer.getBean("pathInfo");
      System.out.println("controller: " + iocContainer.getBean("pathInfo"));

      // 페이지 컨트롤러를 실행한다.
      resp.setContentType("text/html;charset=UTF-8");
      String viewName = controller.execute(req, resp);

      if (viewName.startsWith("redirect:")) { // 예) "redirect:list"
        resp.sendRedirect(viewName.substring(9)); // 예) "list"
        return;
      } else {
        req.getRequestDispatcher(viewName).include(req, resp); // JSP를 실행한 후 리턴된다.
      }
    } catch (Exception e) {
      req.setAttribute("exception", e);
      req.getRequestDispatcher("/error.jsp").forward(req, resp);
    }
  }
}

별도 -

  • com.bitcamp.servlet ... LoginCheckFilter 클래스 변경
    // 콘텐트를 틍록, 변경, 삭제하는 경우 로그인 여부를 확인한다.
    if (servletPath.toLowerCase().endsWith("add") || servletPath.toLowerCase().endsWith("update")
        || servletPath.toLowerCase().endsWith("delete")) {

080. Spring WebMVC 프론트 컨트롤러 도입하기

1단계 - Spring WebMVC 프레임워크를 프로젝트에 추가한다.

  • search.maven.org 에서 spring-webmvc 라이브러리 검색한다.
  • 빌드 스크립트 파일(build.gradle)에 의존 라이브러리 정보를 추가한다.
    • 기존의 spring-context 라이브러리를 제거한다.
    • webmvc 라이브러리가 context 라이브러리를 의존하기 때문에 자동으로 포함된다.
  • gradle eclipse 실행한다.
  • 이클립스IDE에서 프로젝트를 갱신한다.

2단계 - 스프링에서 제공하는 프론트 컨르롤러를 사용한다.

  • com.bitcamp.servlet.DispatcherServlet 클래스 삭제
  • com.bitcamp.board.listener.ContextLoaderListener 클래스 변경
// 웹애플리케이션이 시작되었을 때 공유할 자원을 준비시키거나 해제하는 일을 한다.
@MultipartConfig(maxFileSize = 1024 * 1024 * 10)
@WebListener
public class ContextLoaderListener implements ServletContextListener {
  @Override
  public void contextInitialized(ServletContextEvent sce) {
    System.out.println("공유 자원을 준비 중!!");
    try {

      // 웹 기능이 포함된 스프 IoC 컨테이너 준비
      AnnotationConfigWebApplicationContext iocContainer =
          new AnnotationConfigWebApplicationContext();

      iocContainer.register(AppConfig.class);
      iocContainer.refresh();// 자바 config zmffotm(AppConfig)에 설정된 대로 객체를 생성한다.

      ServletContext ctx = sce.getServletContext();

      // 자바 코드로 서블릿 객체를 직접 생성하여 서버에 등록하기
      DispatcherServlet servlet = new DispatcherServlet(iocContainer);

      Dynamic config = ctx.addServlet("DispatcherServlet", servlet);
      config.addMapping("/service/*");
      config.setMultipartConfig(
          new MultipartConfigElement(this.getClass().getAnnotation(MultipartConfig.class)));
      config.setLoadOnStartup(1);

    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

3단계 - 페이지 컨트롤러를 스프링 규격에 맞춰서 정의한다.

  • com.bitcamp.board.controller.XxxController 클래스 변경

4단계 - 서비스, DAO 객체의 역할을 설명하는 애노테이션으로 바꾼다.

  • com.bitcamp.board.dao.*Dao 클래스 변경
    @Repository // DAO 역할을 수행하는 객체에 붙이는 애노테이션
  • com.bitcamp.board.service.*Service 클래스 변경
    @Service // 서비스 역할을 수행하는 객체에 붙이는 애노테이션

0개의 댓글