어플리케이션의 공통기능들을 핵심로직과 분리하여 공통관심(로깅, 트랜잭션 등등)으로 두고 관리한다. 
이떄 기본적이 개념은 공통 관심 사항을 구현한 코드를 핵심 로직을 구현한 코드 안에 삽입하는 것이다. 

 트랜잭션 같은 부가기능은 핵심기능과 같은 방식으로 모듈화하기 매우 힘들다. 이름 그대로 부가기능이기때문에 스스로 독립적인 방식으로 존재해서는 적용되기 어렵기 떄문이다. 그래서 데코레이션 패턴, 다이내믹 프록시, 오브젝트 생성 후처리, 자동 프록시 생성, 포인트 컷과 같은 기법이 생겨났다.
  그리고 이런 독립적으로 모듈화가 불가능한 것들의 모듈화를 연구해온 사람들이 기존의 객체지향 설계 페러다임과 구분되는 새로운 특성이 있다고 생각했고 그것을 애스팩트라고 불렀다. 
 
 애스팩트 : 그 자체로 애플리케이션의 핵심 기능을 담고 있진 않지만 애플리케이션을 구성하는 한가지 요소이고 핵심기능에 부가되어 의미를 갖는 특별한 모듈 
 그리고 이 모듈을 만들어 설계하고 개발하는 방법을 애스팩트 지향 프로그래밍(AOP)이라고 한다.

target : 부가기능을 부여할 대상
advice : 타깃에게 제공할 부가기능을 담은 모듈 , 오브젝트, 메소드 레벨 모두 정의가능하다.
weaving : advice를 핵심기능에 적용하는 것
aspect : 여러 객체에 공통으로 적용되는 기능
joinpoint : advice를 적용 될수있는 위치, 타깃 오브젝트가 구현한 인터페이스는 모두이다. 
pointcut: joinpoint의 부분집합으로서 실제로 advice가 적용되는 joinpoint를 나타낸다. 
proxy :  클라이언트와 타킷 사이에 투명하게 존재하면서 부가기능을 제공하는 오브젝트 DI를 통해 타깃대신 클라이언트에 주입된다.
advisor : 포인트컷과 어드바이스를 하나씩 갖고 있는 오브젝트. 부가기능(어드바이스)를 어디에(포인트컷) 전달할것인가 아는 기본모듈
            스프링만의 용어임

세가지 weaving방식 
-컴파일시 : aspectJ에서 사용 컴파일 결과 aop가 적용된 클래스 파일이 생성된다. 
-클래스 로딩시 : 원본 클래스 파일을 변경하지 않고 클래스를 로딩할때 jvm이 변경된 바이트 코드를 사용하도록 함으로써 aop를 적용한다. aspectJ도 지원함
-런타임시 : 소스 코드나 클래스 정보 자체를 변경하지 않는다 대신 프록시를 사용한다. 

1.스프링의 AOP( 프록시를 이용한 AOP )
모듈화가 된 부가기능(advice)과 적용대상(pointcut)의 조합을 통해 여러 오브젝트에 산재해서 나타는 공통적인 기능을 손쉽게 개발하고 관리할수 있는 기술이다. 
 스프링은 다이내믹 프록시 기술을 이용해 AOP개발 기능을 제공한다. 
프록시 기술 방식의 AOP는 데코레이션 패턴 또는 프록시 패턴을 응용해 기존 코드에 영향을 주지 않은 채로 부가기능을 타깃오브젝트에 제공할수있는 객체지향 프로그래밍 모델로부터 출발한다. 여기에 포인트 컷이라는 적용 대상 기법과 자동 프록시 생성이라는 적용 기법까지 적목하면 비로소  AOP라고 부를수있는 효과적인 부가기능 모듈화가 가능해진다. 

