AspectJ Weaver 라이브러리 추가

- Spring이 AOP 처리된 객체 생성 시 사용하는 라이브러리

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>${org.aspectj-version}</version>
</dependency>

 

AOP설정

root-context.xml > NameSpaces에서 aop, context체크

<?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:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://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">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
	
	<context:annotation-config></context:annotation-config>
	<context:component-scan base-package="com.jjundol.aop.service"></context:component-scan>
	<context:component-scan base-package="com.jjundol.aop"></context:component-scan>	
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
	
</beans>

- <component-scan>을 이용하여 com.jjundol.aop.service(Target), com.jjundol.aop(Advice)를 스캔 => Spring의 Bean으로 등록됨

- <aop:aspectj-autoproxy>를 이용하여 LogAdvice에 지정한 Advice관련 Annotation들이 동작함(@Before, @AfterReturning, @AfterThorwing, @Around)

 

Target JoinPoint 

SampleService 인터페이스

package com.jjundol.aop.service;
public interface SampleService {
	public Integer doAdd(String str1, String str2) throws Exception;
}

SampleServiceImpl.java

package com.jjundol.aop.service;

import org.springframework.stereotype.Service;

import lombok.extern.log4j.Log4j;

@Service
@Log4j
public class SampleServiceImpl implements SampleService {
	@Override
	public Integer doAdd(String str1, String str2) throws Exception {
		log.info("Target JointPoint : SampleServiceImpl > doAdd");
		return Integer.parseInt(str1) + Integer.parseInt(str2);
	}
}

 

Advice 작성

- 로그를 기록하는 일은 비즈니스 로직과는 상관없는 일이지만, 필요는 한 관심사임 Aspect

- Annotaion설정

- @Aspect : 해당 클래스 객체는 Aspect를 구현한 것임

- @Component : Spring에서 Bean으로 인식하도록

- @Before : JointPoint실행 전에 실행

- @AfterThrowing : Target이 예외를 발생한 후에 동작

- @Around : 직접 Target메서드를 실행할 수 있는 권한을 가지고 있고, 메서드 실행 전/후 처리가 가능

- 내부적으로  PointCut을 지정

- AspectJ 표현식 = PointCut : execution은 접근 제한자와 특정 클래스의 특정 메서드를 지정할 수 있음, 맨 앞의 * 은 접근 제한자, 맨 뒤의 * 은 클래스 이름과 메서드, args(....)로 파라미터 활용

package com.jjundol.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import lombok.extern.log4j.Log4j;

@Aspect
@Log4j
@Component
public class LogAdvice {

	@Before( "execution(* com.jjundol.aop.service.SampleService*.*(..))" )
	public void logBefore() {
		log.info("Advice @Before :  logBefore ============");
	}
	
	@Before( "execution(* com.jjundol.aop.service.SampleService*.doAdd(String, String)) && args(str1, str2)" )
	public void logBeforeWithParam(String str1, String str2) {
		log.info("Advice @Before : logBeforeWithParam str1 : " + str1);
		log.info("Advice @Before : logBeforeWithParam str2 : " + str2);
	}
	
	@AfterThrowing( pointcut = "execution(* com.jjundol.aop.service.SampleService*.*(..))", throwing = "exception" )
	public void logException(Exception exception) {
		log.info("Advice @AfterThrowing :logException Exception!" + exception);	
	}
	
	@Around( "execution(* com.jjundol.aop.service.SampleService*.*(..))" )
	public Object logTime(ProceedingJoinPoint pjp) {
		
		long start = System.currentTimeMillis();
		
		log.info("@Around Target : " + pjp.getTarget());
		log.info("@Around Param : " + pjp.getArgs());
		
		// invoke method
		Object result = null;
		
		try {
			result = pjp.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		
		long end = System.currentTimeMillis();
		
		log.info("@Around TIME : " + (end - start));
		
		return result;		
	}

}

 

AOP 테스트

1) 

