SPRING

[SPRING]어노테이션 기반 AOP 설정하기 (.)

worri-pi 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