[SPRING]어노테이션 기반 AOP 설정하기 (.)
이제는 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 님 감사합니다.