본문 바로가기
Spring/study

spring 14강 AOP와 트랜잭션 처리 실습

by avvin 2019. 6. 28.

spring 14강 AOP와 트랜잭션 처리 실습



AOP 실습 예제 ) 


사용자가 메시지를 남기면 포인트 10 증가

메시지를 읽으면 포인트 5 증가

.aop  : MessageAdvice.java


.controller.message : MessageController.java / LogAdvice.java (< 로그수집 기능에 대한 내용은 13강 포스팅 참고)


.model.message.dto : UserDTO.java / MessageDTO.java


.model.message.dao : MessageDAO/Impl  /  PointDAO/Impl


.service.message : MessageService/Impl



pointMapper.xml





AOP 실습용 테이블


AOP와 트랜잭션 쿼리를 활용한 실습



aop실습.sql //다시확인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
drop table tbl_user cascade constraints;
create table tble_user(
userid varchar2(50) not null,
upw varchar2(50) not null,
uname varchar2(100) not null,
upoint number default 0,
primary key(userid)
);
 
create table tbl_message(
mid number not null,             --메시지 일련번호(PK)
targetid varchar2(50) not null--받는사람 아이디
sender varchar2(50) not null,     --보낸사람 아이디
message varchar2(4000) not null
opendate date,                     -- 열람 시간
senddate date default sysdate,     -- 보낸시간
primary key(mid)
);
 
--시퀀스 생성
create sequence message_seq
start with 1
increment by 1;
 
--제약조건 설정
alter table tbl_message add constraint fk_usertarget
foreign key (targetid) references tbl_user(userid);
 
alter table tbl_message add constraint fk_usersender
foreign key (sender) references tbl_user(userid);
 
--사용자 추가
insert into tbl_user (user_id, upw, uname) values('user00''0000''kim');
insert into tbl_user (user_id, upw, uname) values('user01''1111''park');
insert into tbl_user (user_id, upw, uname) values('user02''2222''woo');
insert into tbl_user (user_id, upw, uname) values('user03''3333''hong');
insert into tbl_user (user_id, upw, uname) values('user04''4444''hwang');
insert into tbl_user (user_id, upw, uname) values('user05''5555''chang');
 
select * from tbl_user;
 
--user02가 user00에게 메시지를 전송
insert into tbl_message(mid, targetid, sender, message)
values(message_seq.nextval, 'user00''user02''message01');
 
--user02에게 포인트 10 추가
update tbl_user set upoint=upoint+10 where userid='user02';
 
select*from tbl_user;
 
--user00의 메시지 박스 조회
select * from tbl_massage where targetid='user00';
update tbl_message set opendate = sysdate where mid =2;
select * from tbl_message;
 
--user00에게 포인트 5 추가
update tbl_user set upoint=upoint+5 where iserid='user00';
select * from tbl_user;
 
delete from tbl_message;
 
commit;
 




pointMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
<!-- mappers/message/pointMapper.xml -->
 
<!-- 다른 mapper와 중복되지 않도록 네임스페이스 기재 -->
<mapper namespace="point">
<!-- sqlSession.update("point.updatePoint", map); -->
    <update id="updatePoint">
        update tbl_user
        set upoint=upoint+#{point}
        where userid=#{userid}
    </update>
    
</mapper>
cs




트랜잭션 처리


트랜잭션 처리 대상 method에 @Transactional 처리를 해준다.


@Transactional이 붙은 메서드는 블럭 안의 모든 코드가 완료되지 않으면 rollback시킨다.


root-context.xml 파일의 네임스페이스의 tx 체크 확인


root-context.xml

1
2
3
4
5
6
7
8
    <!-- 트랜잭션 관련 설정 -->
    <bean id="transactionManager" //=변수명 class=""는 자료형
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
 
    <!-- 트랜잭션 관련 어노테이션을 자동 인식하는 옵션 -->
    <tx:annotation-driven />
cs



id는 변수 명 class는 자료형

property는 해당 클래스의 필드?(변수) , dataSource라는 다른 bean을 참조한다.

 

tx : 트랜잭션 처리 관련된 태그를 사용할 수 있는 네임스페이스 추가


root-context 상단에

1
2
3
4
5
6
7
8
9
<?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:context="http://www.springframework.org/schema/context"
    xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
cs


네임스페이스 설정 코드가 자동으로 추가된다.



MessageDAOImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.example.spring02.model.message.dao;
 
import javax.inject.Inject;
 
import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Repository;
 
import com.example.spring02.model.message.dto.MessageDTO;
import com.example.spring02.model.shop.dao.CartDAO;
@Repository //dao bean으로 등록
public class MessageDAOImpl implements MessageDAO {
 
    @Inject //의존관계 주입(Dependency Injection, DI)
    SqlSession sqlSession;
    