client가 target을 직접적으로 알고 있으면 안된다. client가 사용할 오브젝트를 DI컨테이너 설정을 통해 바꿀수없기 때문이다. 따라서 DI원리에 의존하고 있는 데코레이션 패턴이 적용된 프록시 방식이 적용되지 못한다. 
 따라서 Client가 target이 구현하고있는 인터페이스를 이용해 의존하도록 만들어야 한다.  aop는 하나의 모듈을 많은 오브젝트의 메소드 실행시점에 일괄 적용할 수 있어야 한다.  그래서 스프링은 자동 프록시 생성기를 이용해서 컨테이너 초기화중에 만들어진 빈을 바꿔치기해 프록시 빈을 자동으로 등록해준다. 빈을 등록하는 로직은 포인트컷을 이용하면되고 어노테이션,xml을 통해 이미 정의된 빈의 의존관계를 바꿔치기하는것은 빈 후처리가 사용한다.  이 자동 프록시 생성기가 바로 스프링의 프록시 기반 AOP의 핵심동작원리이다. 

 최소한 네가지 빈을 등록해야한다.
 -자동 프록시 생성기
스프링의 DefaultAdvisorAutoProxyCreator 클래스를 빈으로 등록한다. 다른 빈을 DI하지 않고 자신도 DI되지 않으며 독립적으로 존재한다. 애플리케이션 컨텍스트가 빈 오브젝르를 생성하는 과정에 빈 후처리기로 참여한다. 빈으로 등록된 어드바이저를 이용해서 프록시를 자동으로 생성하는 기능을 한다.

 -어드바이스 
 부가기능을 구현한 클래스를 빈으로 등록한다. 

 -포인트컷 
 스프링의 AspectJExpressionPointcut을 빈으로 등록하고 expression프로퍼티에 포인트컷 표현식을 넣어주며 된다 
 
 -어드바이져
 자동프록시 생성기에 의해 자동 검색되어 사용된다. 

<aop:config>
    <aop:pointcut id="mybatis_operations"   expression="execution( *com. *Dao.)"/>
    <aop:advisor advice-ref="mybatis_txAdvice" pointcut-ref="mybatis_operations"/>
</aop:config>

<aop:config> : AspectJAdvisorAutoProxyCreator 을 빈으로 등록한다.
<aop:pointcut> : AspectJExpressionPointcut을 빈으로 등록한다.
<aop:advisor> :  advice와 pointcut의 ref를 프로퍼티로 갖는 DefaultBeanFactoryPointcutAdvisor를 등록한다.

1.AOP 인터페이스 구현과 <bean> 등록을 이용하는 방법
2.AOP 인터페이스 구현과 aop 네임스페이스의 <aop:advisor>를 이용 하는 방법
3.임의의 자바 클래스와 aop 네임스페이스의 <aop:aspect>를 이용하는 방법
2번과 마찬가지로 aop네임페이스를 사용하나 어드바이저 대신 에스펙트라는 개념을 이용한다. 
스프링 aop를 사용한다면 어떤 개발 방식이든 모두 프록시 방식의 AOP다. 

스프링의 aop는 기본적으로 다이내믹 프록시를 기법을 사용한다. 이 기법을 사용하려면 기본적으로 인터페이스가 있어야 하는데 특별히 레거시 클래스코드를 써야하는 경우 어떻게 해야할까
이때는 클래스 프록시 모드를 사용한다. 인터페이스가 아닌 레거시 클래스 코드일경우 JDK 다이내믹 믹 프록시가 아닌 CGLIB 라이브러리가 제공된다. 
 <tx: config proxy-target-class="true">
클래스 프록시는 타깃클래스를 상속해서 프록시를 만드므로 final클래스는 적용할수없으며 생성자가 두번호출되는 문제로 생성자에서 중요한 작업은 피하도록 해야한다. 그리고 클래스의 public 메소드 전체가 트랜잭션의 대상이 된다. 

4.@AspectJ 애노테이션을 이용한 애스펙트 개발 방법
<aop:config> 대신 <aop:aspectj-autoproxy /> 사용하여 @AspectJ가 붙은 것을 모두 에스펙트로 등록해줘야 한다. 어드바이스와 포인트컷은 자바 클래스내에 정의한 어노테이션을 이용해 정의된다.
 -@PointCut 
 -어드바이스: @Before, @AfterReturning,@AfterThrowing,@After,@Around
 

