본문 바로가기

programming/Gukbi

국비 교육 85일차 - transaction , AOP 프로그램

728x90
반응형

한번에 여러가지 이벤트가 동시에 처리 되는 것을 transaction 프로그램이라고 한다. 한 이벤트에 에러가 있으면 모든 이벤트가 전부 처리되지 않는 프로그램이다. 

 

package com.sist.dao;

import org.springframework.stereotype.Repository;
import java.util.*;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import java.sql.*;
@Repository
public class MemberDAO {
	private Connection conn;
	private PreparedStatement ps;
	private final String URL="jdbc:oracle:thin:@localhost:1521:XE";
	// 객체 생성시에 호출되는 메소드 지정 => XML(init-method)
	@PostConstruct
	public void init()
	{
		try
		{
			Class.forName("oracle.jdbc.driver.OracleDriver");
		} catch (Exception e) {
			
		}
	}
	// 객체가 메모리에서 삭제 메소드 지정 => XML(destroy-method)
	@PreDestroy
	public void close()
	{
		try
		{
			if(ps!=null) ps.close();
			if(conn!=null) conn.close();
		} catch (Exception e) {
			
		}
	}
	
	public void getConnection()
	{
		try
		{
			conn=DriverManager.getConnection(URL,"hr", "happy");
		}catch(Exception ex){}
	}
	
	public void disConnection()
	{
		try
		{
			if(ps!=null) ps.close();
			if(conn!=null) conn.close();
		} catch (Exception e) {
			
		}
	}
	// 기능 설정 (DML 여러개 있는 경우) = 동시에 처리 (트랜잭션)
	public void memberInsert()
	{
		try
		{
			getConnection();
			String sql="INSERT INTO spring_member VALUES(1,'홍길동', '남지')";
			ps=conn.prepareStatement(sql);
			ps.executeUpdate(); // commit
			
			sql="INSERT INTO spring_member VALUES(1,'심정이', '여자')";
			ps=conn.prepareStatement(sql);
			ps.executeUpdate(); // catch => rollback
			
			
		}catch(Exception ex)
		{
			ex.printStackTrace();
		}
		finally
		{
			disConnection();
		}
	}
	// 트랜잭션 처리
	public void memberInsert2()
	{
		try
		{
			getConnection(); // Before
			conn.setAutoCommit(false); // commit을  해제
			
			
			String sql="INSERT INTO spring_member VALUES(1,'홍길동', '남지')";
			ps=conn.prepareStatement(sql);
			ps.executeUpdate(); // commit(X)
			
			sql="INSERT INTO spring_member VALUES(1,'심정이', '여자')";
			ps=conn.prepareStatement(sql);
			ps.executeUpdate(); // commit(X)
			
			conn.commit(); // around
			
		} catch (Exception e) {
			e.printStackTrace();
			try
			{
				conn.rollback();  // 전체 취소 afterThrowing
			}catch(Exception ex){}
		}
		finally
		{
			try
			{
				conn.setAutoCommit(true); // AfterThrowing
			}catch(Exception ex){}
			disConnection();
		}
	}
}

 java에서는 sql문장을 실행하면 자동으로 commit이 되기 때문에 autocommit을 해제한다

만약 에러가 생겨서 catch절을 수행하면 거기서 트랜잭션을 취소할 수 있는 rollback() 명령을 내려준다. 

 

트랜잭션 프로그램이 적용될 수 있는 곳은 게시판 삭제 등이 있다. 

게시판을 삭제하려면 관련 댓글 정보까지 전부 한번에 삭제처리 되어야하기 때문이다. 

 

 

그리고 AOP 프로그램 

목적은 반복되는 메소드를 모아서 한번에 처리한다 -> 유지보수가 편리해진다, 코드의 효율성이 좋아진다

위 프로그램의 실행결과를 보면 오라클 연결과 해제가 계속해서 반복되는것을 볼 수 있다.

바로 저 부분을 공통으로 묶어 AOP로 처리해줄 수 있다.

 

 

일단 AOP를 사용하려면 spring container에 등록을 먼저 해야한다. 

<?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"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:p="http://www.springframework.org/schema/p"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

   <context:component-scan base-package="com.sist.*"/>
   <aop:aspectj-autoproxy/>
   
</beans>

aop:aspectj-autoproxy를 꼭 써줘야 aop가 사용가능하다

 