* 외부에서의 호출은 Proxy객체를 통해 Target객체의 JoinPoint메서드를 호출하는 방식임

- 변수 service는 com.jjundol.aop.service.SampleServiceImpl의 객체((Target객체))가 아니라 Proxy의 인스턴스가 됨

- com.sum.proxy.$Proxy24는 dynamic-Proxy기법이 적용된 결과임

@RunWith(SpringJUnit4ClassRunner.class)
@Log4j
@ContextConfiguration({"file:src/main/webapp/WEB-INF/spring/root-context.xml"})
public class SampleServiceTest {
	
	@Autowired
	private SampleService service;
	
	@Test
	public void testClass() {
		log.info(service);
		log.info(service.getClass().getName());
	}
	
}
INFO : com.jjundol.aop.service.SampleServiceTest - com.jjundol.aop.service.SampleServiceImpl@432038ec
INFO : com.jjundol.aop.service.SampleServiceTest - com.sun.proxy.$Proxy24

2)

@Test
public void testAdd() {
  try {
  	log.info(service.doAdd("123", "567"));
  } catch (Exception e) {
  	e.printStackTrace();
  }		
}
INFO : com.jjundol.aop.LogAdvice - Advice @Before :  logBefore ============
INFO : com.jjundol.aop.LogAdvice - Advice @Before : logBeforeWithParam str1 : 123
INFO : com.jjundol.aop.LogAdvice - Advice @Before : logBeforeWithParam str2 : 567
INFO : com.jjundol.aop.service.SampleServiceImpl - Target JointPoint : SampleServiceImpl > doAdd
INFO : com.jjundol.aop.service.SampleServiceTest - 690

2-1) @Around 사용

- @Around가 먼저 동작하여 Target메서드와 파라미터를 알아낼 수 있고, proceed()를 통해 Advice메서드를 실행시킬 권한도 있음

- @Around메서드는 반환타임이 void가 아닌 타입으로 설정하고, 실행 결과 역시 직접 반환하는 형태로 작성해야 함

INFO : com.jjundol.aop.LogAdvice - @Around Target : com.jjundol.aop.service.SampleServiceImpl@5a3bc7ed
INFO : com.jjundol.aop.LogAdvice - @Around Param : [Ljava.lang.Object;@181e731e
INFO : com.jjundol.aop.LogAdvice - Advice @Before :  logBefore ============
INFO : com.jjundol.aop.LogAdvice - Advice @Before : logBeforeWithParam str1 : 123
INFO : com.jjundol.aop.LogAdvice - Advice @Before : logBeforeWithParam str2 : 567
INFO : com.jjundol.aop.service.SampleServiceImpl - Target JointPoint : SampleServiceImpl > doAdd
INFO : com.jjundol.aop.LogAdvice - @Around TIME : 5
INFO : com.jjundol.aop.service.SampleServiceTest - 690

 

3) @AfterThrowing, Target의 에러 발생 이후 실행되는 Advice

@Test
public void testAddError() throws Exception {
	log.info(service.doAdd("123", "abc"));
}
INFO : com.jjundol.aop.LogAdvice - Advice @Before :  logBefore ============
INFO : com.jjundol.aop.LogAdvice - Advice @Before : logBeforeWithParam str1 : 123
INFO : com.jjundol.aop.LogAdvice - Advice @Before : logBeforeWithParam str2 : abc
INFO : com.jjundol.aop.service.SampleServiceImpl - Target JointPoint : SampleServiceImpl > doAdd
INFO : com.jjundol.aop.LogAdvice - Advice @AfterThrowing :logException Exception!java.lang.NumberFormatException: For input string: "abc"

 

'WEB > spring' 카테고리의 다른 글

[Spring] 스프링/객체 지향 프로그래밍/다형성/SOLID  (0) 2021.03.16
Maven 프로젝트 생성  (0) 2020.04.28
AOP  (0) 2020.03.21
REST 예시  (0) 2020.02.27
HikariCP 커넥션 풀  (0) 2019.12.30