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 |