날아라쩡글이의 블로그입니다.
AOP만들기 본문
728x90
반응형
우리는 @Aspect을 만들면 된다.
- pom.xml에서 라이브러리를 추가한다.
- <dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency> - <dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
</dependency> - 관점지향 프로그래밍을 지원하는 라이브러리 의존성을 추가한다. aspectjrt와 aspectRutime을 다운받아준다.
- sample package로 aop파일을 만들어준다.
- package를 만들고 context-root에 등록해준다.
- namespace에서 aop를 체크하고 <aop:aspectj-autoproxy />라는 위빙을 이용해서 proxy객체로 변경해주는 작업을 선언하고
- 다음 bean class로 aop를 선언해주면 된다.
- aop를 선언할 때 Aspect는 what, where, when을 설정해주면 된다.
- loggin모든 실행전후로 실행하고 싶다면 일단 aop에 @Aspect라는 어노테이션을 부착한다.
- @Aspect
- 공통기능 적용 패키지 이다.
- 공통기능 적용 패키지에는 what(공통기능) + when(언제) + where(대상) 정보를 포함한다.
- what
- logging()메소드의 수행문을 작성한다.
- when
- @Before 어노테이션을 부착에 언제 실행될 지 작성한다.
- where
- 어디에서 실행될지 AspectJ 표현식을 이용하여 괄호 안에 작성한다.
- @Before("within(com.sample.service.*Service)")//when/where
(com.sample.service패키지안에서 Service로 끝나는 애들)AspectJ 표현식
public void logging() { //what
System.out.println("################ AOP로 로그를 출력합니다."); //What 로그를 출력하게 할꺼야
} - 주요 어노테이션
- @Aspect
- AutoProxyCreator 스프링 컨테이너는 등록된 빈 중에서 @Aspect어노테이션이 부착된 빈을 스캔해서 대상 객체를 검색하고, 대상객체의 조인포인트와 결합시킨 프록시 객체를 만들어서 스프링 컨테이너의 빈으로 등록한다.
- @Aspect
- 메소드 실행시점 어노테이션
- @Befor
- 공통기능이 대상 메소드 실행전에 실행된다.
- try에서 맨 처음을 담당한다.
- @AfterReturning
- 공통기능이 대상 메소드가 오류없이 종료된 후에 실행된다.
- try안에서 맨마지막을 담당한다.
- @AfterThrowing
- 공통기능이 대상 메소드 실행중 예외가 발생하면서 실행된다.
- throw new로 예외를 던질 때 실행된다.
- @After
- 공통기능이 대상 메소드가 오류없이 종료된 후에 실행된다.
- finally로 사용된다.
- @Around
- 공통기능이 대상 메소드 실행 전부에 실행된다.
- 선언적 트랜잭션 처리를 지원하는 TransactionManage의 구현체들이 @AroundAdvice와 유사하다
- 거의 @Around를 사용한다.
- 모든 부분에서 실행된다.
- @Befor
- 규칙 role
- @Pointcut
- 공통 기능이 적용될 규칙을 지정할 때 사용한다.
- @Pointcut
- loggin에 jointpoint를 적을 수 있다.
before와 after는 매개변수를 작성하지 않아도 되지만 - Around는 매개변수를 작성해야하고, 메소드가 반드시 존재해야한다.
- 반환값이 있으면 값이 존재해야하며 jointpoint.proceed(); 메소드를 이용해서 @AroundAdive가 적용되는 대상메소드 실행 후 반환값을 담는 변수로 값을 지정해야한다.
- proceed()로 값을 담지 않을 경우, 실행전코드, 정상종료 후 , 종료 후 코드만 실행되고 아예 기능자체가 실행되지 않으며 서버가 오류가 뜬다.
- 서비스의 메소드를 실행하기 위해서는 값을 전달하는 것이 중요하다.
- @Aspect
- jointPoint와 ProceedingJoinPoint
- 공통기능이 적용되는 지점에 대한 정보를 제공하는 객체이다.
- 대상객체, 대상메소드, 대상메소드의 매개변수 등의 정보를 조회할 수 있다.
- JointPoint는 실행싯점이 @Before, @After, @AfterReturning, @AfterThrowin인 Advice에서 사용한다.
- ProceedingJointPoint는 실행시점이 @ARound인 Advice에서만 사용한다.
- ProceedingJointPoint는 @AroundAdvice가 적용되는 대상 메소드를 실행시키는 기능이 포하모디어 있다.
- @Around("execution(* com.sample.service.*Service.*(..))")
//공통기능 정의시 특별한 제약이 없지만, Around은 제약조건이 존재한다.
public Object runningTimeCheck(ProceedingJoinPoint jointPoint) throws Throwable { //생략할수없다- //@Around를 사용하면 ProceedingJointPoint를 매개변수에 작성해주어야한다.
- throwable 예외를 받아야한다.
- Object returnValue = null;
반환타입이 Object로 값을 담는 변수를 null로 지정하여 선언한다. - try {
//@Around Advice가 적용되는 대상메소드 실행 전에 실행할 코드 작성 -@Before 싯점과 동일하다.
System.out.println("################################# @Around Advice에서 대상메소드 실행전 코드를 실행함");
returnValue = jointPoint.proceed();
//@Around Advice가 적용되는 대상메소드를 실행시키는 코드
//값이 없으면 null값이 들어간다.
//@Around Advice가 적용되는 대상메소드 정상적으로 종료된 후 실행할 코드 작성 -@AfterReturning싯점과 동일하다.
System.out.println("################################# @Around Advice에서 대상메소드 정상 종료 후 코드를 실행함");
return returnValue;
//예외가 발생하지 않으면 return한다.
}catch(Throwable e) {
//@Around Advice가 적용되는 대상메소드 실행 중 오류가 발생했을 때 실행할 코드 작성 -@AfterThrowing싯점과 동일하다.
System.out.println("################################# @Around Advice에서 대상메소드 오류발생후 코드를 실행함");
throw e;
//예외가 발생시 예외를 던져준다.
} finally {
//@Around Advice가 적용되는 대상메소드 실행 후에 실행할 코드 작성 -@After싯점과 동일하다.
System.out.println("################################# @Around Advice에서 대상메소드 종료후 코드를 실행함");
}- 대상 메소드 실행 전후에 실행할 공통기능을 구현하는 Advice 메소드
- @Around
- 반환타입은 반드시 Object로 정한다.
- 메소드 이름은 상관없다.
- 매개변수에는 반드시 ProceedingJoinPoint타입의 매개변수를 포함해야한다.
- 예외는 throwable을 던져진다.
- 대상 메소드 실행 전, 후에 실행할 공통기능임을 나타낸다.
- 서비스 내부에서 실행되는 것을 확인할 수 있다.
- stopwatch라는 객체를 사용해서 시간을 확인할 수 있다.
- StopWatch stopWatch = new StopWatch();객체를 생성
try앞에 stopWatch.start();를 호출한다.
finally마지막에 stopWatch.stop()을 호출하여
stopWatch.getTotalTimeMillis()메소드를 호출하여 밀리초로 표현할 수 있다. - 실행을 하면 첫번째는 커넥션 풀 구성을 위해서 시간이 걸리고 두번째 이후로는 똑같은 쿼리는 또 요청할 가능성이 존재하여 Oracle메모리안에 가지고 있기 때문에 몇밀리초 소모가 된다.
- 그럼이런 시간체크는 언제 할까?
- 부하테스트/통합테스트시 사용한다. step by step으로 사람의 수를 늘려서 서버의 부하테스트를 진행한다.
- 시나리오를 받고 그 시나리오에 해당하는지 확인한다
- ex)부하테스트가 잘 안되면 연말정산 때 국세청 서버가 터지는 것과 동일하게 서버가 정지될 수 있다. 테스트는 필수 이다.
- 만약, AOP가 존재하지 않았다면?
- Autowired로 Dao호출해서 입력하고, 전체적으로 Service에 입력해야한다.
- public List<Book> getAllBookList(){
StopWatch stopWatch = new StopWatch();
stopWatch.start();
List<Book> books = bookDao.getAllBooks();
stopWatch.stop();
statDao.insert("getAllBookList", stopWatch.getTotalMilis());
SQL에 Dao로 만들어서 입력해준다.
}; - 패키지가 몇백개가 된다.
- 공부를 배우는 우리마져도, 실전이면 엄청많을 것이다.
- package만 해도 몇백개가 되는데, 전체 작성하려면 복잡하고, 부하테스트가 끝나면 삭제를 해줘야한다.
- 그러나 AOP로 설정을 한다면 공통기능을 별도의 클래스로 만들수있고, 해당 메소드를 사용할 수 있고, root에서 주석처리를 하면 되니 제도로 쉽다.
- AOP로 탈부착이 쉽다. 핵심기능을 해치지 않는다.
- 공통기능 AOP는 제한적이다.
- 요청객체와 응답객체의 접근이 어렵다.
- requstContextHolder를 만들었고, 객체접근이 쉬워지라고 제공했다.
- springAOP는 만능 공통 기능으로 어렵고, 일반적으로는 트랜잭션으로 처리한다. AOP처럼 동작하고
- 인증과 인가의 경우 인터셉터와 filter로 확인 하면 된다.
- @Around
- AspectJ의 언어표현식
- execution(* com.sample.service.*Service.*(..))
- execution (반환타입/ 패키지 명을 포함한 클래스와 /메소드명)
- execution(* com.sample.dao.BookDao.get*(..))
- com.sample.dao패키지의 BookDao클래스에서 get으로 시작하는 모든 메소드가 실행될 때
- execution(* com.sample.dao.BookDao.update*(*))
- com.sample.dao패키지의 BookDao클래스애서 update로 시작하고, 매개변수 한 개 전달받는 메소드가 실행될 때
- execution(* com.sample.dao.BookDao.*(..))
- com.sample.dao패키지의 BookDao클래스에서 모든 메소드가 실행될 때
- execution(Book com.sample.dao.BookDao.*(..))
- com.sample.dao패키지의 BookDao클래스에서 반환타입이 Book객체인 모든 메소드가 실행될 떄
- execution(* com.sample..*Dao.*(..))
- com.sample 패키지에서 Dao로 끝나는 클래스의 모든 메소드가 실행될 때, com.sample 패키지의 모든 하위 패키지에서 Dao로 끝나는 클래스의 모든 메소드가 실행될 때
- 반환타입에 *은 반환타입이 any라는 의미이다.(어떤 반환타입이든 가능하다라는 의미)
- 패키지 경로의 ..은 현재 패키지 및 그 하위의 모든 패키지를 의미한다.
- 클래스 이름, 메소드 이름에 *을 붙이면 any라는 의미이다.
- 매개변수자리에 *은 매개변수 any라는 의미이다.(어떤 타입이든 가능하다라는 의미다.)
- 매개변수자리에 ..은 매개변수 상관없다라는 의미다.(매개변수가 없어도 되고, 매개변수가 여러 개여도 되고, 매개변수 타입도 상관없다.
- get*(*) 과 get*(..)의 차이
- get*(*)은 getBookByNo(int no), getBookByTitle(String title)등이 해당된다. 아무거나 한개는 와야한다.
- get*(..은 getAllBoos(), getBookByNo(int no), getBookByTitle(String title), getBooksByPrice(int min, int max)등이 해당된다.
// 매개변수가 하나도 없어도되고, 10개넘게 와도 상관없다.
- within(com.sample.service.*Service)")
- within(패키지를 포함한 클래스 명)
- within(com.sample.service.BookService)
- com.sample.service.BookService객체의 모든 메소드가 실행될 때
- within(com.sample.service.*Service)
- com.sample.service 패키지의 모든 Service객체의 모든 메소드가 실행될 떄
- within(com.sample..*ServiceImpl)
- com.sample의 모든 ServiceImpl와 com.sample의 모든 하위패키지의 ServiceImpl객체의 모든 메소드가 실행될 때
- 패키지 경로 ..은 현재 패키지 및 그 하위 모든 패키지를 의미한다.
- 클래스 이름에 *을 붙이면 any라는 의미로 *Service는 Service인 모든 클래스를 의미한다.
- execution(* com.sample.service.*Service.*(..))
- 대상 메소드 실행 전후에 실행할 공통기능을 구현하는 Advice 메소드
반응형
'중앙 HTA (2106기) story > spring java framwork story' 카테고리의 다른 글
vue.js와 springboot AJAX로 전송하기 (0) | 2022.01.14 |
---|---|
springboot사용, 설정하기 (0) | 2022.01.13 |
AOP(Aspect Oriented Programming) (0) | 2022.01.11 |
트랜잭션 @transactional (0) | 2022.01.11 |
inteceptor로 로그인 처리하기 (0) | 2022.01.11 |
Comments