Java EE 정리 47
03 May 2019
Reading time ~3 minutes
Java EE 정리 47 - Spring AOP(2)
Spring AOP 복습
- 핵심 로직에서 공통로직을 분리하기 위해 사용
- aspectjrt.jar, aspectjweaver.jar를 pom.xml에 정의
- 설정파일에 AOP사용 설정
<aop:aspectj-autoproxy/>
-
AOP로 처리하는 공통로직
-
세션 존재 확인
- 메소드명을 ses를 붙이는 식으로 컨벤션을 정해서 모든 메소드에 적용시킬 수 있음
- DB에서 커넥션 얻고 끊기 등
-
세션 존재 확인
- 어제 했던 예제를 변경해서 AOP를 사용해볼 것
public interface ItemService {
public String getName(); // 추가된 abstract methods
public String getAddr();
public int getAge();
}
@Component
public class ItemServiceImpl implements ItemService {
@Autowired
private ItemDAO i_dao;
@Override
public String getName() {
String name = "김정윤";
System.out.println("getName /// "+name);
return name;
}
@Override
public String getAddr() {
String addr="서울시 강남구 역삼동";
System.out.println("getAddr /// "+ addr);
return addr;
}
@Override
public int getAge() {
int age = 30;
System.out.println("getAge /// "+age);
return age;
}
}
@Aspect
@Component
public class ItemAop {
@Before("execution(* get*())")
public void getter() {
System.out.println("getter before advice");
}
}
public class AopRun {
public void execute() {
ApplicationContext ac =
new ClassPathXmlApplicationContext("kr/co/sist/run/application-context.xml");
ItemService is = ac.getBean(ItemService.class);
is.getName();
is.getAddr();
is.getAge();
}
public static void main(String[] args) {
AopRun ar = new AopRun();
ar.execute();
}
}
- 패키지명을 명시하면 한정시켜서 target을 줄 수 있다
@Before("execution(* kr.co.sist.service.get*())")
-
Pointcut으로 target method를 한정할 수 있다
- execution의 대상은 @Component가 붙어있는 Spring Bean
// 모든 Bean의 getter 호출
// @Before("execution(* get*())")
// ItemServiceImpl의 getter 호출
// @Before("execution(* kr.co.sist.service.ItemServiceImpl.get*())")
// ItemServiceImpl의 반환형이 String인 getter 호출
@Before("execution(String kr.co.sist.service.ItemServiceImpl.get*())")
public void getter() {
System.out.println("getter before advice");
}
JoinPoint interface
- JoinPoint는 @Before, @After, @AfterReturning, @AfterThrowing에서 사용되는 parameter를 Advice에서 처리하는 객체
- Advice의 매개변수로 정의
@Before(...)
public void test(JoinPoint jp) {
Object[] objArr = jp.getArgs(); // target의 매개변수를 받아올 수 있다
Signature s = jp.getSignature();
String methodName = s.getName(); // target method의 method명 얻기
int modifier = s.getModifiers(); // target method의 접근지정자 얻기
}
- JoinPoint 사용 예
public class AopRun {
public void execute() {
ApplicationContext ac =
new ClassPathXmlApplicationContext("kr/co/sist/run/application-context.xml");
ItemService is = ac.getBean(ItemService.class);
ItemDomain id = is.searchItem("이재찬");
System.out.println(id.getItemNo());
System.out.println(id.getItem());
}
public static void main(String[] args) {
AopRun ar = new AopRun();
ar.execute();
}
}
- org.aspectj.lang.Signiture 를 사용하면 method정보(매개변수, method명)을 얻을 수 있다
@Aspect
@Component
public class ItemAop {
@Before("execution(* searchItem(String))")
public void beforeItem(JoinPoint jp) {
System.out.println("before advice");
// target method의 매개변수 처리(JoinPoint를 사용하여)
Object[] params = jp.getArgs();
// target method의 method정보(매개변수, method명)
Signature s = jp.getSignature();
System.out.println("target method명 : "+s.getName());
System.out.println("target method 접근지정자 : "+s.getModifiers());
if (params != null) {
System.out.println("매개변수 갯수 : "+params.length);
for(Object param : params) {
System.out.println("매개변수 값 : "+param);
}
}
}
}
- 접근지정자는 숫자로 반환됨, java.lang.refelect.Modifier를 사용하면 문자열로 출력가능
System.out.println("target method 접근지정자 : "
+ (Modifier.isPublic(s.getModifiers()) ? "public" : "" ));
System.out.println("target method 접근지정자 : "
+ Modifier.toString(s.getModifiers()));
ProceedingJoinPoint interface
- JoinPoint가 @Around에서만 사용
- target method parameter와 return value 처리하는 객체
-
ProceedingJoinPoint사용 시 Advice 제작 주의점
-
advice method가 반드시 return을 가져야 함(void가 아니어야 함)
- Object도 괜찮고 아니면 target method의 반환형과 일치시켜도 됨
- 매개변수에 ProceedingJoinPoint 선언
- Advice method에 throws Throwable 정의
- return value를 처리해야 한다
-
advice method가 반드시 return을 가져야 함(void가 아니어야 함)
@Around( ... )
public 반환형 advice(ProceedingJoinPoint pjp) throws Throwable{
Object o = pjp.proceed(); // target method의 반환값을 가져옴
if (o instanceof String) {
// 반환값이 특정 클래스인지 확인할 때는 instanceof로 비교
}
return o;
}
- ProceedingJoinPoint 사용 예
public interface ItemService {
public ItemDomain searchItem(String name);
public ItemDomain searchData(String name);
...
@Component
public class ItemServiceImpl implements ItemService {
...
@Override
public ItemDomain searchData(String name) {
return new ItemDomain("IO_O001", name+" 애벌레, 크런커..");
}
}
@Aspect
@Component
public class ItemAop {
@Around("execution(* searchData(..))") // ".."은 모든 매개변수
public void around(ProceedingJoinPoint pjp) {
}
...
public class AopRun {
public void execute() {
ApplicationContext ac =
new ClassPathXmlApplicationContext("kr/co/sist/run/application-context.xml");
ItemService is = ac.getBean(ItemService.class);
ItemDomain id = is.searchData("정택성");
System.out.println(id.getItemNo());
System.out.println(id.getItem());
}
public static void main(String[] args) {
AopRun ar = new AopRun();
ar.execute();
}
}
@Around("execution(* searchData(..))") // ".."은 모든 매개변수
public ItemDomain around(ProceedingJoinPoint pjp) throws Throwable {
// pjp.getArgs(); // target의 파라미터 처리가능
// pjp.getSignature(); // target method 정보사용
// target의 리턴형을 받아와서 리턴을 만듦
ItemDomain id = (ItemDomain)pjp.proceed(); // target method가 유일할 때
if (id != null) {
id.setItem(id.getItem()+"s('.^)V");
}
return id;
}
/* @Around("execution(* searchData(..))") // ".."은 모든 매개변수
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// target의 리턴형을 받아와서 리턴을 만듦
Object o = pjp.proceed(); // target method가 여러개 일 때
return o;
}
*/