before & after코드 추가 -> 공통 코드 중복
만약 변경 사항이 생기면 똑같은 곳을 여러번 고쳐야한다.
조금 더 현명한 방법으로 해결하려고 한다
그것이 AOP
코드들을 따로 분리해냈다
하나의 메서드에 핵심기능/부가기능으로 두 개의 관심사가 있다.
서로 다른 관심사가 하나의 메서드에 있다면
관심사를 분리해줘야한다.
분리해야 하는 것 :
1. 관심사 / 2. 변하는 것/변하지 않는 것 / 3. 중복 코드
메서드 호출 부분 -> reflection API를 호출해서 메소드를 호출할 수 있도록 한다.
실습
package com.fastcampus.ch3.aop;
import java.lang.reflect.Method;
public class AopMain {
public static void main(String[] args) throws Exception{
MyAdvice myAdvice=new MyAdvice();
Class myClass = Class.forName("com.fastcampus.ch3.aop.MyClass");
Object obj = myClass.newInstance();
for(Method m : myClass.getDeclaredMethods()){ //myClass에 정의된 메소드를 배열로 받아와서 하나씩 얻어온다
myAdvice.invoke(m,obj,null); //myClass에 선언된 메소드가 하나씩 넘어가게 된다.
}
}
}
class MyAdvice{
void invoke(Method m, Object obj, Object... args) throws Exception{
System.out.println("[before]{");
m.invoke(obj, args); //aaa() aaa2() aaa3() 호출 가능
System.out.println("}[after]");
}
}
class MyClass{
void aaa(){
System.out.println("aaa is called.");
}
void aaa2(){
System.out.println("aaa2 is called.");
}
void aaa3(){
System.out.println("aaa3 is called.");
}
}
메소드 이름이 패턴에 맞을 경우에만 before after가 나오게 하는 코드
package com.fastcampus.ch3.aop;
import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class AopMain {
public static void main(String[] args) throws Exception{
MyAdvice myAdvice=new MyAdvice();
Class myClass = Class.forName("com.fastcampus.ch3.aop.MyClass");
Object obj = myClass.newInstance();
for(Method m : myClass.getDeclaredMethods()){ //myClass에 정의된 메소드를 배열로 받아와서 하나씩 얻어온다
myAdvice.invoke(m,obj,null); //myClass에 선언된 메소드가 하나씩 넘어가게 된다.
}
}
}
class MyAdvice{
Pattern p = Pattern.compile("a.*"); //정규식을 이용해, 메소드가 a로 시작하는 것만 골라서 아래 코드를 추가해본다
boolean matchs(Method m){
Matcher mather = p.matcher(m.getName());
return mather.matches();
}
void invoke(Method m, Object obj, Object... args) throws Exception{
if(matchs(m))//패턴에 맞는 경우만 before, after 출력하게 함
System.out.println("[before]{");
m.invoke(obj, args); //aaa() aaa2() aaa3() 호출 가능
if(matchs(m))
System.out.println("}[after]");
}
}
class MyClass{
void aaa(){
System.out.println("aaa is called.");
}
void aaa2(){
System.out.println("aaa2 is called.");
}
void bbb3(){
System.out.println("bbb3 is called.");
}
}
패턴 대신에 어노테이션을 붙여보자
package com.fastcampus.ch3.aop;
import org.checkerframework.checker.units.qual.A;
import org.springframework.transaction.annotation.Transactional;
import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class AopMain {
public static void main(String[] args) throws Exception{
MyAdvice myAdvice=new MyAdvice();
Class myClass = Class.forName("com.fastcampus.ch3.aop.MyClass");
Object obj = myClass.newInstance();
for(Method m : myClass.getDeclaredMethods()){ //myClass에 정의된 메소드를 배열로 받아와서 하나씩 얻어온다
myAdvice.invoke(m,obj,null); //myClass에 선언된 메소드가 하나씩 넘어가게 된다.
}
}
}
class MyAdvice{
Pattern p = Pattern.compile("a.*"); //정규식을 이용해, 메소드가 a로 시작하는 것만 골라서 아래 코드를 추가해본다
boolean matchs(Method m){
Matcher mather = p.matcher(m.getName());
return mather.matches();
}
void invoke(Method m, Object obj, Object... args) throws Exception{
if(m.getAnnotation(Transactional.class)!=null)
System.out.println("[before]{");
m.invoke(obj, args); //aaa() aaa2() aaa3() 호출 가능
if(m.getAnnotation(Transactional.class)!=null)
System.out.println("}[after]");
}
}
class MyClass{
@Transactional
void aaa(){
System.out.println("aaa is called.");
}
void aaa2(){
System.out.println("aaa2 is called.");
}
void bbb3(){
System.out.println("bbb3 is called.");
}
}
코드를 자동으로 추가하려면, 메소드마다 라인 수가 다르니
맨 앞과 맨 끝에만 추가할 수 있다.
앞에만 넣으면 비포 메소드
뒤에만 넣으면 애프터 메소드
앞 뒤 둘 다 넣으면 어라운드 메소드
부가 기능 ( <-> 핵심 기능) 을 동적으로 추가해주는 기술 (코드가 실행되는 과정에서 자동적으로 추가 되는 것)
횡단 관심사라는 말이 왜 나왔냐면
서로 다른 모듈에서 쓰이는 공통적인 부분인 로깅, 트랜잭션, 시큐리티 등을 횡단 관심사라고 한다.
공통으로 쓰이기 때문에 횡단 관심사라고 한다.
advice로 별도로 떼내어 동적으로 advice(코드)를 추가해주는 기술을
AOP라고 한다!
따로 target과 advice를 생성해놨다가, 실행 중에 합쳐져서 만들어진 새로운 객체가 Proxy라고 한다
합치는 것을 weaving이라고 한다.
pointcut은 패턴
oop나 aop는 변경에 유리한 코드를 만들기 위한 것이다. => 분리
코드를 쪼개서 (분리) 작성했는데,
MyClass에는 핵심기능 -> target
MyAdvice에는 부가기능 -> advice
으로 따로 분리가 되었다.
aop library가 target과 advice를 합쳐 원래 왼쪽 상자의 코드와 똑같게 만들어준다.
(이것이 join -> 데이터의 중복을 제거하는 목적으로 Table을 나누고 join으로 합친다.)
부가기능과 핵심기능을 수정할 때
각각 따로 수정가능 => 변경에 유리
핵심 기능 내의 3개의 메서드가 전부 join point이고, (advice가 조인될 대상)
이것을 패턴으로 정의한 것이 pointcut
After을 두 개로 쪼개놨는데,
try - catch 구문에서
try 끝에 들어가는 것이 after returning
catch에 들어가는 것이 after throwing
포인트컷이란 패턴
내가 부가기능을 추가할 메서드를 고르기 위한 패턴
메서드 호출하는 부분이 핵심 기능
실습
package com.fastcampus.ch3.aop;
import org.springframework.stereotype.Component;
@Component
public class MyMath {
public int add(int a,int b){
int result = a+b;
return result;
}
public int add(int a,int b, int c){
int result = a+b+c;
return result;
}
public int substract(int a,int b){
int result=a-b;
return result;
}
public int multiply(int a,int b){
int result = a*b;
return result;
}
}
root-context_aop.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com.fastcampus.ch3.aop"/>
</beans>
AopMain2.java
package com.fastcampus.ch3.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class AopMain2 {
public static void main(String[] args) {
ApplicationContext ac = new GenericXmlApplicationContext("file:src/main/webapp/WEB-INF/spring/**/root-context_aop.xml");
MyMath mm =(MyMath) ac.getBean("myMath");
System.out.println("mm.add(3,5) = " + mm.add(3,5));
System.out.println("mm.multiply(3,5) = " + mm.multiply(3, 5));
}
}
LoggingAdvice.java
package com.fastcampus.ch3.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
@Aspect
public class LoggingAdvice {
@Around("execution(* com.fastcampus.ch3.aop.MyMath.*(..))") //pointcut - 부가기능이 적용될 메서드의 패턴
public Object methodCallLog(ProceedingJoinPoint pjp) throws Throwable { //메소드의 모든 정보가 들어가있음
long start = System.currentTimeMillis();
System.out.println("<<[start] "+pjp.getSignature().getName()+ Arrays.toString(pjp.getArgs()));
Object result = pjp.proceed(); //target의 메소드를 호출
System.out.println("result = "+result);
System.out.println("[end]>> "+(System.currentTimeMillis()-start)+"ms");
return result;
}
}
'패캠 챌린지' 카테고리의 다른 글
MyBatis (0) | 2023.04.28 |
---|---|
서비스계층(Layer)의분리, Transactional (0) | 2023.04.05 |
Transaction, Commit, Rollback (0) | 2023.04.03 |
DAO의 작성과 적용 (0) | 2023.03.30 |
스프링으로 DB 다루기 (0) | 2023.03.29 |