YJ의 새벽
Spring ( AOP 관점 지향 프로그래밍 ) 본문
- Spring AOP ??
-- 관점 지향 프로그래밍 !!!
-- 중복되는 공통 코드부분을 별도의 영역으로 분리 !!!
-- 소스코드의 중복을 줄이고, 필요할때마다 가져다 쓸수있게 객체화 하는 기술 !!!
*** Aspect ?? : Advice + Pointcut == 기능이 수행.
-- 동작코드를 의미하는 Advice 와
작성한 Advice가 실제로 적용된 메소드인 Pointcut 을 합친 개념.
-- AOP 개념을 적용하면 핵심기능 코드 사이에 끼어있는 부가기능을 독립적인 요소로 구분해 낼 수 있고,
이렇게 구분된 부가기능 Aspect는 런타임 시에 필요한 위치에 동적으로 참여하게 할 수 있다.
-- pom.xml 설정정보 확인 .
--- servlet-context.xml 에 proxy 추가.
** proxy-target-class : 프록시가 적용될 클래스가 인터페이스면 false , 일반클래스면 true
-- Spring AOP 는 기본적으로 인터페이스를 상속받은 프록시가 생성되어 코드를 수행함.
-- 일반클래스일경우 cglib 라이브러리를 이용하여 일반 클래스를 상속받아 프록시를 생성한다.
*** Tomcat 서버 -> overview -> configuration -> Arguments 에서
-Djava.net.preferIPv4Stack=true 추가 !! ( ip 주소를 알아볼수있게 함. )
----------------------------------------------------------AOP 예제-----------------------------------------------------------------------------------
---- Pointcut 을 모아놓은 클래스를 따로 만든다. CommonPointcut.class
package edu.kh.comm.common.aop;
import org.aspectj.lang.annotation.Pointcut;
public class CommonPointcut {
// 회원 서비스용 Pointcut
@Pointcut("execution(* edu.kh.comm.member..*Impl.*(..))")
public void memberPointcut() {
}
// 모든 ServiceImpl 클래스용 Pointcut
@Pointcut("execution(* edu.kh.comm..*Impl.*(..))")
public void implPointcut() {
}
}
---- BeforeAspect .class ( Pointcut 에 지정된 메서드가 수행되기 전 advice(메서드) 수행 )
-- JoinPoint 인터페이스는 항상 첫번째 매개변수로 작성 ~!!
-- @Before( Pointcut 모아놓은클래스.메서드 ) 로 작성.
@Slf4j
@Component
@Aspect
public class BeforeAspect {
// JoinPoint 인터페이스 :
// advice가 적용되는 Target Object (ServiceImpl)의
// 정보, 전달되는 매개변수, 메서드, 반환값, 예외 등을 얻을 수 있는 메서드를 제공
// (주의사항) JoinPoint 인터페이스는 항상 첫 번째 매개변수로 작성 되어야 한다!
@Before("CommonPointcut.implPointcut()")
public void serviceStart(JoinPoint jp) {
String str = "----------------------------\n";
// jp.getTarget() : aop 가 적용된 객체 ( 각종 ServiceImpl )
String className = jp.getTarget().getClass().getSimpleName(); // 간단한 클래스명(패키지명제외)
// jp.getSignature() : 수행되는 메서드 정보
String methodName = jp.getSignature().getName();
// jp.getArgs() : 메서드 호출시 전달된 매개변수
String param = Arrays.toString( jp.getArgs() );
str += "Start : " + className + " - " + methodName + "\n";
// Start : MemberServiceImpl - login
str += "Parameter : " + param + "\n";
// ip 를 얻어오기 !!!!!!!!!!!!!
try {
// HttpServletRequest 얻어오기
// 단, 스프링 스케줄러 동작시 예외 발생 ( 스케줄러는 요청 객체가 존재하지않음 )
HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
Member loginMember = (Member) req.getSession().getAttribute("loginMember");
// ip : xxx.xxx.xxx.xxx ( email : test01@naver.com )
str += "ip : "+ getRemoteAddr(req);
if ( loginMember != null ) { //로그인상태인경우
str += " (email : "+ loginMember.getMemberEmail() + ")";
}
}catch( Exception e ) {
str += "[스케줄러 동작]";
}
log.info(str);
}
public static String getRemoteAddr(HttpServletRequest request) {
String ip = null;
ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-RealIP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("REMOTE_ADDR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
---- AfterAspect . class ( Pointcut 에 지정된 메서드가 수행된 후 advice(메서드) 수행 )
@Slf4j
@Component
@Aspect
@Order(1)
public class AfterAspect {
@After("CommonPointcut.implPointcut()")
public void serviceEnd(JoinPoint jp) {
String str = "----------------------------\n";
// jp.getTarget() : aop 가 적용된 객체 ( 각종 ServiceImpl )
String className = jp.getTarget().getClass().getSimpleName(); // 간단한 클래스명(패키지명제외)
// jp.getSignature() : 수행되는 메서드 정보
String methodName = jp.getSignature().getName();
str += "End : " + className + " - " + methodName + "\n";
// End : MemberServiceImpl - login
str += "---------------------------------\n";
log.info(str);
}
}
---- AfterReturningAspect . class ( 반환값을 가져오는 기능 )
@Slf4j
@Component
@Aspect
@Order(5) // 큰 숫자 순으로 먼저 실행
public class AfterReturningAspect {
// @AfterReturning : 기존 @After + 반환값 얻어오기 기능
// returning ="returnObj" : 반환 값을 저장할 매개변수를 지정
@AfterReturning(pointcut = "CommonPointcut.implPointcut()" , returning ="returnObj")
public void serviceReturnValue(Object returnObj) {
log.info("Return Value : "+returnObj);
}
}
---- AroundAspect . class ( 전처리 @Before , 후처리 @After 한번에 작성가능한 기능 )
@Slf4j
@Component
@Aspect
@Order(3)
public class AroundAspect {
// @Around : 전처리(@Before)와 후처리(@After)를 한 번에 작성 가능한 advice
@Around("CommonPointcut.implPointcut()")
public Object runningTime( ProceedingJoinPoint jp ) throws Throwable {
// ProceedingJoinPoint 인터페이스 : 전/후 처리 관련기능, 값을 얻어올 수 있는 메서드제공
// proceed() 메소드 호출 전 : @Before advice 작성
// proceed() 메소드 호출 후 : @After advice 작성
// 메소드 마지막에 proceed()의 반환값을 리턴해야함
long startMs = System.currentTimeMillis();
Object obj = jp.proceed(); // 전/후 처리 나누는 기준
long endMs = System.currentTimeMillis();
log.info("Running Time : " + ( endMs - startMs ) + "ms" );
return obj;
}
}
---- AfterThrowingAspect . class ( 기존 @After + 던져지는 예외 얻어오기 기능 )
@Slf4j
@Component
@Aspect
public class AfterThrowingAspect {
// @AfterThrowing : 기존 @After + 던져지는 예외 얻어오기 기능
// throwing="exceptionObj" : 던져진 예외를 저장할 매개변수를 지정
@AfterThrowing(pointcut = "CommonPointcut.implPointcut()", throwing="exceptionObj")
public void serviceReturnValue(Exception exceptionObj) {
String str = "<<exception>> : " + exceptionObj.getStackTrace()[0];
str += " - " + exceptionObj.getMessage();
log.error(str);
}
}
'Spring > Spring' 카테고리의 다른 글
Spring ( Web Socket ( 메시지 ) ) (0) | 2023.05.15 |
---|---|
Spring ( @Scheduled ) (0) | 2023.05.11 |
Spring 19 ( 댓글 , 대댓글 삽입,수정,삭제 ) (1) | 2023.05.10 |
Spring 18 ( 게시글 삭제 ) (0) | 2023.05.09 |
Spring 17 ( 게시글 작성/수정) (0) | 2023.05.08 |