    @Override
    public void create(MessageDTO dto) {
        sqlSession.insert("message.create", dto);
    }
 
    @Override
    public MessageDTO readMessage(int mid) {
        return null;
    }
 
    @Override
    public void updateState(int mid) {
    }
 
}
 




MessageServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.example.spring02.service.message;
 
import javax.inject.Inject;
 
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import com.example.spring02.model.message.dao.MessageDAO;
import com.example.spring02.model.message.dao.PointDAO;
import com.example.spring02.model.message.dto.MessageDTO;
 
@Service
public class MessageServiceImpl implements MessageService {
//Inject는 각각 해야 함
    @Inject
    MessageDAO messageDao;
    @Inject
    PointDAO pointDao;
 
    //트랜잭션 처리 대상 method
    @Transactional
    @Override
    public void addMessage(MessageDTO dto) {
        //메시지를 테이블에 저장
        messageDao.create(dto);
        //메시지를 보낸 회원에게 10포인트 추가
        pointDao.updatePoint(dto.getSender(), 10); 
    }
 
    @Override
    public MessageDTO readMessage(String userid, int mid) {
        // TODO Auto-generated method stub
        return null;
    }
 
}
 



@Trancactional 는 메시지를 저장했는데, 포인트 추가에 실패하면 롤백시킴




MessageController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.example.spring02.controller.message;
 
import javax.inject.Inject;
 
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
import com.example.spring02.model.message.dto.MessageDTO;
import com.example.spring02.service.message.MessageService;
 
@RestController
@RequestMapping("messages/*"//공통적인 url mapping
public class MessageController {
    @Inject
    MessageService messageService;
    @RequestMapping(value="/", method=RequestMethod.POST)
    public ResponseEntity<String> addMessage(
            @RequestBody MessageDTO dto){
        ResponseEntity<String> entity=null;
        try {
            messageService.addMessage(dto);
            entity=new ResponseEntity<>("success",HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
            entity=new ResponseEntity<>(e.getMessage()
                    ,HttpStatus.BAD_REQUEST);
        }
        return entity;
    }
}
cs



ResponseEntity는 코드를 실행했을때 성공했는지 실패했는지 간단한 메시지와 에러코드를 전달





MessageAdvice.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.example.spring02.aop;
 
import java.util.Arrays;
 
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
 
@Component // 기타 bean
@Aspect // aop bean - 공통 업무를 지원하는 코드
public class MessageAdvice {
 
    private static final Logger logger
        =LoggerFactory.getLogger(MessageAdvice.class);
    
    @Before(
            "execution(* "
            + " com.example.spring02.service.message"
            + ".MessageService*.*(..))")    
    public void startLog(JoinPoint jp) {
        logger.info("핵심 업무 코드의 정보:"+jp.getSignature());
        logger.info("method:"+jp.getSignature().getName());
        logger.info("매개변수:"+Arrays.toString(jp.getArgs()));
    }
    
    @Around(
            "execution(* "
            + " com.example.spring02.service.message"
            + ".MessageService*.*(..) )")
    public Object timeLog(ProceedingJoinPoint pjp)
        throws Throwable {
        //호출 전(Before)
        long start=System.currentTimeMillis();
        Object result=pjp.proceed();
        //호출 후(After)
        long end=System.currentTimeMillis();
        logger.info(pjp.getSignature().getName()+":"+(end-start));
        logger.info("=================");
        return result;
    }
}



메시지 서비스 실행 전에는 로그수집, 실행 전후에 시간 체크해주는 advice 클래스


MessageService의 모든 클래스(, 모든 파라미터)가 호출될 때 마다(실행 직전에) startLog 메서드 실행

한가지 작업에 로그가 너무 많이 쌓이므로 

이전에 만든 logAdvice에 있던 로그 메서드의 @Around는 주석처리 해놓는다.



@After와 @Before는 매개변수로 JoinPoint 변수를,

@Around는 매개변수로 ProceedingJoinPoint 변수를 사용




ARC 추가하기


이번 실습에서는 뷰(view)를 별도로 만들지 않고 Advanced Rest client 확장 프로그램으로만 테스트 수행


Advanced Rest client : 크롬 확장 프로그램


구글 크롬에서 advanced rest client 서칭


ARC 추가하기

https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo





입력데이터를 json으로 보내기 위해 사용


( GET과 POST는 HTTP메서드 )


Method : POST

Request URL : http://localhost/spring02/message  (실행시 포트번호 있으면 포트까지 그대로 입력)

Body 

- Body content type : application/json

- Editor view : Raw unput



String 타입의 json형식 데이터를 보내면


MessageController 메서드 매개변수자리에서 @Requestody가 MessageDTO 타입으로 받아서 매핑시켜준다.