2.AspectJ AOP ( 바이트코드 새성과 조작을 통한 AOP )
 자바 언어 자체를 확장해서 만든 aspect문법을 이용해 에스펙트를 작성해야한다.  스프링의 AOP는 단지 DI의 도움을 받아 프록시 오브젝트를 추가함으로써 애스펙트를 적용할수있다. 하지만 AspectJ는 이와 달리 아예 타깃오브젝트 자체의 코드를 바꿈으로서 애스펙트를 적용한다.  따라서 프록시를 사용하지 않는다. 대신 타깃 오버젝트와 자바 코드에 처음부터 애스펙트가 적용되어 있던것처럼 클래스 바이트코드를 변경하는 작업이 필요하다. 
 자바의 프록시 기법으로는 특정 오브젝트가 생성된 시점이나 특정 필드를 읽을 지점에 어드바이스를 추가할 방법이 없다. 하지만 AspectJ는 private 메소다의 호출, 스태틱 메소드 호출, 오브젝트의 생성순간 까지 다 참여가능하다. 
 
스프링 빈에서 작업의 실행을 어드바이즈하는것이 전부라면 스프링 AOP가 좋은 선택이나 컨테이너가 관리하지않는 객체(도메인 등)를 어드바이즈하거나 간단한 메서드들 외에 조인포인트를 어드바이즈해야된다면 AspectJ 를 사용하는것이 좋다. 

출처 :토비,  4.0 최범균저

'STUDY > SPRING' 카테고리의 다른 글

Java EE 디자인패턴(ing)  (0) 2018.11.12
DATA Access  (0) 2018.11.08
TRANSACTION  (0) 2018.11.06
MVC  (0) 2018.11.02
[코드로 배우는 스프링 웹프로젝트]  (0) 2018.10.31

트랜잭션 : 더 이상 쪼갤 수 없는 최소 단위의 작업 

