-
[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