1. @Aspect 어노테이션을 이용하여 Aspect 클래스에 직접 Advice 및 Point등을 직접 설정 2. 설정파일에 <aop:aspectj-autoproxy/> 를 추가 해야함 3. Aspect class를 <bean>으로 등록 4. 어노테이션(Annotation) - @Aspect : Aspect 클래스 선언 - @Before("pointcut") - @AfterReturning(pointcut="", returning="") - @AfterThrowing(pointcut="",throwing="") - @After("pointcut") - @Around("pointcut")
5. Around를 제외한 나머지 메소드들은 첫 argument로 JoinPoint를 가질 수 있다. 6. Around 메소드는 argument로 ProceedingJoinPoint를 가질 수 있다.
@Aspect public class AdviceClass { @Before("within(aop.core.CoreClass)")//pointcut 지정 public void beforLogger(){ System.out.println("AdviceClass.before.beforeLogger()---------------------"); } @AfterThrowing(pointcut="within(aop.core.CoreClass)",throwing="ex") public void afterThrowLogger(Throwable ex){ System.out.println("AdviceClass.afterThrowLogger()----------------------"); System.out.println(ex.getMessage()); } @AfterReturning(pointcut="within(aop.core.CoreClass)",returning="ret") public void afterReturnLogger(Object ret){ System.out.println("AdviceClass.afterReturnLogger()--------------"); System.out.println(ret); } @After("within(aop.core.CoreClass)")//pointcut만 적용할 경우 pointcut=을 생략하고 within 정의 public void afterLogger(){ System.out.println("AdviceClass.afterLogger()----------------------"); } @Around("within(aop.core.CoreClass)") public Object aroundLogger(ProceedingJoinPoint jp)throws Throwable{ System.out.println("AdviceClass.aroundLogger() 실행----------------"); return jp.proceed(); } }
public class CoreClass { public void businessMethod(int i){ if(i<0){ throw new RuntimeException("i가 0보다 작다"); } System.out.println("Business메소드가 실행되었습니다."); } }
* 결과
------------정상처리---------------- AdviceClass.before.beforeLogger()--------------------- AdviceClass.aroundLogger() 실행---------------- Business메소드가 실행되었습니다. AdviceClass.afterReturnLogger()-------------- null AdviceClass.afterLogger()---------------------- ------------에러내기---------------- AdviceClass.before.beforeLogger()--------------------- AdviceClass.aroundLogger() 실행---------------- AdviceClass.afterThrowLogger()---------------------- i가 0보다 작다 Exception in thread "main" AdviceClass.afterLogger()---------------------- java.lang.RuntimeException: i가 0보다 작다 at aop.core.CoreClass.businessMethod(CoreClass.java:6) at aop.core.CoreClass$$FastClassByCGLIB$$359776d6.invoke(<generated>) at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191) at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:692) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:55) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:50) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:42) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80) at aop.common.AdviceClass.aroundLogger(AdviceClass.java:34) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610) at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:50) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:625) at aop.core.CoreClass$$EnhancerByCGLIB$$cebdb4e6.businessMethod(<generated>) at TestMain.main(TestMain.java:14)
public class TestMain { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("aop/config/aop.xml"); MemberService service = (MemberService)ctx.getBean("memberService"); service.a(); String str = service.c(); System.out.println("main : "+str);
}
}
package aop.advice;
import org.aspectj.lang.ProceedingJoinPoint;
public class LoggerAdvice { public Object aroundLogger(ProceedingJoinPoint jp)throws Throwable{ System.out.println("---------before"); try{ Object retValue = jp.proceed();//클라이언트가 호출한 핵심 로칙 처리 메소드 호출 System.out.println("---------after-returning : 리턴값 : "+retValue);
// retValue = "asdfsdf";//String 값도 주소값으로 넘어다니기 때문에 retValue의 main에서의 값이 변한다. return retValue; }catch (Throwable ta) { System.out.println("--------after-throwing"); throw ta;//여기서 예외를 처리하는 것이 아니기 때문에 호출한 곳으로 예외를 던져준다. }finally{ System.out.println("--------after"); }
public class MemberService { public void a(){ System.out.println("MemberService.a() 실행"); } public void b(){ System.out.println("MemberService.b() 실행"); } public String c(){ System.out.println("MemberService.c() 실행"); return "abc"; } }
* 결과
---------before MemberService.a() 실행 ---------after-returning : 리턴값 : null --------after ---------before MemberService.c() 실행 ---------after-returning : 리턴값 : abc --------after main : abc
public class WareHouse { private static WareHouse instance=new WareHouse(); private int flowerCount=10; private int bookCount=10; private WareHouse(){} public static WareHouse getInstance(){ return instance; } public int getFlowerCount() { return flowerCount; } public int getBookCount() { return bookCount; }
}
package shop.service;
import shop.data.WareHouse;
public class BookServiceImpl implements StoreService { private WareHouse wareHouse;
public BookServiceImpl() { } public BookServiceImpl(WareHouse wareHouse){ this.wareHouse=wareHouse; } @Override public void sell(int count) throws InsufficientInventoryException{ if(wareHouse.getBookCount()<count){ throw new InsufficientInventoryException("Book 재고가 부족!",count); } System.out.println("책 "+count+"권 판매 ok!"); } }
package shop.service;
import shop.data.WareHouse;
public class FlowerServiceImpl implements StoreService { private WareHouse wareHouse; public FlowerServiceImpl(WareHouse wareHouse) { super(); this.wareHouse = wareHouse; } @Override public void sell(int count) throws InsufficientInventoryException { if(wareHouse.getFlowerCount()<count){ throw new InsufficientInventoryException("꽃 재고 부족",count); } System.out.println("꽃 "+count+" 송이 판매!"); } }
package shop.service;
//재고 부족시 발생시킬 Exception객체 public class InsufficientInventoryException extends Exception { private int orderCount; public InsufficientInventoryException() { }
public InsufficientInventoryException(String message, int orderCount) { super(message); this.orderCount = orderCount; } public int getOrderCount(){ return orderCount; }
}
package shop.service;
public interface StoreService {
public abstract void sell(int count) throws InsufficientInventoryException;
public class MemberDTO { private String id; private String name; private int age; public MemberDTO() { super(); } public MemberDTO(String id, String name, int age) { super(); this.id = id; this.name = name; this.age = age; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "MemberDTO [id=" + id + ", name=" + name + ", age=" + age + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; MemberDTO other = (MemberDTO) obj; if (age != other.age) return false; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; }
}
package aop.service;
import java.util.ArrayList;
import aop.dto.MemberDTO;
public interface MemberService {
public abstract MemberDTO selectMemberById(String id);
public abstract ArrayList selectAllMember();
public abstract void registerMember(MemberDTO mto);
public abstract void modifyMember(MemberDTO mto);
}
package aop.service;
import java.util.ArrayList;
import aop.dto.MemberDTO;
public class MemberServiceImpl implements MemberService {
@Override public MemberDTO selectMemberById(String id){ System.out.println("MemberServiceImpl.selectMemberById() 실행"); return new MemberDTO(id, "홍길동", 33); }
@Override public ArrayList selectAllMember(){ System.out.println("MemberServiceImpl.selectAllMember() 실행"); return new ArrayList(); }
@Override public void registerMember(MemberDTO mto){ System.out.println("MemberServiceImpl.registerMember(mto) 실행"); throw new RuntimeException("가입도중 오류 발생"); }
@Override public void modifyMember(MemberDTO mto){ System.out.println("MemberServiceImpl.modifyMember(mto) 실행"); throw new RuntimeException("회원 정보 수정 도충 오류발생"); } }
* 결과
MemberServiceImpl.selectMemberById() 실행 ------------LoggerAdvice.afterReturnLogger 시작-------------- ------------LoggerAdvice.afterReturnLogger 종료-------------- ------------LoggerAdvice.afterReturnLogger2 시작-------------- ret : MemberDTO [id=abcde, name=홍길동, age=33] ------------LoggerAdvice.afterReturnLogger2 종료-------------- main() : MemberDTO [id=abcde, name=홍길동, age=1000] MemberServiceImpl.selectAllMember() 실행 ------------LoggerAdvice.afterReturnLogger 시작-------------- ------------LoggerAdvice.afterReturnLogger 종료-------------- ------------LoggerAdvice.afterReturnLogger2 시작-------------- ret : [] ------------LoggerAdvice.afterReturnLogger2 종료-------------- main() : [] MemberServiceImpl.registerMember(mto) 실행 가입도중 오류 발생
public class TestAOP { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("aop/config/aop.xml"); Service service = (Service) ctx.getBean("customerService"); service.register(); service.modify(); String name = service.selectMemberNameById("111"); System.out.println(name); } }
<aop:aspect id="loggerJp" ref="logger"> <aop:before pointcut="execution(public * aop..*Service.select*(..))" method="logJp"/><!-- pointcut을 직접 지정할 수도 있다. --> </aop:aspect> </aop:config>
</beans>
package aop.core;
public class CustomerService implements Service{ public void register(){ System.out.println("고객 등록 처리"); } public void modify(){ System.out.println("고객 정보 수정 처리"); } public String selectMemberNameById(String id) { System.out.println(id+"의 이름을 조회합니다."); return "홍길동"; } }
package aop.core;
public interface Service {
public abstract void register();
public abstract void modify();
public String selectMemberNameById(String id); }
* 결과
------------------------------- 로그 처리 ------------------------------- 고객 등록 처리 ------------------------------- 로그 처리 ------------------------------- 고객 정보 수정 처리 타겟 객체 이름 : aop.core.CustomerService -----------인수값------------- 111 sig.getName() : selectMemberNameById sig.toShortString() : Service.selectMemberNameById(..) sig.toLongString() : public abstract java.lang.String aop.core.Service.selectMemberNameById(java.lang.String) ------------------------------- 로그 처리 ------------------------------- 111의 이름을 조회합니다. 홍길동
5. 패턴문자. - * : 1개의 모든 값을 표현 - argument에서 쓰는 경우 : 1개의 argument - package에 쓰인 경우 : 1개의 하위 package - 이름(메소드, 클래스)에 쓰일 경우 : 모든 글자들
- .. : 0개 이상 - argument에서 쓰인 경우 : 0개 이상의 argument - package에 쓰인 경우 : 0개 이상의 하위 package
6. execution - execution(수식어패턴? 리턴타입패턴 패키지패턴?.클래스명패턴.메소드패턴(argument패턴)) - 수식어패턴 : public, protected, 생략 - argument에 type을 명시할 경우 객체 타입은 fullyName으로 넣어야 한다. - java.lang은 생략가능 - 위 예 설명 적용 하려는 메소드들의 패턴은 public 제한자를 가지며 리턴 타입에는 모든 타입이 다 올 수 있다. 이름은 abc.def 패키지와 그 하위 패키지에 있는 모든 클래스 중 Service로 끝나는 클래스들 에서 set을 시작하는 메소드이며 argument는 0개 이상 오며 타입은 상관없다.