DAO메소드에서 DB커넥션을 매번 만든다. connection 오브젝트를 하나로 사용하려면 호출시마다 파라미터로 전달해주어야 한다. 
히지만 이렇게 될 경우 개발자가 connection까지 연결을 고려해야한다.
그래서 스프링은 트랜잭션 동기화를 설정해준다.
DAO가 사용하는JDBCTemplete이 트랜잭션 동기화 방식을 이용하도록 하는 것이다. 
실제 스프링을 사용하지 않는 자바에서는 직접 connection연결 관리부터 커밋,롤백을 제어해주어야 한다. 
     manager.getConnection( getConnectionHandler -> {
            if( getConnectionHandler.failed() ) {
                return;
            }
            SQLConnection conn = connectionHandler.getConnection();
            conn.setAutoCommit( false, commitHandler -> {
                if(commitHandler.failed() ) {
                    conn.close();
                    return;
                }
                ......
                //트랜잭션 작업 실행
                          //실패시
                if ( completeHandler.failed() ) {
                        conn.rollback( rollbackHandler -> {
                            if( rollbackHandler.failed() ) {
                                                                 logger.info( "rollbackHandler.failed()" );
                            }
                            conn.close();
                        } );
                        return;
                }
                //성공시
                conn.commit( commitHandler -> {
                        conn.close();
                })  
......   
})

내부적으로는 setAutoComit(false)를 호출해 트랜잭션을 시작시킨 후에  본격적으로 DAO기능을 이용하기 시작한다.  동작 메소드 내부에서
이용하는 JDBCTemplete메소드에서는 가장 먼저 트랜잭션 동기화 저장소에 현재 시작된 트랜잭션을 가진 connection object가 존재하는지 확인한다. 
이미 저장해둔 connection이 있다면 이를 가져와 PreparedStatment를 만들고 수정 SQL을 실행한다.  실행이 끝난후 connection을 닫지 않은 채로 작업
을 마친다. 
  여전히 connection은 열려있고 트랜잭션은 진행중 인채로 동기화 저장소에 저장되어있다. 모든 작업이 완료되면 commit을 호출해서 트랜잭션을 완료시킨다. 그리고 close()를 호출한다.  

 이떄 한개의 트랜젝션안에 여러개의 DB에 데이터를 넣는 작업은 해야할때는 어떻게 해야 할까?
로컬 트랜잭션은 하나의 DB Connection에 종속된다. 따라서 별도의 트랜잭션 관리자를 통해 트랜잭션을 관리하는 글로벌 트랜젝션방식을 사용해야한다
자바는 하나이상의 DB가 참여하는 트랜잭션을 만들면 JTA를 사용해야한다. 그외 하버네이트를 이용해 트랜잭션을 관리 할수있다.
하지만 특정 트랜잭션 방법에 의존적이지 않고 독립적이게 만들 수 없을까?
 DB에서 제공하는 DB클라이언트 라이브러리와 API는 서로 호환되지 않는 독자적인 방식이다.  SQL을 이용하는 방식이라는 공통점으로 만
들어낸것이 JDBC이며 이 추상화된 기술로 DB의 종류에 상관없이 일관적으로 데이터에 엑서스 할수있다. 이것처럼 트랜잭션 처리코드에서 추상화가 도입될수있다. 

스프링은 데이터 엑서스 기술과 트랜잭션 서비스 사이의 종속성을 제거하고 스프링이 제공하는 트랜잭셔 추상 계층을 이용해서 서비스의 종류나 환경이 바뀌어도 트랜잭션을 사용하는 코드는 그대로 유지할 수 있는 유연성을 갖추고 있다.

 스프링의 트랜잭션 추상 인터페이스 : PlatformTransactionManager
 PlatformTransactionManager를 구현한 transactionManger를 주입하는 방식으로 사용한다. 

단일 책임 원칙 : 하나의 모듈은 한가지 책임을 가진다. 

PlatformTransactionManager은 트랜잭션의 경계를 지정(시작,종료, 종료시 정상, 비정상 판단)한다.
PlatformTransactionManager의 구현 클래스
-DataSourceTransactionManager 
  Connection의 트랜잭션 API를 이용해서 트랜잭션을 관리한다. 그리고 이 매니저를 사용하려면 빈에 등록되어있어야한다.
이 빈의 ref는 datasource id를 지정한다 
            -root-context.xml
       <bean id="dataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource" destroy-method="close">
             <property name="driverClassName"                                   value="#{contextProperties.driver}"/>
             <property name="url"                                               value="#{contextProperties.url}"/>
             <property name="username"                                          value="#{contextProperties.username}"/>
             <property name="password"                                          value="#{contextProperties.password}">
       </bean>
       <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
             <property name="dataSource" ref="dataSource"/>
       </bean>
        
       -contextProperties
        driver=oracle.jdbc.driver.OracleDriver
        url=jdbc:oracle:thin:@127.0.0.1:1521:XE
        username=user
        password=1234
        -service
        @Resource(name="transactionManager")
        private DataSourceTransactionManager transactionManager;

        DefaultTransactionDefinition defaultTransactionDefinition= new DefaultTransactionDefinition();
       /**
        * Set the name of this transaction. Default is none.
        * <p>This will be used as transaction name to be shown in a
        * transaction monitor, if applicable (for example, WebLogic's).
        */
       defaultTransactionDefinition.setName("insert tx");
       status = transactionManager.getTransaction(defaultTransactionDefinition);
       try{
           dao의 작업 
           transactionManager.commit( status );
       }catch{
           transactionManager.rollback( status );
       }

선언적 트랜잭션 
코드에서 실행하지않고 설정파일이나 어노테이션을 이용하여 트랜잭션의 범위, 롤백규칙을 정의한다.
트랜잭션은 전파방식, 격리수준, 읽기 전용, 타임아웃, 롤백규칙으로 정의된다. 

1.aop와 tx스카마 태그사용
트랙잰션 설정마다 변경이 필요없다. 
count 같은 쿼리를 만들때 읽기전용 속성을 메소드마다 추가해줘야하지만 tx 스키마 태그를 이용하면 한번에 처리가능하다.
       <tx:annotation-driven transaction-manager="transactionManager" />
       <tx:advice id="mybatis_txAdvice" transaction-manager="transactionManager">
             <tx:attributes>
                    <tx:method name="list*"                 read-only="true"  propagation="NOT_SUPPORTED"/>
                    <tx:method name="count*"                read-only="true"  propagation="NOT_SUPPORTED"/>
                    <tx:method name="insert*"               timeout="3" rollback-for="Exception"   propagation="REQUIRED"/>
                    <tx:method name="add*"                  timeout="3" rollback-for="Exception"   propagation="REQUIRED"/>
                    <tx:method name="update*"               timeout="3" rollback-for="Exception"   propagation="REQUIRED"/>
                    <tx:method name="delete*"               timeout="3" rollback-for="Exception"   propagation="REQUIRED"/>
             </tx:attributes>
       </tx:advice>

<aop:config>
<aop:pointcut id="mybatis_operations"  expression="execution( *com. *Dao.)"/>
<aop:advisor advice-ref="mybatis_txAdvice" pointcut-ref="mybatis_operations"/>
</aop:config>

2.@Transactional
1번과 같이 일괄적으로 적용하는 방식은 대부부 상황에 잘 들어맞는다. 하지만 클래스나 메소드마다 제각각 속성일 경우 이름 패턴을
통해 적용하는건 적합하지 않다. 이때 직접 타깃에 어노테이션을 사용한다.
<tx:annotation-driven> : @Transactional이 붙은클래스나 인터페이스 또는 메소드를 찾아 트랜잭션 advice를 적용한다
@Target( ElementType.METHOD, ElementType,TYPE}) : 사용할 대상지정, 메소드와 타입처럼 한개이상 지정가능
@Retention( RetentionPolicy.RUNTIME ) 애노테이션 정보가 언제까지 유지되는 지를 지정한다. 런타임때도 정보 얻을수 있다.
@Inherited 상속을 통해서도 애노테이션 정보 얻을 수 있다. 
public @interface Transactional {
Isolation isolation() default Isolation.DEFAULT;
boolean readOnly() default false;
}

@Transactional은 동시에 포인트컷의 자동등록에서 사용된다. 

DefaultTransaction이 구현하고 있는 TransactionDefinition 인터페이스는 트랜잭션의 동작방식에 영향을 줄수있는 네가지 속성을 정의하고 있다.

전파방식( PROPAGATION )
 -트랜잭션의 경계에서 이미 진행중인 트랜잭션이 있을때 또는 없을때 어떻게 동작할 것인가를 결정하는 방식 
 예를 들면 A의 트랜잭션이 시작돼서 진행중이라면 B의 코드는 어떤 트랜잭션에서 동작해야할까.. 그리고 어딘가에 참여하여 예외발생시 두 트랜잭션은
 어떻게 되는 것인가.
 getTransaction() 메소드는 항상 트랜잭션을 시작하는 것이 아니라 전파속성과 현재 진행중인 트랙잭션의 존재여부로 반환값을 정한다.

 -REQUIRED : default 이미시작된 트랜잭션이 있으면 없으면 새로 시작한다. 하나의 트랜잭션이 시작된 후에 다른 트랜잭션 경계가 설정된 메소드를 호출하면 자연스럽게 하나가 된다. (가장 많이 사용 default) A, B , A>B B>A 모두 가능하다. 
 -SUPPORTS : 이미 시작된 트랜잭션이 있으면 참여하고 없으면 없이 진행한다. connection, 하이버네이트는 공유가능하다.
 -MANDATORY : 이미시작된 트랜잭션이 있으면 없으면 예외발생, 
 -REQUIRES_NEW : 항상 새로운 트랙잭션 시작한다. 항상 독자적인 트랜잭션이다.
 -NOT_SUPPORTED : 사용하지않는다.( aop를 통해 한번에 적용하고 특정메소드가 적용되지않게 예외처리 )
 -NEVER : 트랜잭션을 사용하지 않도록 강제한다. 이미 진행중인 트랜잭션이 있다면 예외발생
 -NESTED : 이미 진행중인 트랜잭션이 있다면 중첩 트랜잭션을 사용한다. 독립된트랜잭션이 아닌 트랜잭션 안에 다시 트랜잭션을 만드는 것이다. 로그 트랜잭션과 메인 트랜잭션중 부모 자식관계를 만들어 로그는 실패해도 메인트랜잭션은 커밋될수있도록 함

격리수준( IOSLATION )
동시에 여러 트랜잭션이 진행될때 트랜잭션이 작업결과를 다른 트랜잭션에게 어떻게 노출할 것인지 결정
 -DEFAULT : 사용하는 데이터엑서스 기술에 따름 
 -READ_UNCOMMITTED : 하나의 트랜잭션이 커밋되기전에 그 변화가 다른 트랜잭션에 노출 , 빠르나 데이터 일관성이 떨어짐
 -READ_COMMITTED :  많이 사용되는 격리수준,  커밋하지 않으면 정보를 읽어올수없다.  하지만 읽는 로우는 다른 트랜잭션이 수정가능하다.  SELECT이후 변경된 ROW 가능
 -REPEATABLE_READ : 읽은 로우를 다른 트랙잭션이 수정할수없다 하지만 추가는 가능하다. SELECT이후 추가된 ROW가능
 -SERIALIZABLE : 동시에 같은 테이블 접근 불가능. 격리수준은 높으나 성능이 가장 떨어지므로 극단적인 안정작업만 추천

제한시간( TIME OUT )
-제한시간을 지정하여 예외 발생가능, 기본설정은 없다. 

읽기전용( READ-ONLY )
-읽기전용으로 쓰기 방지

트랜잭션 정의를 수정하려면 TransactionDefinition 오브잭트를 생성하고 사용하는 코드는 트랜잭션 경계설정 기능을 가진 TransactionAdvice다.
하지만 이 속성을 변경하면 모든 트랜잭선이 변경된다. 
원하는 메소드만 선택하여 정의를 변경할수있다. 어드바이스의 기능을 확장하면 된다. 
메소드 이름 패턴에 따라 다른 트랜잭션 정의가 적용되도록 만들면 되는데 TransactionInterceptor를 사용한다.

<tx:advice> : TransactionInterceptor 빈이 등록된다. 
<tx:attributes> :  개별 어트리뷰트를 등록하여 지정할수있다. 
롤백 예외
-런타임 예외시 롤백한다. 
-런타임 예외시 말고 롤백이 필요하다면 rollback-for, rollbackForClassName을 사용한다. 


참고: 토비의 스프링














'STUDY > SPRING' 카테고리의 다른 글

DATA Access  (0) 2018.11.08
Aspect Oriented Programming  (0) 2018.11.06
MVC  (0) 2018.11.02
[코드로 배우는 스프링 웹프로젝트]  (0) 2018.10.31
Annotation  (0) 2018.10.30


실행순서 
    0.Filter 모든 요청에 걸러져서 dispatcherServlet으로 들어간다. 
    1.웹 브라우저를 통해 클라이언트 요청이 dispatcherServlet에게 들어온다. 
    2.dispatcherServlet은 HandlerMappging에게 어떤 컨트롤러가 처리할지 결과를 받아온다.
    3.HandlerAdapter는 DispatcherServlet의 처리 요청을 변환해서 컨트롤러에게 전달하고 컨트롤러의 응답 결과를 DispatcherServlet 요구하는 결과 방식으로 변환한다. 
      웹 브라우저, 캐시들도 설정한다.
    4.Controllers는 클라이언트 요청을 처리한 뒤 결과를 DispatcherServlet에게 리턴한다. 
    5.HandlerAdapter는 controller의 실행결과를 ModelAndView로 변환해서 DispatcherServlet에게 리턴한다. 
    6.DispatcherServlet은 viewResolver에게 컨트롤러의 처리결과를 보여줄 view를 결정한다. 
    7.해당 view에게 처리결과 화면을 생성한다. jsp나 velocity 탬플리 파일을 이용해 응답 결과를 전송한다. 

1.Filter
- 공통적인 업무(로그인, 로그, 권한체크...)를 할때 공통관심으로 분리하는데 이떄 사용할 수 있는 것이 서블릿 필터,스프링 프레임워크를 사용하면 interceptor, AOP가 있다.  
이 세가지의 실행시점은 서버실행시 서블릿이 올라오는 동안 init이 실행되고 filter가 실행,  컨트롤러가 실행되기전에 intercepter의 preHandler, 그리고
AOP의 작업 후에 역으로 postHandler, atfer Completion, filter로 진행되고  destory 된다. 
 init(), doFilter(), destory()
필터는 스프링과 무관하게 지정된 자원에 대해 동작한다.  즉 스프링 외부에 존재한다. 
그래서 servlet에서 처리하기 전후에 사용되기 편하며 servletRequest 혹은 response를 교체하는것이 가능하다. 
web.xml에 설정한다. 
*interceptor의 차이점
실행시점에 속하는 영역(Context)이다. intercepter는 스프링내부에 존재하며 dispatcherServlet이 컨트롤 호출 전후로 작동한다.
preHandler(), postHandler(), afterCompletion()
예외처리가 spring annotation으로 작동하므로 관리의 편의성이 있다. 
     <mvc:interceptors>
             <mvc:interceptor>
                    <mvc:mapping path="/**"/>
                    <bean class="className"/>
             </mvc:interceptor>
     </mvc:interceptors>

*AOP와 interceptor의 차이점
spring의 AOP은 joinPoint등을 활용해서 호출대상이 되는 메소드의 파라미터를 받는 구조이나 intercepter는 filter와 유사하게 HttpServletRequest, httpServletRequest로 파라미터를 받는 구조이다. 
포인트 컷으로 @after, @Before.... 
예외처리가 spring annotation으로 작동하므로 관리의 편의성이 있다. 
    <filter>
             <filter-name>encodingFilter</filter-name>
              <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
             <init-param>
                    <param-name>encoding</param-name>
                    <param-value>UTF-8</param-value>
             </init-param>
    </filter>
    <filter-mapping>
            <filter-name>encodingFilter</filter-name>
            <url-pattern>/*</url-pattern>
    </filter-mapping>

2.dispatcherServlet 
컨테이너를 생성한다.
web.xml을 먼저 읽어와  [서블리이름]-servlet.xml 파일의 정보로 설정한다. 이떄 servlet.xml 형식이 아닌 다른 파일을 사용했다면 contextConfigLocation 태그로 설정을 변경하며
xml이 아닌 annotation(@Configuration)으로 설정했다면 contextClass를 AnnotationConfigWebApplicationtext를 xml에 추가해주고 contextConfigLocation에 해당 시작 클래스를 등록해야한다. 
   <servlet>
             <servlet-name>dispatcher</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
             <init-param>
                    <param-name>contextConfigLocation</param-name>
                    <param-value>classpath:spring/context-*.xml</param-value>
             </init-param>
             <load-on-startup>1</load-on-startup>
   </servlet>
3.handlerMapping & handlerAdapter
스프링은 Url이 들어오면 어떤 컨트롤러 오브젝트가 이를 처리하게 할지 매핑해주는 전략을 만들어 DI로 제공한다.
 오브젝트 어댑터 패턴을 사용해서 특정 컨트롤러를 호출해야 할때는 해당 컨트롤러 타입을 지원하는 어댑터를 중간에 껴서 호출하는것이다.  DIspatcherServlet은 컨트롤러가 어떤 메소드를 가졌고 어떤 인터페이스를 구현했는지 전혀 알지못한다. 대신 컨트롤러의 종류에 따라 적절한 어뎁터를 사용한다. 이렇게 하면 하나의 dispatcherServlet이 동시에 여러가지 타입이 컨트롤러를 사용할수있다. 

dispathcherServlet은 컨트롤러를 직접 실행하지 않고 HandlerAdapterf를 사용하여 간접적으로 컨트롤러을 실행하는데 실제 반환하는 핸들러의 객체 타입이 무엇인지 신경쓰지않는다. ModelAndVIew가 아닌 string 타입이 될수도 있다. 이때 중간에서 string을 modelAndview로 변경해주어야 하는데 그때 사용되는 것이 HandlerAdapter이다 
<mvc:annotatinon-driven>설정이나 @EnaleWebMvc 애노테이션을 사용하면 handlerMapping과 HandlerAdapter를 등록한다.
 RequestMappingHandlerMapping  : @controller 적용 빈객체를 핸들러로 사용하는 HandlerMapping구현, 적용 우선순위가 높다.
 SimpleUrlHandlerMapping :  <mvc:default-servlet-handler> <mvc:view-controller> <mvc:resources> 태그 사용 
 RequestMappingHandlerAdapter:  RequestMappingHandlerMapping 에 대한 어뎁터
 HttpRequestHandlerAdaper: HttpRequestHandler에 대한 어뎁터
 SimpleControllerHandlerAdapter: Controller 인터페이스를 구현한 객체에 대한 어뎁터 

4.viewResolver
컨트롤러가 지정한 뷰이름으로부터 응답 결과 화면을 생성하는 view객체를 구현할때 사용 
주요 구현클래스
  internalResourceViewResolver  / velocityViewResolver / velocityLayoutViewResolver / BeanNameViewResolver 
  servlet-context.xml 의 internalResourceViewResolver 
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
     <beans:property name="prefix" value="/WEB-INF/views/" />
     <beans:property name="suffix" value=".jsp" />
</beans:bean>
 servlet-context.xml 의 BeanNameViewResolver 
 bean에서 BeanNameViewResolver 등록하고 bean의 id가 download인 클래스를 등록하면 
 return new ModelAndVIew( "download", "downloadFile", downloadFile )이 실행가능하다. 
 이름이 download인 DownloadVIew객체를 뷰로 검색하며 DIspatcherServlet은 downloadView를 이용해 DownLoadController의 처리결과를 출력하게 된다. 
 다수의 viewResolver는 우선순위 설정할것
      <bean id="internalResource" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
             <property name="prefix" value="/application/" />
             <property name="suffix" value=".jsp" />
             <property name="order" value="2"/>
      </bean>
      <bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
 <property name="order" value="1"/>
      </bean>
order이 작을수록 우선순위가 높으며 우선순위가 높은 viewResolver가 null을 리턴하면 다음 우선순위를 찾는다. 이떄 internalResoureceviewResolver는 항상 뷰 이름에 매핑되는 뷰 객체를 리턴하기때문에 null을 리턴할수없다. internalResoureceviewResolver후에 지정된 viewResolver는 탐색하지않고 없을경우 404에러를 출력한다. 따라서 internalResoureceviewResolver는 마지막 우선순위를 지정하도록 해야한다. 

뷰 생성까지 모든 작업을 마쳤으면 dispatchServlet은 HttpServletResponse에 담긴 최종 결과를 서블릿 컨테이너에게  돌려준다.
그리고 이 컨테이너는 httpServletResponse에 담긴 정보를 Http응답으로 만들어 사용자의 브라우저나 클라이언트에게 전송하고 작업을 종료한다. 












'STUDY > SPRING' 카테고리의 다른 글

Aspect Oriented Programming  (0) 2018.11.06
TRANSACTION  (0) 2018.11.06
[코드로 배우는 스프링 웹프로젝트]  (0) 2018.10.31
Annotation  (0) 2018.10.30
스프링 컨테이너와 빈  (0) 2018.10.30

+ Recent posts