본문으로 바로가기

Spring

category Backend/Spring 2020. 1. 29. 20:11
    반응형

    스프링을 구성하는 모듈

    1. Core, Beans
      스프링의 핵심 모듈로 Bean 컨테이너 관련 기능을 제공한다.
    2. Context
      국제화나 Java EE가 제공하는 몇몇 기능을 지원한다.
    3. AOP
      AOP(관점지향 프로그래밍) 기능을 제공하는 모듈
    4. ORM
      ORM(Object Relational Mapping) 기능을 제공하는 모듈. ORM API(JPA나 Hibernate, iBatis, JDO등) 지원
    5. JDBC
      JDBC 추상화 기능을 제공한다. JDBC에 의한 데이터베이스 액세스를 지원하며 트랜잭션 관리의 기반이 된다.
    6. Web
      파일 업로드 같은 웹애플리케이션 이용에 편리한 기능을 제공
    7. Web Servlet
      웹 애플리케이션에 MVC 프레임워크 기능을 제공한다.
    8. Test
      JUnit이나 TestNG를 사용한 테스트를 지원한다. 여러 테스트 시나리오로 활용할 수 있는 목 객체도 제공한다.

    의존성 주입 (Dependency Injection)

    스프링의 핵심은 의존 관계 주입이다.

    스프링 디자인패턴

    • 인터페이스를 통한 결합 약화
    • 팩토리 메소드 패턴
      • 의존성 주입 (Dependency Injection)

    빈팩토리

    • Bean의 생성과 설정, 관리를 맡음

    빈요소

    • 싱글턴 설정 가능(기본적으로 싱글톤)
    • 로딩 지연 여부
    • 의존성 설정
      • 생성자
      • 프로퍼티
    • 빈 초기화 시 실행시킬 메서드
    • 빈 컨테이너 종료 시 실행시킬 메서드

    빈 라이프 사이클

    빈 라이프 사이클을 알아둬야하는 이유는 빈 라이프 사이클 인터페이스 구현을 통해서 공통 처리를 하거나 로깅을 위함이다.

    어플리케이션 컨택스트

    • 빈 팩토리의 기능 외에 편리한 기능을 추가한다.
      • 메시지 국제화
      • 자원 접근 수단 간소화
      • 이벤트 처리
      • 복수 컨텍스트 적재
    • @Autowired는 어플리케이션 컨텍스트에서만 사용 가능하다.

    웹 스프링프레임워크

    기본적으로 스프링프레임워크는 콘솔이든 웹이든 애플리케이션에 구애를 받지 않는다. 콘솔일 경우 인스턴스화해서 Context를 사용하면 되지만 웹인 경우 아래와 같이 org.springframework.web.servlet.DispatcherServlet 스프링프레임워크에서 제공하는 DispatcherServlet을 사용하여 Context를 사용한다.

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/root-context.xml</param-value>
      </context-param>
      <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
      </servlet-mapping>
    </web-app>
    

    AOP 관점지향 프로그래밍

    • 각 모듈에 산재되어 있는 관심사(=관점)를 '횡단적 관심사'라고 부른다.
    • AOP는 '횡단적 관심사' 까지 분리하는 것으로, 각 모듈에서 관심사에 관한 코드를 완전히 제거
    • 객체지향 보다 한층 독릭성이 강해지는 것은 물론 재사용성과 보존성도 향상

    AOP 용어

    • 위빙
      관심사를 모듈에 삽입하는 것
    • Advice
      위빙되는 구체적인 처리
    • Joinpoint
      어드바이스를 위빙하는 포인트
    • Pointcut
      하나 혹은 여러개의 조인포인트를 하나로 묶은 것을 포인트 컷이라고 한다.
    • Advisor
      어드바이스 + 포인트컷

    프록시를 사용한 Spring AOP

    프록시를 통한 AOP 구현

    beans.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
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    
        <bean id="targetBean" class="sample1.MessageBeanImpl">
            <property name="name">
                <value>Spring</value>
            </property>
        </bean>
    
        <bean id="loggingAdvice" class="sample1.LoggingAdvice" />
    
        <!-- proxy 빈 생성 -->
        <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <!-- 대상빈 지정 -->
            <property name="target">
                <ref local="targetBean" />
            </property>
    
            <!-- advisor 지정 -->
            <property name="interceptorNames">
                <list>
                    <value>advisor</value>
                </list>
            </property>
        </bean>
    
        <!-- advisor 빈 생성 -->
        <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
            <!-- advice -->
            <property name="advice">
                <ref local="loggingAdvice" />
            </property>
    
            <!-- pointcut -->
            <property name="pointcut">
                <bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
                    <property name="pattern">
                        <value>.*sayHello.*</value>
                    </property>
                </bean>
            </property>
        </bean>
    </beans>
    

    LoggingAdvice.java

    package sample1;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.util.StopWatch;
    
    public class LoggingAdvice implements MethodInterceptor {
    
        public Object invoke(MethodInvocation invocation) throws Throwable {
    
            String methodName = invocation.getMethod().getName();
    
            StopWatch sw = new StopWatch();
    
            sw.start(methodName);
    
            System.out.println("[LOG] METHOD: " + methodName + " is calling.");
            Object rtnObj = invocation.proceed(); // 호출한 메소드 실행 
    
            sw.stop();
    
            System.out.println("[LOG] METHOD: " + methodName + " was called.");
            System.out.println("[LOG] 처리시간 " + sw.getTotalTimeMillis() / 1000 + "초");
    
            return rtnObj;
        }
    }
    package sample1;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.util.StopWatch;
    
    public class LoggingAdvice implements MethodInterceptor {
    
        public Object invoke(MethodInvocation invocation) throws Throwable {
    
            String methodName = invocation.getMethod().getName();
    
            StopWatch sw = new StopWatch();
    
            sw.start(methodName);
    
            System.out.println("[LOG] METHOD: " + methodName + " is calling.");
            Object rtnObj = invocation.proceed(); // 호출한 메소드 실행 
    
            sw.stop();
    
            System.out.println("[LOG] METHOD: " + methodName + " was called.");
            System.out.println("[LOG] 처리시간 " + sw.getTotalTimeMillis() / 1000 + "초");
    
            return rtnObj;
        }
    }

    HelloApp.java

    package sample1;
    
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.xml.XmlBeanFactory;
    import org.springframework.core.io.FileSystemResource;
    
    public class HelloApp {
        public static void main(String[] args) {
            BeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
    
            // MessageBean이 아닌 ProxyBean을 호출하고 있다.
            MessageBean bean = (MessageBean)factory.getBean("proxy");
    
            bean.sayHello();
        }
    }

    HelloApp.java 에서 중요한 포인트는 사용하는 빈이 아닌 Proxy 빈을 호출하고 있다는 점이다. 프록시 -> Advice -> 실제 메소드 순으로 호출이 된다.

    AspectJ를 사용한 Spring AOP

    프록시가 아닌 AspectJ를 사용하여 SpringAOP를 구현한다. AspjectJ는 관점지향 프로그래밍을 지원하기 위한 언어이다. 기존 클래스파일에 AspectJ로 프로그래밍한 내용이 위빙하여 최종 결과물이 생성된다. AOP 프로그래밍은 횡단 관심사 분리로 독립성 및 재사용성을 보장해준다.

    관점지향 프로그래밍은 새로운 방법론으로, 모듈화하는 새로운 단위인 애스펙트(aspect, 다른 여러 모듈들에 걸쳐 기능을 제공함)를 도입하여 횡단 관심사를 분리한다. 횡단 관심사를 핵심 모듈에 포함시키는 대신에 관점지향 프로그래밍을 사용하여 별도의 애스펙트에 구현한다. 컴파일러와 비슷한 애스펙트 직조기(aspect weaver)는 핵심 모듈(클래스)과 횡단 모듈(애스펙트와 클래스)을 직조(weaving) 과정으로 결합하여 최종 시스템을 구성한다. 즉, 횡단 모듈이 직조 과정을 통해 핵심 모듈의 결합점(join point, 아래에 설명)에 삽입되게 된다. 관점지향 프로그래밍은 횡단 관심사를 명확하게 모듈화하기 때문에 설계, 구현, 그리고 유지보수하기가 쉬운 시스템을 빠른 시간에 제작할 수 있다.

    beans.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-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
    
        <bean id="loggingSample" class="sample1.LoggingSample" />
    
        <!-- AspectJ -->
        <aop:config>
            <aop:aspect id="logAspece" ref="loggingSample">
                <!-- pointcut-->
                <aop:pointcut expression="execution(* sayHello())" id="logPointCut"/>
    
                <!-- advice -->
                <aop:around pointcut-ref="logPointCut" method="logAround"/>
            </aop:aspect>
        </aop:config>    
    
        <bean id="targetBean" class="sample1.MessageBeanImpl">
            <property name="name">
                <value>Spring</value>
            </property>
        </bean>
    </beans>
    

    LoggingSample.java

    package sample1;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.springframework.util.StopWatch;
    
    public class LoggingSample {
    
        public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
            String methodName = pjp.getKind();
            StopWatch sw = new StopWatch();
    
            sw.start(methodName);
    
            System.out.println("[LOG] METHOD: " + methodName + " is calling.");
            Object rtnObj = pjp.proceed();  // 메소드 호출
    
            sw.stop();
    
            System.out.println("[LOG] METHOD: " + methodName + " was called.");
            System.out.println("[LOG] 처리시간 " + sw.getTotalTimeMillis() / 1000 + "초");
    
            return rtnObj;
        }
    
    }

    HelloApp.java

    package sample1;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.FileSystemXmlApplicationContext;
    
    public class HelloApp {
        public static void main(String[] args) {
            ApplicationContext factory = new FileSystemXmlApplicationContext("beans.xml");
    
            //프록시가 아닌 실제 빈을 호출한다.
            MessageBean bean = (MessageBean)factory.getBean("targetBean");
    
            bean.sayHello();
        }
    }

    프록시가 아닌 실제 빈을 호출한다. 실제 advice가 빈에 위빙되었기 때문이다.

    AspectJ 어노테이션

    @Aspect, @Around... 어노테이션으로 AOP를 구현할 수 있다.

    스프링 JDBC

    JDBC는 자바가 DB에 접근하는 방식은 표준으로 정의한 것을 의미한다. JDBC 구현체를 DB Vendor에서 제공하면 JDBC 표준에 맞게 DB 접근이 가능하다. 하지만 JDBC에도 다음과 같은 문제가 있다.

    JDBC의 문제점

    1. JDBC 프로그래밍의 복잡성
      • Connection 연결 및 닫기 같은 유저가 관리하면 자칫 크리티컬한 문제가 발생할 수 있는 부분들이 그대로 노출되어있다.
      • Connection이나 Statement, ResultSet 클래스의 관리
    2. SQLException
      모든 JDBC 예외는 SQLException를 던지는데 구체적인 예외를 알기 위해서는 SQLException의 SQLState를 알아야 하고, SQLState를 안다고 해도 DB마다 각기 다른 예외 상태 데이터를 전달하기 때문에 예외처리를 하기 번거롭다.
    3. JDBC 인스턴스 획득
      • Connection 클래스 취득

    이와 같은 문제점으로 스프링 JDBC를 사용하면 JDBC의 장점은 유지하면서 문제점을 개선할 수 있다.

    스프링 JDBC 사용하기

    applicationContext.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
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    
        <!-- 1)데이터소스 설정 DriverManagerDataSource 클래스를 이용한다 -->
        <bean id="dataSource"
            class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <!-- JDBC 드라이버 클래스명을 설정 -->
            <property name="driverClassName">
                <value>com.mysql.jdbc.Driver</value>
            </property>
            <!-- JDBC 접속 문자열 설정 -->
            <property name="url">
                <value>jdbc:mysql://localhost/spring</value>
            </property>
            <!-- MySQL 유저 ID 설정 -->
            <property name="username">
                <value>springuser</value>
            </property>
            <!-- MySQL 패스워드 설정 -->
            <property name="password">
                <value>springpassword</value>
            </property>
        </bean>
    
        <bean id="menuUi" class="ui.MenuUi">
            <property name="selectTeamUi" ref="selectTeamUi"></property>
        </bean>
    
        <bean id="teamDao" class="dao.impl.TeamDaoImpl">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <bean id="selectTeamUi" class="ui.SelectTeamUi">
            <property name="teamDao" ref="teamDao"></property>
        </bean>
    
    </beans>

    JPA

    Java Persistence API (JPA) 자바 표준 O/R 맵핑 사양이다. 구현된 것으로는 하이버네이트나 탑링크 제품이 있다.

    하이버네이트

    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="1.0"
        xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
        <persistence-unit name="ticketReservation">
            <provider>org.hibernate.ejb.HibernatePersistence</provider>
            <class>model.Event</class>
            <class>model.Rank</class>
            <class>model.Reservation</class>
            <class>model.Ticket</class>
            <class>model.User</class>
            <exclude-unlisted-classes/>
            <properties>
                <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
                <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
                <property name="hibernate.connection.username" value="springuser" />
                <property name="hibernate.connection.password" value="springpassword" />
                <property name="hibernate.connection.url" value="jdbc:mysql://localhost/spring" />
                <property name="hibernate.show_sql" value="true" />
            </properties>
        </persistence-unit>
    </persistence>

    스프링 배치

    스프링 배치와 쿼츠의 다른점

    • 쿼츠는 스케줄러에 관한 기능을 제공한다. 배치는 기능이 없다.
    • 스프링 배치는 배치에 필요한 기능들을 제공한다. 스케줄링 기능이 없다.
    • 주로 쿼츠의 스케줄링을 사용하여 배치를 처리한다.

    배치에 필요한 기술

    • 대용량 처리를 위해서 서버의 부하를 줄이는 방법
    • 프로세스가 중단됐을 때 중간부터 다시 실행하는 방법
    • 중복 실행을 방지하는 방법

    스프링 배치 구성

    spring batch jar 파일안에 DML이 존재함. 스프링 배치 구동을 위해 필요한 테이블들(메타데이터)

    • JOB에 대한 정보를 저장함.
    반응형