ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [SPRING]어노테이션 기반 AOP 설정하기 (.)
    SPRING 2021. 8. 25. 17:47

     

     

    이제는 CONTEXT:COMPONENET-SCAN 속성을 이용하여 어노테이션 기반 AOP를 설정하자.

     

     

     

     

    package com.springbook.biz.board;
    
    import java.util.List;
    
    public interface BoardService {
    	
    	void insertBoard(BoardVO vo);
    	void update(BoardVO vo);
    	void delete(BoardVO vo);
    	BoardVO get(BoardVO vo);
    	List<BoardVO> getList(BoardVO vo);	
    }
    package com.springbook.biz.board;
    
    import java.sql.Date;
    
    public class BoardVO {
    	private int seq;
    	private String title;
    	private String writer;
    	private String content;
    	private Date regdate;
    	private int cnt;
    	public int getSeq() {
    		return seq;
    	}
    	public void setSeq(int seq) {
    		this.seq = seq;
    	}
    	public String getTitle() {
    		return title;
    	}
    	public void setTitle(String title) {
    		this.title = title;
    	}
    	public String getWriter() {
    		return writer;
    	}
    	public void setWriter(String writer) {
    		this.writer = writer;
    	}
    	public String getContent() {
    		return content;
    	}
    	public void setContent(String content) {
    		this.content = content;
    	}
    	public Date getRegdate() {
    		return regdate;
    	}
    	public void setRegdate(Date regdate) {
    		this.regdate = regdate;
    	}
    	public int getCnt() {
    		return cnt;
    	}
    	public void setCnt(int cnt) {
    		this.cnt = cnt;
    	}
    	@Override
    	public String toString() {
    		return "BoardVO [seq=" + seq + ", title=" + title + ", writer=" + writer + ", content=" + content + ", regdate="
    				+ regdate + ", cnt=" + cnt + "]";
    	}	
    }
    package com.springbook.biz.board.impl;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.springframework.stereotype.Repository;
    
    import com.springbook.biz.board.BoardVO;
    import com.springbook.biz.common.JDBCUtil;
    
    @Repository("boardDAO") 
    public class BoardDAO {
    	
    	private Connection conn = null;
    	private PreparedStatement pstmt = null;
    	private ResultSet rs = null;
    	
    	public void insertBoard(BoardVO vo) {
    		System.out.println("insertBoard()기능처리");
    		String sql = "insert into board1(seq,title,writer,content) values((select nvl(max(seq),0)+1 from board1),?,?,?)";
    		
    		try {
    			conn = JDBCUtil.getConnection();
    			pstmt = conn.prepareStatement(sql);
    			pstmt.setString(1, vo.getTitle());
    			pstmt.setString(2, vo.getWriter());
    			pstmt.setString(3, vo.getContent());
    			pstmt.executeUpdate();
    			
    		}catch(Exception e) {
    			e.printStackTrace();
    		}finally {
    			JDBCUtil.close(pstmt,conn);
    		}
    	}
    	
    	public void update(BoardVO vo) {
    		System.out.println("update() 실행");
    		String sql = "update board1 set title=?,writer=? where seq=?";
    		
    		try {
    			conn = JDBCUtil.getConnection();
    			pstmt = conn.prepareStatement(sql);
    			pstmt.setString(1, vo.getTitle());
    			pstmt.setString(2, vo.getWriter());
    			pstmt.setInt(3, vo.getSeq());
    			pstmt.executeUpdate();
    		}catch(Exception e) {
    			e.printStackTrace();
    		}finally {
    			JDBCUtil.close(pstmt, conn);
    		}
    	}
    	
    	public void delete(BoardVO vo) {
    		System.out.println("delete() 실행");
    		String sql = "delete from board1 where seq=?";
    		
    		try {
    			conn = JDBCUtil.getConnection();
    			pstmt=conn.prepareStatement(sql);
    			
    			pstmt.setInt(1, vo.getSeq());
    			pstmt.executeUpdate();
    		}catch(Exception e) {
    			e.printStackTrace();
    		}finally {
    			JDBCUtil.close(pstmt, conn);
    		}
    	}
    	
    	public BoardVO get(BoardVO vo) {
    		System.out.println("get() 실행");
    		String sql = "select * from board1 where seq=?";
    		BoardVO vo3 = null;
    		try {
    			conn=JDBCUtil.getConnection();
    			pstmt=conn.prepareStatement(sql);
    			pstmt.setInt(1, vo.getSeq());
    			rs=pstmt.executeQuery();
    			
    			if(rs.next()) {
    				vo3 = new BoardVO();
    				vo3.setCnt(rs.getInt("cnt"));
    				vo3.setContent(rs.getString("content"));
    				vo3.setRegdate(rs.getDate("regdate"));
    				vo3.setSeq(rs.getInt("seq"));
    				vo3.setTitle(rs.getString("title"));
    				vo3.setWriter(rs.getString("writer"));
    			}
    			}catch(Exception e) {
    				e.printStackTrace();
    			}finally {
    				JDBCUtil.close(rs,pstmt, conn);
    			}			
    		return vo3;
    		
    	}
    	
    	public List<BoardVO> getList(BoardVO vo){
    		System.out.println("getList() 실행");
    		List<BoardVO> list = new ArrayList<BoardVO>();
    		String sql = "select * from board1";
    		
    		
    		try {
    			conn=JDBCUtil.getConnection();
    			pstmt = conn.prepareStatement(sql);
    			rs=pstmt.executeQuery();
    			
    			while(rs.next()) {
    				BoardVO bvo = new BoardVO();
    				bvo.setCnt(rs.getInt("cnt"));
    				bvo.setContent(rs.getString("content"));
    				bvo.setRegdate(rs.getDate("regdate"));
    				bvo.setSeq(rs.getInt("seq"));
    				bvo.setTitle(rs.getString("title"));
    				bvo.setWriter(rs.getString("writer"));
    				list.add(bvo);
    			}
    			
    		}catch(Exception e) {
    			e.printStackTrace();
    		}finally {
    			JDBCUtil.close(rs,pstmt, conn);
    		}		
    		return list;		
    	}	
    }
    package com.springbook.biz.board.impl;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.springbook.biz.board.BoardService;
    import com.springbook.biz.board.BoardVO;
    import com.springbook.biz.common.LogAdvice;
    
    @Service("boardservice")
    public class BoardServiceImpl implements BoardService {
    	
    	@Autowired  
    	private BoardDAO boardDAO;
    	
    	
    	@Override
    	public void insertBoard(BoardVO vo) {
    		boardDAO.insertBoard(vo);
    	}
    	
    	@Override
    	public void update(BoardVO vo) {
    		boardDAO.update(vo);		
    	}
    
    	@Override
    	public void delete(BoardVO vo) {
    		boardDAO.delete(vo);		
    	}
    
    	@Override
    	public BoardVO get(BoardVO vo) {
    		return boardDAO.get(vo);
    	}
    
    	@Override
    	public List<BoardVO> getList(BoardVO vo) {
    		return boardDAO.getList(vo);
    	}
    
    }
    <?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:aop="http://www.springframework.org/schema/aop"
    	xsi:schemaLocation="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.3.xsd
    		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
    	<context:component-scan base-package="com.springbook.biz"></context:component-scan>
    
    	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>
    package com.springbook.biz.board;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Scanner;
    
    import org.springframework.context.support.AbstractApplicationContext;
    import org.springframework.context.support.GenericXmlApplicationContext;
    
    
    public class BoardServiceClient {
    
    	public static void main(String[] args) {
    		
    		AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
    		BoardService bs = (BoardService) factory.getBean("boardservice");
    		BoardVO vo = new BoardVO();
    		Scanner sc = new Scanner(System.in);
    		Boolean test = true;
    		List<BoardVO> list = new ArrayList<>();
    		
    		while(test) {
    			System.out.println("1.글추가 2.전체글조회 3.특정글조회 4.글정보수정 5.글삭제 6.프로그램종료");
    			System.out.println("메뉴선택 >>");
    			int num = sc.nextInt();
    			
    			switch(num) {
    				case 1 :
    					System.out.println("등록할 글 정보를 입력하시오. \n글제목 : ");
    					vo.setTitle(sc.next());
    					System.out.println("작성자 : ");
    					vo.setWriter(sc.next());
    					System.out.println("글내용 : ");
    					vo.setContent(sc.next());
    					
    					bs.insertBoard(vo);
    					
    					list = bs.getList(vo);
    					for(int i = 0 ; i<list.size();i++) {
    						System.out.println(list.get(i).toString());
    					}
    					continue;
    				case 2:
    					
    					list = bs.getList(vo);
    					for(int i = 0 ; i<list.size();i++) {
    						System.out.println(list.get(i).toString());
    					}
    					
    					
    					continue;
    				case 3:
    					System.out.println("특정번호 입력");
    					vo.setSeq(sc.nextInt());
    					
    					System.out.println(bs.get(vo).toString());
    					
    					continue;
    				case 4:
    					System.out.println("업데이트 원하는 번호 입력");
    					vo.setSeq(sc.nextInt());
    					
    					System.out.println("수정 타이틀 입력");
    					vo.setTitle(sc.next());
    					
    					System.out.println("수정 내용 입력");
    					vo.setContent(sc.next());
    					
    					bs.update(vo);
    					
    					list = bs.getList(vo);
    					for(int i = 0 ; i<list.size();i++) {
    						System.out.println(list.get(i).toString());
    					}
    					continue;
    				case 5:
    					System.out.println("지우고싶은 번호 입력");
    					vo.setSeq(sc.nextInt());
    					bs.delete(vo);
    					continue;
    				case 6:
    					System.out.println("프로그램 종료");
    					test = false;
    					factory.close();
    					break;
    					
    			}
    		}}}

     

     

     

     

    * 어드바이스 메소드 사용을 위해 어드바이스 객체가 필요하므로 클래스 위에 @Service 어노테이션을 작성한다.

    (@Component도 객체 생성은 되지만, 어드바이스는 비즈니스 로직 중 하나이므로 @Service를 쓴다)

     

    * 그리고 컨테이너가 어드바이스 클래스를 aspect 클래스로 인식하려면 @Aspect 어노테이션이 필요하다.

    @Aspect는 pointcut과 advice의 결합이다. 따라서 @Aspect 어노테이션이 작성된 클래스에는 pointcut과 advice와 관련된 코드가 있어야한다.

     

    * 위빙을 하기 위해서는 pointcut이 필요하다. 참조 메소드(구현 로직이 없는 메소드) 위에 @Pointcut 어노테이션을 작성한다.

     

    * 각 어드바이스에 적합한 메소드를 구현하면 된다. 어드바이스 메소드는 다음과 같다.

     @Before : 어드바이스 타겟 메소드가 호출되기 전 어드바이스 기능을 수행

     @After : 타켓 메소드의 결과가 성공,예외 등 상관없이 타겟 메소드가 완료되면 어드바이스 기능을 수행

     @AfterReturning(정상적 반환 이후) : 타켓 메소드가 성공적으로 결과값을 반환한 후에 어드바이스 기능을 수행. 비즈니스 메소드 수행 후 반환되는 바인드 변수를 통해 얻어내야하기 때문에 pointcut="참조메소드", returning="바인드 변수" 꼴로 작성되고, 매개변수로 선언해야한다.

     @AfterThrowing(예외 발생 이후) : 타겟 메소드가 수행 중 예외를 던지게 되면 어드바이스 기능을 수행. 비즈니스 메소드 실행 도중 발생하는 예외를 받아야하기 때문에 pointcut="참조메소드", returning="바인드변수" 꼴로 작성되고, 매개변수로 선언해야한다.

     @Around는 JoinPoint가 아니라 ProceedingJoinPoint를 사용하여 AOP가 적용된 메소드의 시작부터 끝까지 과정을 관리 (어드바이스 타겟 메소드를 감싸서 타겟 메소드 호출 전과 후에 어드바이스 기능을 수행)

     

     

     

     

     

     

     @Before

    package com.springbook.biz.common;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Service;
    
    @Service
    @Aspect
    public class BeforeAdvice {
    	@Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
    	public void allPointcut() {}
    	
    	@Before("allPointcut()")
    	public void beforeLog() {
    		System.out.println("[사전처리]비즈니스 로직 수행 전 동작");
    	}
    }

     

    @After

    package com.springbook.biz.common;
    
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Service;
    
    @Service
    @Aspect
    public class AfterAdvice {
    	
    	@Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
    	public void allPointcut() { }
    	
    	@After("allPointcut()")
    	public void finallyLog() {
    		System.out.println("[사후처리] 비즈니스 로직 수행 후 무조건 동작");
    	}
    }

     

    @AfterReturning

    package com.springbook.biz.common;
    
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Service;
    
    @Service
    @Aspect
    public class AfterReturningAdvice {
    
    	@Pointcut("execution(* com.springbook.biz..*Impl.get*(..))")
    	public void getPointcut() { }
        
    	@AfterReturning(pointcut="getPointcut()", returning="returnObj")
    	public void AfterLog(Object returnObj) {
    		System.out.println("[사후처리] 비즈니스 로직 수행 후 동작");
    		System.out.println("[사후처리]" + returnObj.toString() +"\n");
    	}
    }

     

    AfterReturning에 대해서 더 찾아봤다.

     

    JointPoint 클래스를 advice 메소드의 parameter로 설정하여 호출 대상 메소드의 정보를 받아온다.

    java relect를 사용하기 때문에 해당 클래스에 관련 메타데이터 정보를 JoinPoint에서 가져올 수 있다.

    getSignature 메소드를 통해 메소드 이름 정보를 받아올 수 있다.

    getArgs 메소드를 통해 타겟 메소드의 argument를 받아올 수 있고 받아온 argument는 Object 배열로 받아와 세부정보를 사용할 수 있다.

    ...getArgs 이용방법을 모르겠다.

     

    package com.springbook.biz.common;
    
    import java.lang.reflect.Method;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Service;
    
    @Service
    @Aspect
    public class AfterReturningAdvice {
    
    
    	@Pointcut("execution(* com.springbook.biz..*Impl.get*(..))")
    	public void allPointcut() {
    		
    	}
    	@AfterReturning(pointcut="allPointcut()", returning="returnObj")
    	public void AfterLog(JoinPoint joinPoint, Object returnObj) {
    	
    		MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
    		Method method = methodSignature.getMethod();
    		System.out.println("Method : " + methodSignature.toString());
    		System.out.println("method length:" + method.getParameters().length);
    		
    		System.out.println("--Method arguments start");
    		Object[] args = joinPoint.getArgs();
    		
    		String title = null;
    		title = method.getParameters()[0].getName();
    		
    		System.out.println("title :" + title);//얻어내려면 컴파일(javac) 시점에 VM option에 -parameterㄴ를 추가해야한다. 
    		
    		System.out.println("[test]" + joinPoint.getSignature().getName());
    		System.out.println("[test]" + joinPoint.getSignature().toString());
    		
    //		String businessMethodName = joinPoint.getSignature().getName();
    //		Object[] args = joinPoint.getArgs();
    //		String info = returnObj.toString();
    //		
    //		System.out.println("[test] 비즈니스 로직 수행 후 동작");
    //		System.out.println("[test]" + returnObj.toString() +"\n");
    //		System.out.println("[test]" + joinPoint.getArgs());
    //		System.out.println("[test]" + joinPoint.getSignature().getName());
    //		System.out.println("[test]" + joinPoint.getSignature().toString());
    //		
    //		System.out.println("[test]"+args);
    	}
    }

     

     

    @AfterThrowing

    package com.springbook.biz.common;
    
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Service;
    
    @Service
    @Aspect
    public class AfterThrowingAdvice {
    	
    	@Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
    	public void allPointcut() { }
    	
    	@AfterThrowing(pointcut="allPointcut()",throwing="exceptObj")
    	public void exceptionLog(Exception exceptObj) {
    		System.out.println("[예외처리] 비즈니스 로직 수행 중 예외발생");
    		
    		if(exceptObj instanceof IllegalArgumentException) {
    			System.out.println("부적합한 값이 입력되었다");
    		}else if(exceptObj instanceof NumberFormatException) {
    			System.out.println("숫자 형식의 값이 아닙니다.");
    		}
    	}
    }

     

    @Around

    package com.springbook.biz.common;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Service;
    import org.springframework.util.StopWatch;
    
    @Service
    @Aspect
    public class AroundAdvice {
    
    	@Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
    	public void allPointcut() {
    		
    	}
    	
    	@Around("allPointcut()")
    	public Object aroundLog(ProceedingJoinPoint pjp)throws Throwable{
    		System.out.println("[BEFORE]: 비즈니스 메소드 수행 전에 처리할 내용..");
    		Object returnObj = pjp.proceed();
    		System.out.println("[AFTER]: 비즈니스 메소드 수행 후에 처리할 내용..");
    		System.out.println(returnObj);
    		return returnObj;
    		
    		return obj;
    	} 
    }

    @Around에서 StopWatch사용

    package com.springbook.biz.common;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Service;
    import org.springframework.util.StopWatch;
    
    @Service
    @Aspect
    public class AroundAdvice {
    
    	@Pointcut("execution(* com.springbook.biz..*Impl.*(..))")
    	public void allPointcut() {
    		
    	}
    	
    	@Around("allPointcut()")
    	public Object aroundLog(ProceedingJoinPoint pjp)throws Throwable{
    		
    		StopWatch stopwatch = new StopWatch();
    		stopwatch.start();
    		
    		Object obj = pjp.proceed();
    		
    		stopwatch.stop();
    		System.out.println("()메소드 수행에 걸린 시간 : " +stopwatch.getTotalTimeMillis() + "(ms)초");
    		
    		return obj;
    	} 
    }

     

     

    도움이 많이 된 https://engkimbs.tistory.com/746 님 감사합니다.

    728x90

    'SPRING' 카테고리의 다른 글

    [SPRING] pointcut 표현식  (0) 2021.08.25
    [SPRING] AOP 실습  (0) 2021.08.25
    [SPRING] aspect 환경설정와 AOP  (0) 2021.08.24
    [SPRING] 데이터 추가, 변경, 삭제  (0) 2021.08.24
    [SPRING] Spring 의존성  (0) 2021.08.21
Designed by Tistory.