/*
 *    AOP (횡단지향프로그램) : 분석이 어렵다  => 스프링에서 지원하는 로그파일,트랜잭션 , 보안 (AOP)
 *    
 *    public void insert()
 *    {
 *       getConnection();
 *       try
 *       {
 *           conn.setAutoCommit(false);
 *           INSERT문장 실행
 *           INSERT문장 실행
 *           conn.commit();
 *       }catch(Exception ex)
 *       {
 *           conn.rollback();
 *           ex.printStackTrace();
 *       }
 *       finally
 *       {
 *          conn.setAutoCommit(true)
 *          disConnection();
 *       }
 *       
 *    }
 *    
 *    @Transactional
 *    public void insert()
 *    {
 *           getConnection();
 *           INSERT문장 실행
 *           INSERT문장 실행
 *           disConnection();
 *    }
 *    
 *    1. JoinPoint
 *         어디에서 호출 
 *         BEFORE
 *         public void display()
 *         {
 *             ====> Before
 *             try
 *             {
 *             
 *             }catch(Exception ex)
 *             {
 *             
 *             }
 *         }
 *         AFTER
 *         public void display()
 *         {
 *             
 *             try
 *             {
 *             
 *             }catch(Exception ex)
 *             {
 *             
 *             }
 *             finally
 *             {
 *                ==> After
 *             }
 *         }
 *         AFTERRETURNING
 *         public void display()
 *         {
 *             
 *             try
 *             {
 *             
 *             }catch(Exception ex)
 *             {
 *             
 *             }
 *             finally
 *             {
 *                ==> After
 *             }
 *             
 *             return => 정상수행 => return값을 받을 수 있다 
 *         }
 *         AFTERTHROWING
 *         public void display()
 *         {
 *             
 *             try
 *             {
 *             
 *             }catch(Exception ex)
 *             {
 *                => 에러발생시 처리 => 에러내용을 받아 볼 수 있다 
 *             }
 *             finally
 *             {
 *                ==> After
 *             }
 *         }
 *         AROUND
 *         public void display()
 *         {
 *             
 *             try
 *             {
 *                ==============
 *                  소수 코딩 
 *                ==============
 *             }catch(Exception ex)
 *             {
 *             
 *             }
 *             finally
 *             {
 *                ==> After
 *             }
 *         }
 *    2. PointCut
 *         어떤 메소드가 호출될때 적용 
 *    3. Advice
 *         JointPoint+PointCut
 *    4. Aspect
 *         Advice여러개를 가지고 있는 클래스 
 */

 어디서 aop가 실행되는지에 따라 jointpoint가 달라진다.

 

사용자 정의 메소드 앞에서 실행되면 : before

사용자 정의 메소드 앞에서 실행되면 : after

사용자 정의 메소드 앞뒤에서 실행되면 : around

catch절 안에서 실행되면 (에러 발생시에 실행) : AfterThrowing

 

pointcut은 어떤 메소드가 호출될때 실행될 것인지를 정해준다.

그리고 jointPoint+pointCut을 합친것을 Advice라고 하고, 

여러 Advice를 모아 놓은 클래스를 Aspect라고 한다. 

 

그럼 Aspect 파일을 한 번 보겠다. 

 

package com.sist.aop;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
// 메모리 할당이 아니라 => 공통으로 사용되는 메소드를 모아두는 곳

import com.sist.dao.BoardDAO;
@Aspect
@Component
public class MyAspect {
	@Autowired
	private BoardDAO dao;
	
	@Before("execution(* com.sist.dao.BoardDAO.aop*(..))")
	/*
	 * 	* com.sist.dao.BoardDAO.aop*(..)
	 * == 
	 * 리턴형 (모든 리턴형)
	 */	
	public void getConnection()
	{
		dao.getConnection();
	}
	
	@After("execution(* com.sist.dao.BoardDAO.aop*(..))")
	public void disConnection()
	{
		dao.disConnection();
	}
}
@After - jointPoint
"execution(* com.sist.dao.BoardDAO.aop*(..))" - pointCut

 이렇게 나눠서 보면 된다. 

 

그리고 aop를 적용해서 일부러 에러가 나는 소스코드를 작성해봤다

package com.sist.manager;

import org.springframework.stereotype.Component;

@Component
public class MyManager {
   public void display()
   {
	   int i=10/0;
	   System.out.println("i="+i);
   }
}

0으로 나누면 당연히 에러를 실행하는게 맞는데, aop에서 경고메세지를 날려줄 수 있다. 

 

package com.sist.aspect;

import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MyAspect {
   // 에러가 난 경우 
   @AfterThrowing(value="execution(* com.sist.manager.MyManager.display())", throwing="ex")
   public void afterThrowing(Exception ex)
   {
	   System.out.println("AOP에서 감지된 에러:"+ex.getMessage());
   }
}

 에러가 난 경우 에러메세지를 출력할 수 있도록 만든 코드이다. 

 

 

728x90
반응형