AOP

정태규·2023년 2월 9일
0

여러 메서드에 공통 코드를 추가해야 한다면??

예를들어

class MyClass{
	void aaa(){
    	system.out.println("[before]{");
    	system.out.println("aaa() is called.");
        system.out.println("}[after]");
    }
    
    void aaa2(){
    	system.out.println("[before]{");
    	system.out.println("aaa2() is called.");
        system.out.println("}[after]");
    }
    
    void bbb(){
    	system.out.println("[before]{");
    	system.out.println("bbb() is called.");
        system.out.println("}[after]");
    }
}

이러한 코드가 있다고 하자.
모든 메서드에는
system.out.println("[before]{")
system.out.println("}[after]")
이 들어간다. 공통되는 메서드를 일일이 코딩하는 것은 비효율적인 코드라고 할 수 있다.

따라서 공통되는 코드만 앞으로 빼줄 수 있다면 굉장히 좋을 것이다.

다음 코드를 보자.

public class AopMain{
	public static void main(String[] args){
    	MyAdvice myAdvice = new MyAdvice();
        
        Class myClass = Class.forName("Package.AopMain");	//Package에는 자신의 package명 적어주면됨
        Object obj = myClass.newInstance();
        
        for(Method m : myClass.getDeclaredMethods()){
        	myAdvice.invoke(m,obj,null);
        }
    }
}


class MyAdvice{

    void invoke(Method m, Object obj, Object... args) throws Exception{
    	
        System.out.println("[Before]{");
        m.invoke(obj, args); // aaa(),aaa2(),bbb() 호출가능
		System.out.println("}[after]");
    
    }
}

class MyClass{
    
    void aaa(){
        System.out.println("aaa() is called.");
    }
    void aaa2(){
        System.out.println("aaa2() is called.");
    }
    void bbb(){
        System.out.println("bbb() is called.");

    }
}

위의 코드와 비교했을때 잘 만들어진 코드라는 것을 알 수가 있다.

AOP는 이처럼 공통되는 코드를 따로 빼주어서 메서드가 좀 더 특화될 수 있게 해준다.

Pattern

이번에는 a로 시작하는 메서드만 실행될 수 있게 만들어보자.

Pattern p = Pattern.compile("a.*")// a로 시작하는 것들을 패턴으로 만들어서 p에 저장,comile()안에 들어가는 것은 정규식(regex)이다

public class AopMain{
	public static void main(String[] args){
    	MyAdvice myAdvice = new MyAdvice();
        
        Class myClass = Class.forName("Package.AopMain");	//Package에는 자신의 package명 적어주면됨
        Object obj = myClass.newInstance();
        
        for(Method m : myClass.getDeclaredMethods()){
        	myAdvice.invoke(m,obj,null);
        }
    }
}

class MyAdvice{
    
    boolean matcehs(Method m){ // m 이 일치하면 true
    	return Pattern.matches("a.*","m.getName()"); // m이 일치하면 true
        }
        
    void invoke(Method m, Object obj, Object... args) throws Exception{
    	if(matches(m))
        	System.out.println("[Before]{");
        
        m.invoke(obj, args); // aaa(),aaa2(),bbb() 호출가능
		
        if(matches(m))
        	System.out.println("}[after]");
    
    }
}

class MyClass{
    
    void aaa(){
        System.out.println("aaa() is called.");
    }
    void aaa2(){
        System.out.println("aaa2() is called.");
    }
    void bbb(){
        System.out.println("bbb() is called.");

    }
}

@transactional

내가 원하는 메서드만 사용할 수 있도록 하기 위해서 Pattern을 사용했었다. 이와 비슷하게 사용되는 것이 @Transactional이다.

메서드 위에 annotation 표시를 해주면 getAnnotation()을 사용해서 annotation 표시가 있는 메서드만 사용할 수 있게 한다.

class MyAdvice{
            
    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(),bbb() 호출가능
		
        if(m.getAnnotation(Transactional.class)!=null)
        	System.out.println("}[after]");
    
    }
}

class MyClass{
    @Transactional
    void aaa(){
        System.out.println("aaa() is called.");
    }
    @Transactional
    void aaa2(){
        System.out.println("aaa2() is called.");
    }
    @Transactional
    void bbb(){
        System.out.println("bbb() is called.");

    }
}

AOP의 개념과 용어

AOP란?

Aspect Oriented Programming 관점 지향 프로그래밍 이라고 부른다.
부가기능을 동적으로 추가해주는 기술
메서드의 시작 또는 끝에 자동으로 코드를 추가

AOP 관련 용어

target : advice가 추가될 객체
advice: target에 동적으로 추가될 부가 기능(코드)
join point: advice가 추가(join)될 대상(메서드)
pointcut: join point들을 정의한 패턴
proxy : target에 advice가 동적으로 추가되어서 생성된 객체
weaving: target에 advice를 추가해서 proxy를 생성하는 것

Advice 종류

종류에너테이션설명
around advice@Around메서드의 시작과 끝 부분에 추가되는 부가 기능
before advice@Before메서드의 시작 부분에 추가되는 부가 기능
after advice@After메서드의 끝 부분에 추가되는 부가 기능
after returning@AfterRetruning예외가 발생하지 않았을 때,실행되는 부가 기능(try)
after throwing@AfterThrowing예외가 발생했을 때, 실행되는 부가 기능(catch)

Advice의 설정은 XML과 Annotation, 두가지 방법으로 가능하다.

pointcut expression

advice가 추가될 메서드를 지정하기 위한 패턴

execution(반환타입 패키지명.클래스명.메서드명(매개변수 목록))

예) @Around("execution(* com.aaa.bbb.aop.*.*(..))")
모든 반환타입(*), com.aaa.bbb.aop라는 패키지, 모든 클래스(*),모든 메서드(*)

@Component
public class MyMath {
    public int add(int a,int b){
        return a+b;
    }

    public int add(int a,int b,int c){
        return a+b+c;
    }

    public int subtract(int a,int b){
        return a-b;
    }

    public int multiply(int a,int b){
        return a*b;
    }

}


@Component
@Aspect
public class LoggingAdvice{
	@Around("execution(* com.aaa.bbb.aop.MyMath.*(..))")
    public Object methodCalling(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;
    }
}

@Component로 bean에 등록해주고 나서 횡단관심사(부가기능)라는 것을 알려주기 위해 @Aspect를 써준다.
@Around에서 MyMath.*을 등록해주어서 MyMath 클래스에 있는 모든 메서드를 실행할때마다 밑에 정의되어있는 부가기능이 같이 사용된다.

만약에 MyMath에 있는 특정한 메서드만 부가기능을 사용하고 싶다면

@Around("execution(* com.aaa.bbb.aop.MyMath.add*(..))")

이런식으로 해당 메서드이름을 써주면 된다.

0개의 댓글