Coding Planet
AOP(Aspect-Oriented Programming)๋? ์์ ํฌํจ ๋ณธ๋ฌธ
1. AOP(Aspect-Oriented Programming)๋?
AOP(Aspect-Oriented Programming)๋ ํ๋ก๊ทธ๋๋ฐ ํจ๋ฌ๋ค์ ์ค ํ๋๋ก, ๊ด์ฌ์ฌ์ ๋ถ๋ฆฌ(Concern Separation)๋ฅผ ์ํด ์ฌ์ฉ๋๋ ๊ธฐ์ ์ด๋ค. AOP๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง๊ณผ ๊ด๋ จ ์๋ ๋ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ๋ค์ ๋ชจ๋ํํ์ฌ ์ฝ๋์ ์ค๋ณต์ ์ค์ด๊ณ ์ ์ง๋ณด์์ฑ์ ํฅ์์ํค๋ ๋ฐ์ ์ฃผ๋ก ํ์ฉ๋๋ค. AOP์์๋ ๋ค์ํ ๊ด์ (Aspect)์ ์ ์ํ๊ณ , ์ด๋ฌํ ๊ด์ ๋ค์ ํต์ฌ ๋ก์ง์ ์ ์ฉํ๋ ๋ฐฉ์์ผ๋ก ๋์ํ๋ค.
Spring์์๋ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉํ๋ ํด๋์ค(Service, Dao ๋ฑ)์์ ์ค๋ณต๋๋ ๊ณตํต ์ฝ๋ ๋ถ๋ถ(commit, rollback, log์ฒ๋ฆฌ)์ ๋ณ๋์ ์์ญ์ผ๋ก ๋ถ๋ฆฌํด ๋ด๊ณ , ์ฝ๋๊ฐ ์ํ ๋๊ธฐ ์ ์ด๋ ์ด ํ์ ์์ ์ ํด๋น ์ฝ๋๋ฅผ ๋ถ์ฌ ๋ฃ์์ผ๋ก์จ ์์ค ์ฝ๋์ ์ค๋ณต์ ์ค์ด๊ณ , ํ์ํ ๋ ๋ง๋ง๋ค ๊ฐ์ ธ๋ค ์ธ ์ ์๊ฒ ๊ฐ์ฒดํ ํ๋ ๊ธฐ์ ์ ๋งํ๋ค.
2. Spring AOP์ ํต์ฌ ์ฉ์ด
* Aspect๋? : Advice + Pointcut = Aspect
์ค์ ๋ก ๋์ ์ฝ๋๋ฅผ ์๋ฏธํ๋ Advice์ ์์ฑํ Advice๊ฐ ์ค์ ๋ก ์ ์ฉ๋ ๋ฉ์๋์ธ Pointcut์ ํฉ์น ๊ฐ๋ ์ผ๋ก ๋ถ๊ฐ๊ธฐ๋ฅ(๋ก๊น , ๋ณด์, ํธ๋์ญ์ ๋ฑ)์ ๋ํ๋ด๋ ๊ณตํต ๊ด์ฌ์ฌ์ ๋ํ ์ถ์์ ์ธ ๋ช ์นญ. (์ฌ๋ฌ ๊ฐ์ฒด์ ๊ณตํต์ผ๋ก ์ ์ฉ๋๋ ๋ถ๊ฐ๊ธฐ๋ฅ์ ์์ฑํ ํด๋์ค ๋ํ๋) AOP ๊ฐ๋ ์ ์ ์ฉํ๋ฉด ํต์ฌ๊ธฐ๋ฅ ์ฝ๋ ์ฌ์ด์ ๋ผ์ด์๋ ๋ถ๊ฐ๊ธฐ๋ฅ์ ๋ ๋ฆฝ์ ์ธ ์์๋ก ๊ตฌ๋ถํด ๋ผ ์ ์๊ณ , ์ด๋ ๊ฒ ๊ตฌ๋ถ๋ ๋ถ๊ฐ๊ธฐ๋ฅ Aspect๋ ๋ฐํ์ ์์ ํ์ํ ์์น์ ๋์ ์ผ๋ก ์ฐธ์ฌํ๊ฒ ํ ์ ์๋ค. |
3.Spring AOP์ ๊ตฌ์กฐ
4. Spring AOP์ ๊ตฌํ ๋ฐฉ์
1) XML ๊ธฐ๋ฐ์ AOP ๋ค์์คํ์ด์ค๋ฅผ ํตํ AOP ๊ตฌํ
- ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ Advice ํด๋์ค๋ฅผ ์์ฑํ๋ค.
- XML ์ค์ ํ์ผ์ <aop:config>๋ฅผ ์ด์ฉํด์ Aspect๋ฅผ ์ค์ ํ๋ค.
2) @Aspect๋ฅผ ์ ์ํ๋ ํ๊ทธ
- @Aspect ์ด๋ ธํ ์ด์ ์ ์ด์ฉํด์ ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ Aspect ํด๋์ค๋ฅผ ์์ฑํ๋ค.
- ์ด ๋, Aspect ํด๋์ค๋ ์ด๋๋ฐ์ด์ค๋ฅผ ๊ตฌํํ๋ ๋ฉ์๋์ ํฌ์ธํธ์ปท์ ํฌํจํ๋ค.
- dispachter-servlet์ผ๋ก ์ง์ ๋ XML ์ค์ ํ์ผ์ <aop:aspect-autoproxy/>๋ฅผ ์ค์ ํ๋ค.
5. Spring AOP์ Advice์ annotation
6. ์ค์ ์ ์ฉ ์์
** ์ ์ฒด ๊ตฌ์กฐ
1) ํฌ์ธํธ์ปท ์ค์ ํ๊ธฐ
2) Before Aspect ์์
- ์๋ ์์๋ JoinPoint ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํด์ ์น ํ์ด์ง์ ์ ์ํ ์ฌ๋์ ip์ฃผ์๋ฅผ ์ถ๋ ฅํด์ฃผ๋ ๋ฉ์๋๋ฅผ Before๋ฐฉ์์ผ๋ก ๊ตฌํํ ๊ฒ์ด๋ค.
- JointPoint ์ธํฐํ์ด์ค๋ advice๊ฐ ์ ์ฉ๋๋ ๋์ Target Object์ ์ ๋ณด, ์ ๋ฌ๋๋ ๋งค๊ฐ๋ณ์, ๋ฉ์๋, ๋ฐํ๊ฐ, ์์ธ ๋ฑ์ ์ป์ ์ ์๋ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค.
- @Before("CommonPointcut.implPointcut()") : ๋ชจ๋ ServiceImple ํด๋์ค์ ์ ๊ทผํ๊ธฐ ์ ์ ์ฌ์ฉ์์ log๋ก ๋จ๊ธฐ๋ ๋ฉ์๋ ์คํ
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import edu.kh.comm.member.model.vo.Member;
@Component
@Aspect
public class BeforeAspect {
private Logger logger = LoggerFactory.getLogger(BeforeAspect.class);
@Before("CommonPointcut.implPointcut()")
public void serviceStart(JoinPoint jp) { //๋ฉ์๋ ์๊ทธ๋์ฒ
String str = "----------------------------------------------\n";
String className = jp.getTarget().getClass().getSimpleName();
String methodName = jp.getSignature().getName();
String param = Arrays.toString( jp.getArgs() );
str += "Start : " + className + " - " + methodName + "\n";
// Start : MemberServiceImpl - login
str += "Parameter : " + param + "\n";
try {
//HttpServletRequest๋ฅผ ์ด์ฉํด์ ip ์ป์ด์ค๊ธฐ
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+="[์ค์ผ์ค๋ฌ ๋์]";
}
logger.info(str);
}
//pc / ๋ธ๋ผ์ฐ์ / os๊ฐ ๊ฐ์ ๋ฌ๋ผ๋
//ip ์ฃผ์๋ฅผ ์ป์ด์ค๋ ๋ฉ์๋
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;
}
}
3)After Aspect ์์
- @After("CommonPointcut.implPointcut()") : ServeImple์ด ๋๋๋ค ์คํ๋๋ค.
import java.util.Arrays;
@Component
@Aspect
@Order(1)
public class AfterAspect {
private Logger logger = LoggerFactory.getLogger(AfterAspect.class);
@After("CommonPointcut.implPointcut()")
public void serviceEnd(JoinPoint jp) {
String className = jp.getTarget().getClass().getSimpleName();
String methodName = jp.getSignature().getName();
String str = "End : " + className + " - " + methodName + "\n";
// Start : MemberServiceImpl - login
str += "----------------------------------------------\n";
logger.info(str);
}
}
4) Around Aspect
- @Arount : ์ ์ฒ๋ฆฌ(@Before)์ ํ์ฒ๋ฆฌ(@After)๋ฅผ ํ ๋ฒ์ ์์ฑํ ์ ์๋ advice์ด๋ค.
- ์๋์ ๋ฉ์๋๋ ๋ฉ์๋ ์ฒ๋ฆฌ ์์ ์๊ฐ์ ์๋ ค์ฃผ๋ ๋ฉ์๋์ด๋ค.
- ProceedingJointPoint ์ธํฐํ์ด์ค : ์ฃผ๋ก Around ์ด๋๋ฐ์ด์ค์์ ํ์ฉ๋๋ฉฐ ํต์ฌ๋ก์ง์ ํธ์ถ์ ์ ์ดํ๊ณ ์ถ๊ฐ์ ์ธ ์ฒ๋ฆฌ๋ฅผ ์ํํ๋๋ฐ ์ฌ์ฉ๋๋ค.
- proceed() : Around ์ด๋๋ฐ์ด์ค์์ ํต์ฌ ๋ก์ง์ ํธ์ถ์ ์งํํ๋ ๋ฉ์๋. ์ด ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ๋ก์ง์ ์คํํ๊ณ Around ์ด๋๋ฐ์ด์ค ๋ด์์ ์ํ๋ ํ์ด๋ฐ์ ํธ์ถ์ ์ค๋จํ๊ฑฐ๋ ์ถ๊ฐ์ ์ธ ์ฒ๋ฆฌ๋ฅผ ์งํํ ์ ์๋ค. ๋ฐ๋ผ์ ํต์ฌ ๋ก์ง์ ์คํํ๋ proceed()์ด์ ์ ์ ์ฒ๋ฆฌ ๋ด์ฉ์ ์ฐ๊ณ ์ดํ์ ํ์ฒ๋ฆฌ ๋ด์ฉ์ ์ฐ๋ฉด ๋๋ค.
- ์๋์์๋ 1970/01/01 ์ค์ 9์(ํ๊ตญ OS ๊ธฐ์ค) ๋ถํฐ ์ง๊ธ๊น์ง ์ง๋ ์๊ฐ์ ms๋จ์๋ก ๋ํ๋ธ ๊ฐ์ ์ถ๋ ฅํ๋ System.currentTimeMillis() ์ ์ด์ฉํ์ฌ ์๋น์ค ์์์ (@Before)์ ์๋น์ค๊ฐ ๋๋ํ (@After)์ ์๊ฐ ์ฐจ์ด๋ฅผ ์ป์ด๋ด ํ๋ก๊ทธ๋จ ์คํ ์๊ฐ์ ์ป์ด๋ด๋ Aspect๋ฅผ ๊ตฌํํ๊ณ ์๋ค.
@Component
@Aspect
@Order(3)
public class AroundAspect {
private Logger logger = LoggerFactory.getLogger(AroundAspect.class);
// @Around : ์ ์ฒ๋ฆฌ(@Before)์ ํ์ฒ๋ฆฌ(@After)๋ฅผ ํ ๋ฒ์ ์์ฑ ๊ฐ๋ฅํ advice
@Around("CommonPointcut.implPointcut()")
public Object runningTime(ProceedingJoinPoint jp) throws Throwable {
//๋ฉ์๋ ์์ ์๊ฐ์ ์๋ ค์ฃผ๋ ๋ฉ์๋
//ProceedingJoinPoint ์ธํฐํ์ด์ : ์ /ํ ์ฒ๋ฆฌ ๊ธฐ๋ฅ, ๊ฐ์ ์ป์ด์ฌ ์ ์๋ ๋ฉ์๋ ์ ๊ณต
// proceed() ๋ฉ์๋ ํธ์ถ ์ : @Before advice ์์ฑ
// proceed() ๋ฉ์๋ ํธ์ถ ํ : @After advice ์์ฑ
// ๋ฉ์๋ ๋ง์ง๋ง์ proceed()์ ๋ฐํ๊ฐ์ ๋ฆฌํดํด์ผํจ.
// System.currentTimeMillis() :
// 1970/01/01 ์ค์ 9์(ํ๊ตญ OS ๊ธฐ์ค) ๋ถํฐ
// ์ง๊ธ๊น์ง ์ง๋ ์๊ฐ์ ms๋จ์๋ก ๋ํ๋ธ ๊ฐ
long startMs = System.currentTimeMillis();
Object obj = jp.proceed();// ์ ํ ์ฒ๋ฆฌ๋ฅผ ๋๋๋ ๊ธฐ์ค. ์ด๋ณด๋ค ์๊ฐ befor, ๋ค๊ฐ after
long endMs = System.currentTimeMillis();
logger.info("Running Time : " + (endMs-startMs) + "ms");
return obj;
}
}
5)AfterReturning Aspect ์์
- @After๋ ๋ฐํ๊ฐ์ ์ป์ด์ฌ ์ ์๋ค.
- @AfterReturning๋ ๊ธฐ์กด @After์ ๋ฐํ๊ฐ ์ป์ด์ค๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
- returning="returnObj" : ๋ฐํ ๊ฐ์ ์ ์ฅํ ๋งค๊ฐ๋ณ์๋ฅผ ์ง์
@Component
@Aspect
@Order(5) // ํฐ ์ซ์ ์์ผ๋ก ๋จผ์ ์คํ
public class AfterReturningAspect {
private Logger logger = LoggerFactory.getLogger(AfterReturningAspect.class);
@AfterReturning(pointcut = "CommonPointcut.implPointcut()", returning="returnObj")
public void serviceReutrnValue(Object returnObj ) {
logger.info("return Value: " + returnObj);
}
}
6)AfterThrowing Aspect ์์
- @AfterThrowing : ๊ธฐ์กด @After + Throwing๋ ์์ธ ์ป์ด์ค๊ธฐ
- throwing="exceptionObj" : ๋์ ์ง ์์ธ๋ฅผ ์ ์ฅํ ๋งค๊ฐ๋ณ์๋ฅผ ์ง์
@Component
@Aspect
public class AfterThrowingAspect {
private Logger logger = LoggerFactory.getLogger(AfterThrowingAspect.class);
@AfterThrowing(pointcut = "CommonPointcut.implPointcut()", throwing="exceptionObj")
public void serviceReutrnValue(Exception exceptionObj ) {
String str = "<<exception>> : " + exceptionObj.getStackTrace()[0];
str += " - " + exceptionObj.getMessage();
logger.error(str);
}
}
'๐ฑSPRING' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋ฉํฐํํธ(Multipart)๋? Multipart ์ ์ก๊ณผ MultipartResolver ๋ฅผ ํตํ ์์ฒญ ์ฒ๋ฆฌ (0) | 2023.05.18 |
---|---|
Handler Mapping๊ณผ Request Mapping์ด๋? (0) | 2023.05.18 |
Spring JDBC์ ์ ์์ ํน์ง (2) | 2023.05.18 |
Maven์ด๋? POM.XML์์ ์์กด์ฑ ๊ด๋ฆฌ, Maven ์ฌ์ฉํ์ฌ POM.XML์ DI ์ถ๊ฐํ๊ธฐ (0) | 2023.05.18 |
Spring root-context.xml ๋ฏ์ด๋ณด๊ธฐ (0) | 2023.05.03 |